Compare commits

..
Author SHA1 Message Date
Cursor AgentandDaniel Imms e83102070b Merge common/encoding and common/data into common/base
Move TextDecoder, Charsets, and EscapeSequences into the base layer.
Remove separate encoding and data composite projects; update imports
and explicit project references across common subprojects.

Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-30 18:12:34 +00:00
Cursor AgentandDaniel Imms ba582efbfa Inline compiler options in common layer tsconfigs; remove tsconfig.compiler.json
Each subfolder tsconfig.json now explicitly lists lib, types, paths, and
project references instead of extending a shared tsconfig.compiler.json.

Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-30 17:54:54 +00:00
Cursor AgentandDaniel Imms 6d826ac022 Fix duplicate Lifecycle import in InputHandler
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-30 17:45:36 +00:00
Cursor AgentandDaniel Imms 5070a08b58 Move Color to base; slim services/Types; colocate ICoreTerminal
- Restore Color.ts under common/base/ (not services)
- Add base/ColorTypes (IColor, IColorRGB)
- Add terminal/CoreTerminalTypes (ICoreTerminal, IScrollEvent, XtermListener)
- Add terminal/InputHandlerTypes and input/WindowOptions for handler types
- services/Types.ts: service-only (modes, mouse, osc link, cursor styles)
- Update imports across src, addons, browser; re-export KeyboardResultType removed
- browser tsconfig: exclude unit tests from composite build

Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-30 17:45:15 +00:00
Cursor AgentandDaniel Imms 8f5c963982 Fix composite build: re-export KeyboardResultType, exclude tests, remove stray emits
- Export KeyboardResultType as value from Types (const enum)
- Exclude browser unit tests from composite build
- Drop test/benchmark from tsconfig.all (uses path imports to sources)
- Update TaskQueue import paths in browser and addon-webgl
- Remove accidental .js/.d.ts emit files from src/

Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-30 17:33:45 +00:00
Cursor AgentandDaniel Imms eacf90b2b7 Split common into composite subprojects with project references
- common/tsconfig.json is a solution root referencing layer tsconfigs
- Each layer has composite tsconfig with correct references and outDir
- Break buffer/services cycle via buffer-local service interfaces
- Move cell types, keyboard/unicode types, TaskQueue, and TerminalOptions
- Exclude *.test.ts from composite builds; fix parser IDisposable imports

Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-30 17:32:03 +00:00
Cursor AgentandDaniel Imms 6ff26d2c7e Move CircularList to base and colocate ICircularList
Define ICircularList, IInsertEvent, and IDeleteEvent in common/base/CircularList
instead of common/services/Types. Removes the Types ↔ CircularList cycle and
keeps services from depending on Types for base-layer types.

Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-30 17:26:10 +00:00
Cursor AgentandDaniel Imms 3f39edb025 Restore source files after removing only build artifacts
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-30 16:13:32 +00:00
Cursor AgentandDaniel Imms 5f3882efe7 Remove stray emit artifacts from src/common and fix import path
Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-30 16:12:51 +00:00
Cursor AgentandDaniel Imms 1d125ac715 Restructure common into layered folders (issue #5963)
Reorganize src/common into platform (base, encoding, data, buffer,
parser, input), integration (services, terminal), and public API layers.

- Move core utilities to common/base/
- Move TextDecoder to common/encoding/
- Move Types, CircularList, Color, TaskQueue, SortedList to common/services/
- Move CoreTerminal, InputHandler, WindowsMode to common/terminal/
- Define ICharset in common/data/Charsets.ts (re-exported from Types)
- Update imports across src/, addons/, and test/
- Add per-layer tsconfig.json files and REARCHITECTURE.md
- Root common/tsconfig.json remains the composite build entry

Co-authored-by: Daniel Imms <Tyriar@users.noreply.github.com>
2026-05-30 16:12:18 +00:00
272 changed files with 2085 additions and 3600 deletions
+27 -19
View File
@@ -11,9 +11,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v3
- name: Use Node.js 22.x - name: Use Node.js 22.x
uses: actions/setup-node@v5 uses: actions/setup-node@v3
with: with:
node-version: 22.x node-version: 22.x
cache: 'npm' cache: 'npm'
@@ -69,7 +69,7 @@ jobs:
./addons/addon-webgl/out/* \ ./addons/addon-webgl/out/* \
./addons/addon-webgl/out-*st/* ./addons/addon-webgl/out-*st/*
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v4
with: with:
name: build-artifacts name: build-artifacts
path: compressed-build.zip path: compressed-build.zip
@@ -79,9 +79,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v3
- name: Use Node.js 22.x - name: Use Node.js 22.x
uses: actions/setup-node@v5 uses: actions/setup-node@v3
with: with:
node-version: 22.x node-version: 22.x
cache: 'npm' cache: 'npm'
@@ -100,16 +100,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v3
- name: Use Node.js 22.x - name: Use Node.js 22.x
uses: actions/setup-node@v5 uses: actions/setup-node@v3
with: with:
node-version: 22.x node-version: 22.x
cache: 'npm' cache: 'npm'
- name: Install dependencies - name: Install dependencies
run: | run: |
npm ci npm ci
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v4
with: with:
name: build-artifacts name: build-artifacts
- name: Unzip artifacts - name: Unzip artifacts
@@ -129,7 +129,6 @@ jobs:
exit $EXIT_CODE exit $EXIT_CODE
test-unit: test-unit:
needs: build
timeout-minutes: 20 timeout-minutes: 20
strategy: strategy:
matrix: matrix:
@@ -137,16 +136,21 @@ jobs:
runs-on: [ubuntu, macos, windows] runs-on: [ubuntu, macos, windows]
runs-on: ${{ matrix.runs-on }}-latest runs-on: ${{ matrix.runs-on }}-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}.x - name: Use Node.js ${{ matrix.node-version }}.x
uses: actions/setup-node@v5 uses: actions/setup-node@v3
with: with:
node-version: ${{ matrix.node-version }}.x node-version: ${{ matrix.node-version }}.x
cache: 'npm' cache: 'npm'
- name: Install dependencies - name: Install dependencies
run: | run: |
npm ci npm ci
- uses: actions/download-artifact@v7 - name: Wait for build job
uses: NathanFirmo/wait-for-other-job@v1.1.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
job: build
- uses: actions/download-artifact@v4
with: with:
name: build-artifacts name: build-artifacts
- name: Unzip artifacts - name: Unzip artifacts
@@ -162,7 +166,6 @@ jobs:
run: npm run test-unit --forbid-only run: npm run test-unit --forbid-only
test-integration: test-integration:
needs: build
timeout-minutes: 20 timeout-minutes: 20
strategy: strategy:
matrix: matrix:
@@ -171,9 +174,9 @@ jobs:
browser: [chromium, firefox, webkit] browser: [chromium, firefox, webkit]
runs-on: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}.x - name: Use Node.js ${{ matrix.node-version }}.x
uses: actions/setup-node@v5 uses: actions/setup-node@v3
with: with:
node-version: ${{ matrix.node-version }}.x node-version: ${{ matrix.node-version }}.x
cache: 'npm' cache: 'npm'
@@ -182,7 +185,12 @@ jobs:
npm ci npm ci
- name: Install playwright - name: Install playwright
run: npx playwright install --with-deps ${{ matrix.browser }} run: npx playwright install --with-deps ${{ matrix.browser }}
- uses: actions/download-artifact@v7 - name: Wait for build job
uses: NathanFirmo/wait-for-other-job@v1.1.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
job: build
- uses: actions/download-artifact@v4
with: with:
name: build-artifacts name: build-artifacts
- name: Unzip artifacts - name: Unzip artifacts
@@ -232,9 +240,9 @@ jobs:
matrix: matrix:
node-version: [22] node-version: [22]
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}.x - name: Use Node.js ${{ matrix.node-version }}.x
uses: actions/setup-node@v5 uses: actions/setup-node@v3
with: with:
node-version: ${{ matrix.node-version }}.x node-version: ${{ matrix.node-version }}.x
cache: 'npm' cache: 'npm'
@@ -243,7 +251,7 @@ jobs:
npm ci npm ci
- name: Install playwright - name: Install playwright
run: npx playwright install run: npx playwright install
- uses: actions/download-artifact@v7 - uses: actions/download-artifact@v4
with: with:
name: build-artifacts name: build-artifacts
- name: Unzip artifacts - name: Unzip artifacts
+4 -4
View File
@@ -24,12 +24,12 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v3
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v4 uses: github/codeql-action/init@v2
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v4 uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4 uses: github/codeql-action/analyze@v2
+2 -2
View File
@@ -18,9 +18,9 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Use Node.js 22.x - name: Use Node.js 22.x
uses: actions/setup-node@v5 uses: actions/setup-node@v3
with: with:
node-version: 22.x node-version: 22.x
cache: 'npm' cache: 'npm'
+2 -2
View File
@@ -10,9 +10,9 @@ jobs:
release: release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v3
- name: Use Node.js 22.x - name: Use Node.js 22.x
uses: actions/setup-node@v5 uses: actions/setup-node@v3
with: with:
node-version: 22.x node-version: 22.x
cache: 'npm' cache: 'npm'
-1
View File
@@ -17,7 +17,6 @@ docs/
npm-debug.log npm-debug.log
/.idea/ /.idea/
.env .env
mise.toml
build/ build/
.DS_Store .DS_Store
yarn.lock yarn.lock
-374
View File
@@ -1,374 +0,0 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": [
"import",
"typescript"
],
"categories": {
"correctness": "off"
},
"options": {
"typeAware": true,
"maxWarnings": 0
},
"env": {
"builtin": true
},
"ignorePatterns": [
"addons/*/src/third-party/*.ts",
"**/out/*",
"**/out-test/*",
"**/out-esbuild/*",
"**/out-esbuild-test/*",
"**/inwasm-sdks/*",
"**/typings/*.d.ts",
"**/node_modules",
"**/*.js",
"**/*.mjs"
],
"rules": {
"constructor-super": "error",
"for-direction": "error",
"getter-return": "error",
"no-async-promise-executor": "error",
"no-case-declarations": "error",
"no-class-assign": "error",
"no-compare-neg-zero": "error",
"no-cond-assign": "error",
"no-const-assign": "error",
"no-constant-binary-expression": "error",
"no-constant-condition": "error",
"no-control-regex": "error",
"no-debugger": "error",
"no-delete-var": "error",
"no-dupe-class-members": "error",
"no-dupe-else-if": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-empty": "error",
"no-empty-character-class": "error",
"no-empty-pattern": "error",
"no-empty-static-block": "error",
"no-ex-assign": "error",
"no-extra-boolean-cast": "error",
"no-fallthrough": "error",
"no-func-assign": "error",
"no-global-assign": "error",
"no-import-assign": "error",
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-loss-of-precision": "error",
"no-misleading-character-class": "error",
"no-new-native-nonconstructor": "error",
"no-nonoctal-decimal-escape": "error",
"no-obj-calls": "error",
"no-prototype-builtins": "error",
"no-redeclare": "error",
"no-regex-spaces": "error",
"no-self-assign": "error",
"no-setter-return": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-this-before-super": "error",
"no-unassigned-vars": "error",
"no-unexpected-multiline": "error",
"no-unreachable": "error",
"no-unsafe-finally": "error",
"no-unsafe-negation": "error",
"no-unsafe-optional-chaining": "error",
"no-unused-labels": "error",
"no-unused-private-class-members": "error",
"no-unused-vars": "error",
"no-useless-backreference": "error",
"no-useless-catch": "error",
"no-useless-escape": "error",
"no-with": "error",
"preserve-caught-error": "error",
"require-yield": "error",
"use-isnan": "error",
"valid-typeof": "error",
"no-array-constructor": "error",
"no-unused-expressions": "error",
"typescript/ban-ts-comment": "error",
"typescript/no-duplicate-enum-values": "error",
"typescript/no-empty-object-type": "error",
"typescript/no-explicit-any": "error",
"typescript/no-extra-non-null-assertion": "error",
"typescript/no-misused-new": "error",
"typescript/no-namespace": "error",
"typescript/no-non-null-asserted-optional-chain": "error",
"typescript/no-require-imports": "error",
"typescript/no-this-alias": "error",
"typescript/no-unnecessary-type-constraint": "error",
"typescript/no-unsafe-declaration-merging": "error",
"typescript/no-unsafe-function-type": "error",
"typescript/no-wrapper-object-types": "error",
"typescript/prefer-as-const": "error",
"typescript/prefer-namespace-keyword": "error",
"typescript/triple-slash-reference": "error",
"import/no-cycle": "error"
},
"overrides": [
{
"files": [
"**/*.ts",
"**/*.tsx",
"**/*.mts",
"**/*.cts"
],
"rules": {
"constructor-super": "off",
"getter-return": "off",
"no-class-assign": "off",
"no-const-assign": "off",
"no-dupe-class-members": "off",
"no-dupe-keys": "off",
"no-func-assign": "off",
"no-import-assign": "off",
"no-new-native-nonconstructor": "off",
"no-obj-calls": "off",
"no-redeclare": "off",
"no-setter-return": "off",
"no-this-before-super": "off",
"no-unreachable": "off",
"no-unsafe-negation": "off",
"no-var": "error",
"no-with": "off",
"prefer-const": "error",
"prefer-rest-params": "error",
"prefer-spread": "error"
}
},
{
"files": [
"**/*.ts"
],
"jsPlugins": [
"@stylistic/eslint-plugin",
{
"name": "jsdoc-js",
"specifier": "eslint-plugin-jsdoc"
},
"oxlint-plugin-eslint"
],
"rules": {
"@stylistic/comma-spacing": [
"warn",
{
"before": false,
"after": true
}
],
"@stylistic/indent": [
"warn",
2
],
"@stylistic/semi": [
"warn",
"always"
],
"@stylistic/quotes": [
"warn",
"single",
{
"allowTemplateLiterals": "always"
}
],
"@stylistic/member-delimiter-style": [
"warn",
{
"multiline": {
"delimiter": "semi",
"requireLast": true
},
"singleline": {
"delimiter": "comma",
"requireLast": false
}
}
],
"@stylistic/type-annotation-spacing": "warn",
"@stylistic/space-unary-ops": "warn",
"@stylistic/keyword-spacing": "warn",
"@stylistic/max-len": [
"warn",
{
"code": 1000,
"comments": 100,
"ignoreTrailingComments": true,
"ignoreUrls": true,
"ignorePattern": "^ *((?<vt_comment>(//|\\*) @vt)|(?<table_comment>\\* \\| )|(?<commented_code>// ))"
}
],
"@stylistic/new-parens": "warn",
"@stylistic/no-extra-semi": "error",
"@stylistic/no-trailing-spaces": "warn",
"eslint-js/object-curly-spacing": [
"warn",
"always"
],
"@stylistic/spaced-comment": [
"warn",
"always",
{
"markers": [
"/"
],
"exceptions": [
"-"
]
}
],
"jsdoc-js/check-alignment": "warn",
"jsdoc-js/check-param-names": "warn",
"jsdoc-js/multiline-blocks": "warn",
"jsdoc-js/no-multi-asterisks": "warn",
"eslint-js/one-var": [
"warn",
"never"
],
"eslint-js/no-restricted-syntax": [
"warn",
{
"selector": "CallExpression[callee.name='requestAnimationFrame']",
"message": "The global requestAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService."
},
{
"selector": "CallExpression[callee.name='cancelAnimationFrame']",
"message": "The global cancelAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService."
},
{
"selector": "CallExpression > MemberExpression[object.name='window'][property.name='requestAnimationFrame']",
"message": "window.requestAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService."
},
{
"selector": "CallExpression > MemberExpression[object.name='window'][property.name='cancelAnimationFrame']",
"message": "window.cancelAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService."
},
{
"selector": "MemberExpression[object.name='window'][property.name='devicePixelRatio']",
"message": "window.devicePixelRatio should be avoided, get it from ICoreBrowserService."
}
],
"curly": [
"warn",
"multi-line"
],
"eqeqeq": [
"warn",
"always"
],
"no-duplicate-imports": "warn",
"no-else-return": [
"warn",
{
"allowElseIf": false
}
],
"no-eval": "warn",
"no-irregular-whitespace": "warn",
"no-restricted-imports": [
"warn",
{
"patterns": [
".*\\/out\\/.*"
]
}
],
"no-unsafe-finally": "warn",
"no-unused-vars": "off",
"no-var": "warn",
"no-empty": "off",
"no-empty-pattern": "off",
"no-cond-assign": "off",
"no-case-declarations": "off",
"for-direction": "off",
"no-prototype-builtins": "off",
"no-useless-escape": "off",
"no-self-assign": "off",
"no-async-promise-executor": "off",
"prefer-rest-params": "off",
"no-control-regex": "off",
"no-fallthrough": "off",
"prefer-spread": "off",
"prefer-const": "warn",
"no-unused-expressions": "off",
"typescript/array-type": [
"warn",
{
"default": "array",
"readonly": "generic"
}
],
"typescript/consistent-type-assertions": "warn",
"typescript/consistent-type-definitions": "warn",
"typescript/explicit-function-return-type": [
"warn",
{
"allowExpressions": true
}
],
"typescript/explicit-member-accessibility": [
"warn",
{
"accessibility": "explicit",
"overrides": {
"constructors": "off"
}
}
],
"typescript/no-confusing-void-expression": [
"warn",
{
"ignoreArrowShorthand": true
}
],
"typescript/no-useless-constructor": "warn",
"typescript/prefer-nullish-coalescing": [
"warn",
{
"ignorePrimitives": true
}
],
"typescript/prefer-namespace-keyword": "warn",
"typescript/no-unused-vars": [
"warn",
{
"vars": "all",
"args": "none"
}
],
"typescript/no-require-imports": "off",
"typescript/no-explicit-any": "off",
"typescript/no-wrapper-object-types": "off",
"typescript/no-empty-object-type": "off",
"typescript/no-unsafe-function-type": "off",
"typescript/no-this-alias": "off",
"typescript/no-namespace": "off",
"typescript/no-duplicate-enum-values": "off"
}
},
{
"files": [
"**/*.api.ts",
"**/*.test.ts"
],
"rules": {
"eslint-js/object-curly-spacing": "off",
"@stylistic/max-len": "off",
"typescript/no-unused-vars": "off",
"typescript/explicit-function-return-type": "off",
"typescript/explicit-member-accessibility": "off"
}
},
{
"files": [
"demo/**/*.ts",
"**/*.benchmark.ts"
],
"rules": {
"typescript/prefer-nullish-coalescing": "off"
}
}
]
}
-120
View File
@@ -1,120 +0,0 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"options": {
"maxWarnings": 0
},
"env": {
"builtin": true
},
"overrides": [
{
"files": [
"typings/**/*.d.ts"
],
"plugins": [
"typescript"
],
"jsPlugins": [
"@stylistic/eslint-plugin",
{
"name": "jsdoc-js",
"specifier": "eslint-plugin-jsdoc"
},
"oxlint-plugin-eslint"
],
"rules": {
"@stylistic/indent": [
"warn",
2
],
"@stylistic/semi": [
"warn",
"always"
],
"@stylistic/quotes": [
"warn",
"single",
{
"allowTemplateLiterals": "always"
}
],
"@stylistic/member-delimiter-style": [
"warn",
{
"multiline": {
"delimiter": "semi",
"requireLast": true
},
"singleline": {
"delimiter": "comma",
"requireLast": false
}
}
],
"@stylistic/type-annotation-spacing": "warn",
"@stylistic/keyword-spacing": "warn",
"@stylistic/max-len": [
"warn",
{
"code": 1000,
"comments": 80,
"ignoreUrls": true,
"ignorePattern": "^ *(?<ps_description>\\* Ps=)"
}
],
"@stylistic/no-extra-semi": "error",
"@stylistic/no-trailing-spaces": "warn",
"eslint-js/object-curly-spacing": [
"warn",
"always"
],
"@stylistic/spaced-comment": [
"warn",
"always",
{
"markers": [
"/"
],
"exceptions": [
"-"
]
}
],
"@stylistic/comma-dangle": [
"warn",
{
"objects": "never",
"arrays": "never",
"functions": "never"
}
],
"@stylistic/eol-last": "warn",
"curly": [
"warn",
"multi-line"
],
"eqeqeq": [
"warn",
"always"
],
"jsdoc-js/check-alignment": "warn",
"jsdoc-js/check-param-names": "warn",
"no-irregular-whitespace": "warn",
"typescript/array-type": [
"warn",
{
"default": "array",
"readonly": "generic"
}
],
"typescript/explicit-function-return-type": [
"warn",
{
"allowExpressions": true
}
],
"typescript/prefer-namespace-keyword": "warn"
}
}
]
}
+3
View File
@@ -15,6 +15,9 @@
"--recursive", "--recursive",
"${workspaceRoot}/out/**/*.test.js" "${workspaceRoot}/out/**/*.test.js"
], ],
"env": {
"NODE_PATH": "${workspaceRoot}/out"
},
"sourceMaps": true, "sourceMaps": true,
"outFiles": [ "${workspaceRoot}/out/**/*.js" ], "outFiles": [ "${workspaceRoot}/out/**/*.js" ],
"internalConsoleOptions": "openOnSessionStart" "internalConsoleOptions": "openOnSessionStart"
-1
View File
@@ -26,7 +26,6 @@ npm run build && npm run esbuild # Build all TypeScript and bundle
- Integration tests: `npm run test-integration` (Playwright across Chrome/Firefox/WebKit) - Integration tests: `npm run test-integration` (Playwright across Chrome/Firefox/WebKit)
- Integration tests by file: `npm run test-integration -- test/playwright/InputHandler.test.ts`. Never use grep to filter tests, it doesn't work - Integration tests by file: `npm run test-integration -- test/playwright/InputHandler.test.ts`. Never use grep to filter tests, it doesn't work
- Integration tests by addon: `npm run test-integration -- --suite=addon-search`. Suites always follow the format `addon-<something>` - Integration tests by addon: `npm run test-integration -- --suite=addon-search`. Suites always follow the format `addon-<something>`
- Lint: `npm run lint` (oxlint with type-aware rules, then ESLint for `naming-convention` only), `npm run lint-api` for `typings/`, `npm run lint-fix` for oxlint auto-fix
- Lint changes: `npm run lint-changes` to lint only changed files, `npm run lint-changes-fix` to fix them - Lint changes: `npm run lint-changes` to lint only changed files, `npm run lint-changes-fix` to fix them
## Addon Development Pattern ## Addon Development Pattern
+1 -1
View File
@@ -39,7 +39,7 @@ The preferred way to report bugs or request features is to use
It's pretty common for maintainers of large open source projects to suffer from burnout, especially when needing to triage a large number of incoming issues instead of actually building things. Here are some of the steps we take to try mitigate this: It's pretty common for maintainers of large open source projects to suffer from burnout, especially when needing to triage a large number of incoming issues instead of actually building things. Here are some of the steps we take to try mitigate this:
- Support questions live in [GH discussions](https://github.com/xtermjs/xterm.js/discussions), issues may be transferred there without further comment and core maintainers may or may not participate in discussions. - Support questions live in [GH discussions](https://github.com/xtermjs/xterm.js/discussions), issues may be transfered there without further comment and core maintainers may or may not participate in discussions.
- Issues are strictly for well defined features or bugs that are _actionable_. - Issues are strictly for well defined features or bugs that are _actionable_.
- Sometimes features are out of scope. A common example of this is a niche feature that the pricipal implementation ([VS Code](https://code.visualstudio.com/)) won't leverage and therefore would be difficult to maintain and likely suffer from bitrot. The reporter may not agree with this, but you could always create an addon if that works or maintain your own fork if it comes to that. - Sometimes features are out of scope. A common example of this is a niche feature that the pricipal implementation ([VS Code](https://code.visualstudio.com/)) won't leverage and therefore would be difficult to maintain and likely suffer from bitrot. The reporter may not agree with this, but you could always create an addon if that works or maintain your own fork if it comes to that.
- If a feature does not have a clear way forward or needs more discussion it may be closed ro moved to a discussion. - If a feature does not have a clear way forward or needs more discussion it may be closed ro moved to a discussion.
+1 -1
View File
@@ -15,7 +15,7 @@ test.beforeAll(async ({ browser }) => {
}); });
test.afterAll(async () => await ctx.page.close()); test.afterAll(async () => await ctx.page.close());
test.describe('AttachAddon', () => { test.describe('Search Tests', () => {
test.beforeEach(async () => { test.beforeEach(async () => {
await ctx.proxy.reset(); await ctx.proxy.reset();
+2 -32
View File
@@ -78,43 +78,13 @@ export class BrowserClipboardProvider implements IClipboardProvider {
} }
} }
/**
* TODO: Once the base64 handling on Uint8Arrays is more common,
* remove the btoa/atob fallbacks below.
*/
interface IUint8ArrayB64 extends Uint8Array {
toBase64(): string;
}
interface IUint8ArrayB64Ctor extends Uint8ArrayConstructor {
fromBase64(s: string): IUint8ArrayB64;
}
export class Base64 implements IBase64 { export class Base64 implements IBase64 {
public encodeText(data: string): string { public encodeText(data: string): string {
const bytes = new TextEncoder().encode(data) as IUint8ArrayB64; return btoa(data);
if (bytes.toBase64 !== undefined) {
return bytes.toBase64();
}
let bin = '';
for (let i = 0; i < bytes.length; i++) {
bin += String.fromCharCode(bytes[i]);
}
return btoa(bin);
} }
public decodeText(data: string): string { public decodeText(data: string): string {
if ((Uint8Array as IUint8ArrayB64Ctor).fromBase64 !== undefined) {
try { try {
return new TextDecoder().decode((Uint8Array as IUint8ArrayB64Ctor).fromBase64(data)); return atob(data);
} catch {}
return '';
}
try {
const bin = atob(data);
const bytes = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; ++i) {
bytes[i] = bin.charCodeAt(i);
}
return new TextDecoder().decode(bytes);
} catch {} } catch {}
return ''; return '';
} }
@@ -29,7 +29,7 @@ test.describe('ClipboardAddon', () => {
await ctx.page.context().grantPermissions(['clipboard-read', 'clipboard-write']); await ctx.page.context().grantPermissions(['clipboard-read', 'clipboard-write']);
} }
await ctx.page.evaluate(` await ctx.page.evaluate(`
window.term.reset(); window.term.reset()
window.clipboard?.dispose(); window.clipboard?.dispose();
window.clipboard = new ClipboardAddon(); window.clipboard = new ClipboardAddon();
window.term.loadAddon(window.clipboard); window.term.loadAddon(window.clipboard);
@@ -92,22 +92,4 @@ test.describe('ClipboardAddon', () => {
deepEqual(await ctx.page.evaluate(() => window.navigator.clipboard.readText()), ''); deepEqual(await ctx.page.evaluate(() => window.navigator.clipboard.readText()), '');
}); });
}); });
test.describe('non-ASCII', () => {
const testDataEncoded = '4oKsbWzDpMO8dMOf';
const testDataDecoded = '€mläütß';
test('write simple string', async () => {
await ctx.proxy.write(`\x1b]52;c;${testDataEncoded}\x07`);
deepEqual(await ctx.page.evaluate(() => window.navigator.clipboard.readText()), testDataDecoded);
});
test('read simple string', async () => {
await ctx.page.evaluate(`
window.data2 = [];
window.term.onData(e => data2.push(e));
`);
await ctx.page.evaluate((d) => window.navigator.clipboard.writeText(d), testDataDecoded);
await ctx.proxy.write(`\x1b]52;c;?\x07`);
deepEqual(await ctx.page.evaluate('window.data2'), [`\x1b]52;c;${testDataEncoded}\x07`]);
});
});
}); });
+1 -1
View File
@@ -82,7 +82,7 @@ declare module '@xterm/addon-clipboard' {
/** /**
* The clipboard provider interface that enables xterm.js to access the browser clipboard. * The clipboard provider interface that enables xterm.js to access the browser clipboard.
* *
* Note that this clipboard provider does not implement own cut buffers as xterm does, * Note this this clipboard provider does not implement own cut buffers as xterm does,
* but redirect the selection parameter always to navigator.clipboard. * but redirect the selection parameter always to navigator.clipboard.
*/ */
export class BrowserClipboardProvider implements IClipboardProvider{ export class BrowserClipboardProvider implements IClipboardProvider{
+6 -6
View File
@@ -74,14 +74,14 @@ export class FitAddon implements ITerminalAddon, IFitApi {
: (this._terminal.options.scrollbar?.width ?? ViewportConstants.DEFAULT_SCROLL_BAR_WIDTH)); : (this._terminal.options.scrollbar?.width ?? ViewportConstants.DEFAULT_SCROLL_BAR_WIDTH));
const parentElementStyle = _getComputedStyle(this._terminal.element.parentElement); const parentElementStyle = _getComputedStyle(this._terminal.element.parentElement);
const parentElementHeight = Math.max(0, parseInt(parentElementStyle.getPropertyValue('height'), 10) || 0); const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));
const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width'), 10) || 0); const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));
const elementStyle = _getComputedStyle(this._terminal.element); const elementStyle = _getComputedStyle(this._terminal.element);
const elementPadding = { const elementPadding = {
top: parseInt(elementStyle.getPropertyValue('padding-top'), 10) || 0, top: parseInt(elementStyle.getPropertyValue('padding-top')),
bottom: parseInt(elementStyle.getPropertyValue('padding-bottom'), 10) || 0, bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')),
right: parseInt(elementStyle.getPropertyValue('padding-right'), 10) || 0, right: parseInt(elementStyle.getPropertyValue('padding-right')),
left: parseInt(elementStyle.getPropertyValue('padding-left'), 10) || 0 left: parseInt(elementStyle.getPropertyValue('padding-left'))
}; };
const elementPaddingVer = elementPadding.top + elementPadding.bottom; const elementPaddingVer = elementPadding.top + elementPadding.bottom;
const elementPaddingHor = elementPadding.right + elementPadding.left; const elementPaddingHor = elementPadding.right + elementPadding.left;
+9 -9
View File
@@ -90,7 +90,7 @@ terminal.loadAddon(imageAddon);
therefore palette animations cannot be used. therefore palette animations cannot be used.
- **SIXEL Raster Attributes Handling** - **SIXEL Raster Attributes Handling**
If raster attributes were found in the SIXEL data (level 2), the image will always be truncated to the given height/width extent. We deviate here from the specification on purpose, as it allows several processing optimizations. For level 1 SIXEL data without any raster attributes the image can freely grow in width and height up to the last data byte, which has a much higher processing penalty. In general encoding libraries should not create level 1 data anymore and should not produce pixel information beyond the announced height/width extent. Both is discouraged by the >30 years old specification. If raster attributes were found in the SIXEL data (level 2), the image will always be truncated to the given height/width extend. We deviate here from the specification on purpose, as it allows several processing optimizations. For level 1 SIXEL data without any raster attributes the image can freely grow in width and height up to the last data byte, which has a much higher processing penalty. In general encoding libraries should not create level 1 data anymore and should not produce pixel information beyond the announced height/width extend. Both is discouraged by the >30 years old specification.
Currently the SIXEL implementation of the addon does not take custom pixel sizes into account, a SIXEL pixel will map 1:1 to a screen pixel. Currently the SIXEL implementation of the addon does not take custom pixel sizes into account, a SIXEL pixel will map 1:1 to a screen pixel.
@@ -149,9 +149,9 @@ The addon provides the following API endpoints to retrieve raw image data as can
### Memory Usage ### Memory Usage
The addon does most image processing in JavaScript and therefore can occupy a rather big amount of memory. To get an idea where the memory gets eaten, let's look at the data flow and processing steps: The addon does most image processing in Javascript and therefore can occupy a rather big amount of memory. To get an idea where the memory gets eaten, lets look at the data flow and processing steps:
- Incoming image data chunk at `term.write` (terminal) - Incomming image data chunk at `term.write` (terminal)
`term.write` might stack up incoming chunks. To circumvent this, you will need proper flow control (see xterm.js docs). Note that with image output it is more likely to run into this issue, as it can create lots of MBs in very short time. `term.write` might stock up incoming chunks. To circumvent this, you will need proper flow control (see xterm.js docs). Note that with image output it is more likely to run into this issue, as it can create lots of MBs in very short time.
- Sequence Parser (terminal) - Sequence Parser (terminal)
The parser operates on a buffer containing up to 2^17 codepoints (~0.5 MB). The parser operates on a buffer containing up to 2^17 codepoints (~0.5 MB).
- Sequence Handler - Chunk Decoding (addon) - Sequence Handler - Chunk Decoding (addon)
@@ -177,18 +177,18 @@ const totalActive = storageBytes + decodingBytes;
``` ```
Note that browsers have offloading tricks for rarely touched memory segments, esp. `storageBytes` might not directly translate into real memory usage. Usage peaks will happen during active decoding of multiple big images due to the need of 2 full pixel buffers at the same time, which cannot be offloaded. Thus you may want to keep an eye on `pixelLimit` under limited memory conditions. Note that browsers have offloading tricks for rarely touched memory segments, esp. `storageBytes` might not directly translate into real memory usage. Usage peaks will happen during active decoding of multiple big images due to the need of 2 full pixel buffers at the same time, which cannot be offloaded. Thus you may want to keep an eye on `pixelLimit` under limited memory conditions.
Further note that the formulas above do not respect the JavaScript object's overhead. Compared to the raw buffer needs the bookkeeping by these objects is rather small (<<5%). Further note that the formulas above do not respect the Javascript object's overhead. Compared to the raw buffer needs the book keeping by these objects is rather small (<<5%).
_Why should I care about memory usage at all?_ _Why should I care about memory usage at all?_
Well you don't have to, and it probably will just work fine with the addon defaults. But for bigger integrations, where much more data is held in the JavaScript context (like multiple terminals on one page), it is likely to hit the engine's memory limit sooner or later under decoding and/or storage pressure. Well you don't have to, and it probably will just work fine with the addon defaults. But for bigger integrations, where much more data is held in the Javascript context (like multiple terminals on one page), it is likely to hit the engine's memory limit sooner or later under decoding and/or storage pressure.
_How can I adjust the memory usage?_ _How can I adjust the memory usage?_
- `pixelLimit` - `pixelLimit`
A constructor setting, thus you would have to anticipate, whether (multiple) terminals in your page gonna do lots of concurrent decoding. Since this is normally not the case and the memory usage only peaks temporarily, a rather high value should work even with multiple terminals in one page. A constructor setting, thus you would have to anticipate, whether (multiple) terminals in your page gonna do lots of concurrent decoding. Since this is normally not the case and the memory usage is only temporarily peaking, a rather high value should work even with multiple terminals in one page.
- `storageLimit` - `storageLimit`
A constructor and a runtime setting. In conjunction with `storageUsage` you can do runtime checks and adjust the limit to your needs. If you have to lower the limit below the current usage, images will be removed in FIFO order and may turn into a placeholder in the terminal's scrollback (if `showPlaceholder` is set). When adjusting keep in mind to leave enough room for peak memory use during decoding. A constructor and a runtime setting. In conjunction with `storageUsage` you can do runtime checks and adjust the limit to your needs. If you have to lower the limit below the current usage, images will be removed in FIFO order and may turn into a placeholder in the terminal's scrollback (if `showPlaceholder` is set). When adjusting keep in mind to leave enough room for memory peaking for decoding.
- `sixelSizeLimit | iipSizeLimit | kittySizeLimit` - `sixelSizeLimit | iipSizeLimit | kittySizeLimit`
Constructor settings. This has only a small impact on peak memory during decoding. It is meant to avoid processing of overly big or broken image sequences at an earlier phase, thus may stop the invoked decoders from entering memory intensive tasks for potentially invalid data. Constructor settings. This has only a small impact on peaking memory during decoding. It is meant to avoid processing of overly big or broken image sequences at an earlier phase, thus may stop the invoked decoders from entering memory intensive tasks for potentially invalid data.
### Terminal Interaction ### Terminal Interaction
+3 -3
View File
@@ -222,8 +222,8 @@ export class IIPHandler implements IOscHandler, IResetHandler {
private _dim(s: string, total: number, cdim: number): number { private _dim(s: string, total: number, cdim: number): number {
if (s === 'auto') return 0; if (s === 'auto') return 0;
if (s.endsWith('%')) return parseInt(s.slice(0, -1), 10) * total / 100; if (s.endsWith('%')) return parseInt(s.slice(0, -1)) * total / 100;
if (s.endsWith('px')) return parseInt(s.slice(0, -2), 10); if (s.endsWith('px')) return parseInt(s.slice(0, -2));
return parseInt(s, 10) * cdim; return parseInt(s) * cdim;
} }
} }
+1 -1
View File
@@ -3,7 +3,7 @@
* @license MIT * @license MIT
*/ */
import { IAddImageOpts } from './Types'; import { IAddImageOpts } from 'Types';
import { ImageStorage } from './ImageStorage'; import { ImageStorage } from './ImageStorage';
/** /**
+1 -1
View File
@@ -5,7 +5,7 @@
import type { ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { ITerminalAddon, IDisposable } from '@xterm/xterm';
import type { ImageAddon as IImageApi } from '@xterm/addon-image'; import type { ImageAddon as IImageApi } from '@xterm/addon-image';
import { Emitter, type IEvent } from 'common/primitives/Event'; import { Emitter, type IEvent } from 'common/base/Event';
import { IIPHandler } from './IIPHandler'; import { IIPHandler } from './IIPHandler';
import { ImageRenderer } from './ImageRenderer'; import { ImageRenderer } from './ImageRenderer';
import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage'; import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage';
+1 -1
View File
@@ -6,7 +6,7 @@
import { toRGBA8888 } from 'sixel/lib/Colors'; import { toRGBA8888 } from 'sixel/lib/Colors';
import { IDisposable } from '@xterm/xterm'; import { IDisposable } from '@xterm/xterm';
import { ICellSize, ImageLayer, ITerminalExt, IImageSpec, IRenderDimensions, IRenderService } from './Types'; import { ICellSize, ImageLayer, ITerminalExt, IImageSpec, IRenderDimensions, IRenderService } from './Types';
import { Disposable, MutableDisposable, toDisposable } from 'common/primitives/Lifecycle'; import { Disposable, MutableDisposable, toDisposable } from 'common/base/Lifecycle';
const enum Constants { const enum Constants {
PLACEHOLDER_LENGTH = 4096, PLACEHOLDER_LENGTH = 4096,
+5 -5
View File
@@ -6,11 +6,11 @@
import { IDisposable, IMarker, Terminal } from '@xterm/xterm'; import { IDisposable, IMarker, Terminal } from '@xterm/xterm';
// private imports from base repo we build against // private imports from base repo we build against
import { Attributes, BgFlags, Content, ExtFlags, UnderlineStyle } from 'common/primitives/buffer/Constants'; import { Attributes, BgFlags, Content, ExtFlags, UnderlineStyle } from 'common/buffer/Constants';
import type { AttributeData } from 'common/primitives/buffer/AttributeData'; import type { AttributeData } from 'common/buffer/AttributeData';
import type { IParams, IDcsHandler, IOscHandler, IApcHandler, IEscapeSequenceParser } from 'common/primitives/parser/Types'; import type { IParams, IDcsHandler, IOscHandler, IApcHandler, IEscapeSequenceParser } from 'common/parser/Types';
import type { IInputHandler } from 'common/Types'; import type { IBufferLine, IExtendedAttrs } from 'common/buffer/CellTypes';
import type { IBufferLine, IExtendedAttrs } from 'common/primitives/buffer/Types'; import type { IInputHandler } from 'common/terminal/InputHandlerTypes';
import type { ITerminal, ReadonlyColorSet } from 'browser/Types'; import type { ITerminal, ReadonlyColorSet } from 'browser/Types';
import type { IRenderDimensions } from 'browser/renderer/shared/Types'; import type { IRenderDimensions } from 'browser/renderer/shared/Types';
import type { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; import type { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
@@ -168,7 +168,7 @@ export function parseKittyCommand(data: string): IKittyCommand {
cmd.deleteSelector = value; cmd.deleteSelector = value;
continue; continue;
} }
const numValue = parseInt(value, 10); const numValue = parseInt(value);
switch (key) { switch (key) {
case KittyKey.FORMAT: cmd.format = numValue; break; case KittyKey.FORMAT: cmd.format = numValue; break;
case KittyKey.ID: cmd.id = numValue; break; case KittyKey.ID: cmd.id = numValue; break;
+1 -1
View File
@@ -21,7 +21,7 @@ const CACHE_SIZE = 100000;
/** /**
* Enable ligature support for the provided Terminal instance. To function * Enable ligature support for the provided Terminal instance. To function
* properly, this must be called after `open()` is called on the terminal. If * properly, this must be called after `open()` is called on the therminal. If
* the font currently in use supports ligatures, the terminal will automatically * the font currently in use supports ligatures, the terminal will automatically
* start to render them. * start to render them.
* @param term Terminal instance from xterm.js * @param term Terminal instance from xterm.js
+1 -1
View File
@@ -5,7 +5,7 @@
import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm';
import type { ProgressAddon as IProgressApi, IProgressState } from '@xterm/addon-progress'; import type { ProgressAddon as IProgressApi, IProgressState } from '@xterm/addon-progress';
import type { Emitter, IEvent } from 'common/primitives/Event'; import type { Emitter, IEvent } from 'common/base/Event';
const enum ProgressType { const enum ProgressType {
@@ -1,81 +0,0 @@
/**
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { DecorationManager } from './DecorationManager';
import { SearchEngine } from './SearchEngine';
import { SearchLineCache } from './SearchLineCache';
import { Terminal } from 'browser/public/Terminal';
import type { ISearchDecorationOptions } from '@xterm/addon-search';
import type { IDecorationOptions } from '@xterm/xterm';
import { DisposableStore } from 'common/primitives/Lifecycle';
function writeP(terminal: Terminal, data: string): Promise<void> {
return new Promise(r => terminal.write(data, r));
}
describe('DecorationManager', () => {
let store: DisposableStore;
let terminal: Terminal;
let decorationManager: DecorationManager;
beforeEach(() => {
store = new DisposableStore();
terminal = store.add(new Terminal({ cols: 10, rows: 5 }));
decorationManager = store.add(new DecorationManager(terminal));
});
it('should split highlight decorations for a wrapped match', async () => {
await writeP(terminal, '0123456789abcde');
const searchEngine = new SearchEngine(terminal, store.add(new SearchLineCache(terminal)));
const match = searchEngine.find('9abc', 0, 0);
assert.ok(match);
const decorationOptions: IDecorationOptions[] = [];
const registerDecoration = terminal.registerDecoration.bind(terminal);
terminal.registerDecoration = (options: IDecorationOptions) => {
decorationOptions.push(options);
return registerDecoration(options);
};
const options: ISearchDecorationOptions = {
matchOverviewRuler: '#ff0000',
activeMatchColorOverviewRuler: '#00ff00'
};
decorationManager.createHighlightDecorations([match], options);
assert.strictEqual(decorationOptions.length, 2);
assert.strictEqual(decorationOptions[0].x, 9);
assert.strictEqual(decorationOptions[0].width, 1);
assert.strictEqual(decorationOptions[1].x, 0);
assert.strictEqual(decorationOptions[1].width, 3);
const withOverviewRuler = decorationOptions.filter(o => o.overviewRulerOptions !== undefined);
assert.strictEqual(withOverviewRuler.length, 2);
});
it('should only add one overview ruler marker per buffer line', async () => {
await writeP(terminal, 'abcdefghij');
const decorationOptions: IDecorationOptions[] = [];
const registerDecoration = terminal.registerDecoration.bind(terminal);
terminal.registerDecoration = (options: IDecorationOptions) => {
decorationOptions.push(options);
return registerDecoration(options);
};
const options: ISearchDecorationOptions = {
matchOverviewRuler: '#ff0000',
activeMatchColorOverviewRuler: '#00ff00'
};
decorationManager.createHighlightDecorations([
{ term: 'a', col: 0, row: 0, size: 1 },
{ term: 'f', col: 5, row: 0, size: 1 }
], options);
const withOverviewRuler = decorationOptions.filter(o => o.overviewRulerOptions !== undefined);
assert.strictEqual(withOverviewRuler.length, 1);
assert.strictEqual(withOverviewRuler[0].x, 0);
});
});
+1 -1
View File
@@ -5,7 +5,7 @@
import type { Terminal, IDisposable, IDecoration } from '@xterm/xterm'; import type { Terminal, IDisposable, IDecoration } from '@xterm/xterm';
import type { ISearchDecorationOptions } from '@xterm/addon-search'; import type { ISearchDecorationOptions } from '@xterm/addon-search';
import { dispose, Disposable, toDisposable } from 'common/primitives/Lifecycle'; import { dispose, Disposable, toDisposable } from 'common/base/Lifecycle';
import type { ISearchResult } from './SearchEngine'; import type { ISearchResult } from './SearchEngine';
/** /**
+9 -11
View File
@@ -5,9 +5,9 @@
import type { Terminal, IDisposable, ITerminalAddon } from '@xterm/xterm'; import type { Terminal, IDisposable, ITerminalAddon } from '@xterm/xterm';
import type { SearchAddon as ISearchApi, ISearchOptions, ISearchAddonOptions, ISearchResultChangeEvent, ISearchDecorationOptions } from '@xterm/addon-search'; import type { SearchAddon as ISearchApi, ISearchOptions, ISearchAddonOptions, ISearchResultChangeEvent, ISearchDecorationOptions } from '@xterm/addon-search';
import { Emitter, type IEvent } from 'common/primitives/Event'; import { Emitter, type IEvent } from 'common/base/Event';
import { Disposable, MutableDisposable, toDisposable } from 'common/primitives/Lifecycle'; import { Disposable, MutableDisposable, toDisposable } from 'common/base/Lifecycle';
import { disposableTimeout } from 'common/primitives/Async'; import { disposableTimeout } from 'common/base/Async';
import { SearchLineCache } from './SearchLineCache'; import { SearchLineCache } from './SearchLineCache';
import { SearchState } from './SearchState'; import { SearchState } from './SearchState';
import { SearchEngine, type ISearchResult } from './SearchEngine'; import { SearchEngine, type ISearchResult } from './SearchEngine';
@@ -142,14 +142,12 @@ export class SearchAddon extends Disposable implements ITerminalAddon, ISearchAp
} }
prevResult = result; prevResult = result;
results.push(prevResult); results.push(prevResult);
const cols = this._terminal.cols; result = this._engine.find(
let nextCol = prevResult.col + prevResult.size; term,
let nextRow = prevResult.row; prevResult.col + prevResult.term.length >= this._terminal.cols ? prevResult.row + 1 : prevResult.row,
if (nextCol >= cols) { prevResult.col + prevResult.term.length >= this._terminal.cols ? 0 : prevResult.col + 1,
nextRow += Math.floor(nextCol / cols); searchOptions
nextCol = nextCol % cols; );
}
result = this._engine.find(term, nextRow, nextCol, searchOptions);
} }
this._resultTracker.updateResults(results, this._highlightLimit); this._resultTracker.updateResults(results, this._highlightLimit);
+1 -1
View File
@@ -7,7 +7,7 @@ import { SearchEngine } from './SearchEngine';
import { SearchLineCache } from './SearchLineCache'; import { SearchLineCache } from './SearchLineCache';
import { Terminal } from 'browser/public/Terminal'; import { Terminal } from 'browser/public/Terminal';
import type { ISearchOptions } from '@xterm/addon-search'; import type { ISearchOptions } from '@xterm/addon-search';
import { DisposableStore } from 'common/primitives/Lifecycle'; import { DisposableStore } from 'common/base/Lifecycle';
function writeP(terminal: Terminal, data: string): Promise<void> { function writeP(terminal: Terminal, data: string): Promise<void> {
return new Promise(r => terminal.write(data, r)); return new Promise(r => terminal.write(data, r));
+1 -1
View File
@@ -60,7 +60,7 @@ export class SearchEngine {
this._terminal.clearSelection(); this._terminal.clearSelection();
return undefined; return undefined;
} }
if (startCol >= this._terminal.cols) { if (startCol > this._terminal.cols) {
throw new Error(`Invalid col: ${startCol} to search in terminal of ${this._terminal.cols} cols`); throw new Error(`Invalid col: ${startCol} to search in terminal of ${this._terminal.cols} cols`);
} }
@@ -5,7 +5,7 @@
import { assert } from 'chai'; import { assert } from 'chai';
import { SearchLineCache, LineCacheEntry } from './SearchLineCache'; import { SearchLineCache, LineCacheEntry } from './SearchLineCache';
import { Terminal } from 'browser/public/Terminal'; import { Terminal } from 'browser/public/Terminal';
import { timeout } from 'common/primitives/Async'; import { timeout } from 'common/base/Async';
function writeP(terminal: Terminal, data: string): Promise<void> { function writeP(terminal: Terminal, data: string): Promise<void> {
return new Promise(r => terminal.write(data, r)); return new Promise(r => terminal.write(data, r));
+2 -2
View File
@@ -4,8 +4,8 @@
*/ */
import type { Terminal } from '@xterm/xterm'; import type { Terminal } from '@xterm/xterm';
import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'common/primitives/Lifecycle'; import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'common/base/Lifecycle';
import { disposableTimeout } from 'common/primitives/Async'; import { disposableTimeout } from 'common/base/Async';
export type LineCacheEntry = [ export type LineCacheEntry = [
/** /**
@@ -5,8 +5,8 @@
import type { ISearchResultChangeEvent } from '@xterm/addon-search'; import type { ISearchResultChangeEvent } from '@xterm/addon-search';
import type { IDisposable } from '@xterm/xterm'; import type { IDisposable } from '@xterm/xterm';
import { Emitter, type IEvent } from 'common/primitives/Event'; import { Emitter, type IEvent } from 'common/base/Event';
import { Disposable } from 'common/primitives/Lifecycle'; import { Disposable } from 'common/base/Lifecycle';
import type { ISearchResult } from './SearchEngine'; import type { ISearchResult } from './SearchEngine';
/** /**
+2 -16
View File
@@ -414,24 +414,10 @@ test.describe('Search Tests', () => {
}); });
test.describe('Regression tests', () => { test.describe('Regression tests', () => {
test('should advance highlight-all scan by buffer match size for wide characters', async () => {
await ctx.page.evaluate(`
window.calls = [];
window.search.onDidChangeResults(e => window.calls.push(e));
`);
await ctx.proxy.resize(3, 5);
await ctx.proxy.write('𝄞𝄞𝄞');
strictEqual(await ctx.page.evaluate(`window.search.findNext('𝄞', { decorations: { activeMatchColorOverviewRuler: '#ff0000', matchOverviewRuler: '#ffff00' } })`), true);
deepStrictEqual(await ctx.page.evaluate('window.calls'), [
{ resultCount: 3, resultIndex: 0 }
]);
await ctx.proxy.resize(80, 24);
});
test.describe('#2444 wrapped line content not being found', () => { test.describe('#2444 wrapped line content not being found', () => {
let fixture: string; let fixture: string;
test.beforeAll(async () => { test.beforeAll(async () => {
fixture = (await new Promise<Buffer>((res, rej) => readFile(resolve(__dirname, '../fixtures/issue-2444'), (err, data) => err ? rej(err) : res(data)))).toString(); fixture = (await new Promise<Buffer>(r => readFile(resolve(__dirname, '../fixtures/issue-2444'), (err, data) => r(data)))).toString();
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
fixture = fixture.replace(/\n/g, '\n\r'); fixture = fixture.replace(/\n/g, '\n\r');
} }
@@ -515,7 +501,7 @@ test.describe('Search Tests', () => {
window.search.onDidChangeResults(e => window.calls.push(e)); window.search.onDidChangeResults(e => window.calls.push(e));
`); `);
// Move cursor forward 1 time to create a null character, as opposed to regular whitespace // Move cursor forward 1 time to create a null character, as opposed to regular whitespace
await ctx.proxy.write('\x1b[CHi Hi'); await ctx.proxy.write('\\x1b[CHi Hi');
strictEqual(await ctx.page.evaluate(`window.search.findPrevious('h', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true); strictEqual(await ctx.page.evaluate(`window.search.findPrevious('h', { decorations: { activeMatchColorOverviewRuler: '#ff0000' } })`), true);
deepStrictEqual(await ctx.page.evaluate('window.calls'), [ deepStrictEqual(await ctx.page.evaluate('window.calls'), [
{ resultCount: 2, resultIndex: 1 } { resultCount: 2, resultIndex: 1 }
@@ -6,7 +6,7 @@
import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark'; import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark';
import { spawn } from 'node-pty'; import { spawn } from 'node-pty';
import { Utf8ToUtf32, stringFromCodePoint } from 'common/primitives/input/TextDecoder'; import { Utf8ToUtf32, stringFromCodePoint } from 'common/base/TextDecoder';
import { Terminal } from 'browser/public/Terminal'; import { Terminal } from 'browser/public/Terminal';
import { SerializeAddon } from 'SerializeAddon'; import { SerializeAddon } from 'SerializeAddon';
+3 -3
View File
@@ -21,8 +21,8 @@
"package": "../../node_modules/.bin/webpack", "package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package", "prepublishOnly": "npm run package",
"start": "node ../../demo/start", "start": "node ../../demo/start",
"benchmark": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json", "benchmark": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json",
"benchmark-baseline": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/benchmark/*benchmark.js", "benchmark-baseline": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/benchmark/*benchmark.js",
"benchmark-eval": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js" "benchmark-eval": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js"
} }
} }
+6 -6
View File
@@ -7,10 +7,10 @@
import type { IBuffer, IBufferCell, IBufferRange, ITerminalAddon, Terminal } from '@xterm/xterm'; import type { IBuffer, IBufferCell, IBufferRange, ITerminalAddon, Terminal } from '@xterm/xterm';
import type { IHTMLSerializeOptions, SerializeAddon as ISerializeApi, ISerializeOptions, ISerializeRange } from '@xterm/addon-serialize'; import type { IHTMLSerializeOptions, SerializeAddon as ISerializeApi, ISerializeOptions, ISerializeRange } from '@xterm/addon-serialize';
import { IColor } from 'common/primitives/Types'; import type { IAttributeData } from 'common/buffer/CellTypes';
import { IAttributeData } from 'common/primitives/buffer/Types'; import type { IColor } from 'common/base/ColorTypes';
import { DEFAULT_ANSI_COLORS } from 'browser/Types'; import { DEFAULT_ANSI_COLORS } from 'browser/Types';
import { UnderlineStyle } from 'common/primitives/buffer/Constants'; import { UnderlineStyle } from 'common/buffer/Constants';
function constrain(value: number, low: number, high: number): number { function constrain(value: number, low: number, high: number): number {
return Math.max(low, Math.min(value, high)); return Math.max(low, Math.min(value, high));
@@ -173,7 +173,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
private _thisRowLastSecondChar: IBufferCell = this._buffer.getNullCell(); private _thisRowLastSecondChar: IBufferCell = this._buffer.getNullCell();
private _nextRowFirstChar: IBufferCell = this._buffer.getNullCell(); private _nextRowFirstChar: IBufferCell = this._buffer.getNullCell();
protected _rowEnd(row: number, isLastRow: boolean): void { protected _rowEnd(row: number, isLastRow: boolean): void {
// if there is colorful empty cell at line end, we must pad it back, or the color block // if there is colorful empty cell at line end, whe must pad it back, or the the color block
// will missing // will missing
if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) { if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) {
// use clear right to set background. // use clear right to set background.
@@ -221,7 +221,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
// you can't use control sequence to move cursor to (x === row) // you can't use control sequence to move cursor to (x === row)
(thisRowLastChar.getChars() || thisRowLastChar.getWidth() === 0) && (thisRowLastChar.getChars() || thisRowLastChar.getWidth() === 0) &&
// change background of the first wrapped cell also affects BCE // change background of the first wrapped cell also affects BCE
// so we mark it as invalid to simplify the process to determine line separator // so we mark it as invalid to simply the process to determine line separator
equalBg(thisRowLastChar, nextRowFirstChar) equalBg(thisRowLastChar, nextRowFirstChar)
) { ) {
isValid = true; isValid = true;
@@ -233,7 +233,7 @@ class StringSerializeHandler extends BaseSerializeHandler {
isNextRowFirstCharDoubleWidth && isNextRowFirstCharDoubleWidth &&
(thisRowLastSecondChar.getChars() || thisRowLastSecondChar.getWidth() === 0) && (thisRowLastSecondChar.getChars() || thisRowLastSecondChar.getWidth() === 0) &&
// change background of the first wrapped cell also affects BCE // change background of the first wrapped cell also affects BCE
// so we mark it as invalid to simplify the process to determine line separator // so we mark it as invalid to simply the process to determine line separator
equalBg(thisRowLastChar, nextRowFirstChar) && equalBg(thisRowLastChar, nextRowFirstChar) &&
equalBg(thisRowLastSecondChar, nextRowFirstChar) equalBg(thisRowLastSecondChar, nextRowFirstChar)
) { ) {
@@ -192,7 +192,7 @@ test.describe('SerializeAddon', () => {
const cols = 10; const cols = 10;
const line = '+'.repeat(cols); const line = '+'.repeat(cols);
const lines: string[] = [ const lines: string[] = [
sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags at the end, serialize will use \x1b[0m to clear instead of the specific disable sequence sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags a the end, serialize will use \x1b[0m to clear instead of the sepcific disable sequence
sgr(INVERSE) + line, sgr(INVERSE) + line,
sgr(BOLD) + line, sgr(BOLD) + line,
sgr(UNDERLINED) + line, sgr(UNDERLINED) + line,
@@ -6,7 +6,7 @@
import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark'; import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark';
import { spawn } from 'node-pty'; import { spawn } from 'node-pty';
import { Utf8ToUtf32, stringFromCodePoint } from 'common/primitives/input/TextDecoder'; import { Utf8ToUtf32, stringFromCodePoint } from 'common/base/TextDecoder';
import { CoreBrowserTerminal } from 'browser/CoreBrowserTerminal'; import { CoreBrowserTerminal } from 'browser/CoreBrowserTerminal';
import { UnicodeGraphemeProvider } from 'UnicodeGraphemeProvider'; import { UnicodeGraphemeProvider } from 'UnicodeGraphemeProvider';
+3 -3
View File
@@ -21,8 +21,8 @@
"package": "../../node_modules/.bin/webpack", "package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package", "prepublishOnly": "npm run package",
"start": "node ../../demo/start", "start": "node ../../demo/start",
"benchmark": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json out-benchmark/benchmark/*benchmark.js", "benchmark": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json out-benchmark/benchmark/*benchmark.js",
"benchmark-baseline": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/benchmark/*benchmark.js", "benchmark-baseline": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/benchmark/*benchmark.js",
"benchmark-eval": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js" "benchmark-eval": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js"
} }
} }
@@ -2,7 +2,7 @@
* Copyright (c) 2023 The xterm.js authors. All rights reserved. * Copyright (c) 2023 The xterm.js authors. All rights reserved.
* @license MIT * @license MIT
* *
* UnicodeVersionProvider for V15 with grapheme cluster handling. * UnicodeVersionProvider for V15 with grapeme cluster handleing.
*/ */
import type { Terminal, ITerminalAddon, IUnicodeHandling } from '@xterm/xterm'; import type { Terminal, ITerminalAddon, IUnicodeHandling } from '@xterm/xterm';
@@ -57,8 +57,7 @@ function isUrl(urlString: string): boolean {
export class LinkComputer { export class LinkComputer {
public static computeLink(y: number, regex: RegExp, terminal: Terminal, activate: (event: MouseEvent, uri: string) => void): ILink[] { public static computeLink(y: number, regex: RegExp, terminal: Terminal, activate: (event: MouseEvent, uri: string) => void): ILink[] {
const flags = regex.flags.includes('g') ? regex.flags : `${regex.flags}g`; const rex = new RegExp(regex.source, (regex.flags || '') + 'g');
const rex = new RegExp(regex.source, flags);
const [lines, startLineIndex] = LinkComputer._getWindowedLineStrings(y - 1, terminal); const [lines, startLineIndex] = LinkComputer._getWindowedLineStrings(y - 1, terminal);
const line = lines.join(''); const line = lines.join('');
+2 -2
View File
@@ -7,13 +7,13 @@ import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm';
import type { WebLinksAddon as IWebLinksApi } from '@xterm/addon-web-links'; import type { WebLinksAddon as IWebLinksApi } from '@xterm/addon-web-links';
import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider'; import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider';
// consider everything starting with http:// or https:// // consider everthing starting with http:// or https://
// up to first whitespace, `"` or `'` as url // up to first whitespace, `"` or `'` as url
// NOTE: The repeated end clause is needed to not match a dangling `:` // NOTE: The repeated end clause is needed to not match a dangling `:`
// resembling the old (...)*([^:"\'\\s]) final path clause // resembling the old (...)*([^:"\'\\s]) final path clause
// additionally exclude early + final: // additionally exclude early + final:
// - unsafe from rfc3986: !*'() // - unsafe from rfc3986: !*'()
// - unsafe chars from rfc1738: {}|\^~[]` (minus [] for ipv6 addresses, also allow ~) // - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 adresses, also allow ~)
// also exclude as finals: // also exclude as finals:
// - final interpunction like ,.!? // - final interpunction like ,.!?
// - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) // - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls)
+1 -1
View File
@@ -23,7 +23,7 @@ See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-
### Handling Context Loss ### Handling Context Loss
The browser may drop WebGL contexts for various reasons like OOM or after the system has been suspended. There is an API exposed that fires the `webglcontextlost` event on the canvas so embedders can handle it however they wish. An easy, but suboptimal way, to handle this is by disposing of WebglAddon when the event fires: The browser may drop WebGL contexts for various reasons like OOM or after the system has been suspended. There is an API exposed that fires the `webglcontextlost` event fired on the canvas so embedders can handle it however they wish. An easy, but suboptimal way, to handle this is by disposing of WebglAddon when the event fires:
```ts ```ts
const terminal = new Terminal(); const terminal = new Terminal();
+3 -3
View File
@@ -1,11 +1,11 @@
import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { ReadonlyColorSet } from 'browser/Types'; import { ReadonlyColorSet } from 'browser/Types';
import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle } from 'common/primitives/buffer/Constants'; import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle } from 'common/buffer/Constants';
import { IDecorationService, IOptionsService } from 'common/services/Services'; import { IDecorationService, IOptionsService } from 'common/services/Services';
import { ICellData } from 'common/primitives/buffer/Types'; import type { ICellData } from 'common/buffer/CellTypes';
import { Terminal } from '@xterm/xterm'; import { Terminal } from '@xterm/xterm';
import { rgba } from 'common/primitives/Color'; import { rgba } from 'common/base/Color';
import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils'; import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils';
import { blockPatternCodepoints } from './customGlyphs/CustomGlyphDefinitions'; import { blockPatternCodepoints } from './customGlyphs/CustomGlyphDefinitions';
@@ -6,8 +6,8 @@
import { assert } from 'chai'; import { assert } from 'chai';
import { configEquals } from './CharAtlasUtils'; import { configEquals } from './CharAtlasUtils';
import { ICharAtlasConfig } from './Types'; import { ICharAtlasConfig } from './Types';
import { NULL_COLOR } from 'common/primitives/Color'; import { NULL_COLOR } from 'common/base/Color';
import { IColor } from 'common/primitives/Types'; import type { IColor } from 'common/base/ColorTypes';
function createTestConfig(overrides: Partial<ICharAtlasConfig> = {}): ICharAtlasConfig { function createTestConfig(overrides: Partial<ICharAtlasConfig> = {}): ICharAtlasConfig {
const color: IColor = { css: '#ffffff', rgba: 0xffffffff }; const color: IColor = { css: '#ffffff', rgba: 0xffffffff };
+2 -2
View File
@@ -4,10 +4,10 @@
*/ */
import { ICharAtlasConfig } from './Types'; import { ICharAtlasConfig } from './Types';
import { Attributes } from 'common/primitives/buffer/Constants'; import { Attributes } from 'common/buffer/Constants';
import { ITerminalOptions } from '@xterm/xterm'; import { ITerminalOptions } from '@xterm/xterm';
import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { IColorSet, ReadonlyColorSet } from 'browser/Types';
import { NULL_COLOR } from 'common/primitives/Color'; import { NULL_COLOR } from 'common/base/Color';
export function generateConfig(deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, options: Required<ITerminalOptions>, colors: ReadonlyColorSet, devicePixelRatio: number, deviceMaxTextureSize: number, customGlyphs: boolean = true): ICharAtlasConfig { export function generateConfig(deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, options: Required<ITerminalOptions>, colors: ReadonlyColorSet, devicePixelRatio: number, deviceMaxTextureSize: number, customGlyphs: boolean = true): ICharAtlasConfig {
// null out some fields that don't matter // null out some fields that don't matter
+1 -1
View File
@@ -3,7 +3,7 @@
* @license MIT * @license MIT
*/ */
import { isFirefox, isLegacyEdge } from 'common/primitives/Platform'; import { isFirefox, isLegacyEdge } from 'common/base/Platform';
export const DIM_OPACITY = 0.5; export const DIM_OPACITY = 0.5;
// The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge // The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge
@@ -3,7 +3,7 @@
* @license MIT * @license MIT
*/ */
import { toDisposable, IDisposable } from 'common/primitives/Lifecycle'; import { toDisposable, IDisposable } from 'common/base/Lifecycle';
export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable { export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable {
// Observe any resizes to the element and extract the actual pixel size of the element if the // Observe any resizes to the element and extract the actual pixel size of the element if the
+4 -5
View File
@@ -4,8 +4,8 @@
*/ */
import { TextureAtlas } from './TextureAtlas'; import { TextureAtlas } from './TextureAtlas';
import { IRenderDimensions } from 'browser/renderer/shared/Types'; import { IRenderDimensions } from 'browser/renderer/shared/Types';
import { NULL_CELL_CODE } from 'common/primitives/buffer/Constants'; import { NULL_CELL_CODE } from 'common/buffer/Constants';
import { Disposable, toDisposable } from 'common/primitives/Lifecycle'; import { Disposable, toDisposable } from 'common/base/Lifecycle';
import { Terminal } from '@xterm/xterm'; import { Terminal } from '@xterm/xterm';
import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject, type IRasterizedGlyph, type ITextureAtlas } from './Types'; import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject, type IRasterizedGlyph, type ITextureAtlas } from './Types';
import { createProgram, GLTexture, PROJECTION_MATRIX } from './WebglUtils'; import { createProgram, GLTexture, PROJECTION_MATRIX } from './WebglUtils';
@@ -220,7 +220,7 @@ export class GlyphRenderer extends Disposable {
public updateCell(x: number, y: number, code: number, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { public updateCell(x: number, y: number, code: number, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void {
// Since this function is called for every cell (`rows*cols`), it must be very optimized. It // Since this function is called for every cell (`rows*cols`), it must be very optimized. It
// should not instantiate any variables unless a new glyph is drawn to the cache where the // should not instantiate any variables unless a new glyph is drawn to the cache where the
// slight slowdown is acceptable for the developer ergonomics provided as it's a one-off for // slight slowdown is acceptable for the developer ergonomics provided as it's a once of for
// each glyph. // each glyph.
this._updateCell(this._vertices.attributes, x, y, code, bg, fg, ext, chars, width, lastBg); this._updateCell(this._vertices.attributes, x, y, code, bg, fg, ext, chars, width, lastBg);
} }
@@ -389,9 +389,8 @@ export class GlyphRenderer extends Disposable {
gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[i].texture); gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[i].texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, atlas.pages[i].canvas); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, atlas.pages[i].canvas);
gl.generateMipmap(gl.TEXTURE_2D);
this._atlasTextures[i].version = atlas.pages[i].version; this._atlasTextures[i].version = atlas.pages[i].version;
} }
+3 -3
View File
@@ -6,9 +6,9 @@
import { IRenderDimensions } from 'browser/renderer/shared/Types'; import { IRenderDimensions } from 'browser/renderer/shared/Types';
import { IThemeService } from 'browser/services/Services'; import { IThemeService } from 'browser/services/Services';
import { ReadonlyColorSet } from 'browser/Types'; import { ReadonlyColorSet } from 'browser/Types';
import { Attributes, FgFlags } from 'common/primitives/buffer/Constants'; import { Attributes, FgFlags } from 'common/buffer/Constants';
import { Disposable, toDisposable } from 'common/primitives/Lifecycle'; import { Disposable, toDisposable } from 'common/base/Lifecycle';
import { IColor } from 'common/primitives/Types'; import type { IColor } from 'common/base/ColorTypes';
import { Terminal } from '@xterm/xterm'; import { Terminal } from '@xterm/xterm';
import { RenderModelConstants } from './RenderModel'; import { RenderModelConstants } from './RenderModel';
import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject } from './Types'; import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject } from './Types';
+17 -33
View File
@@ -8,14 +8,14 @@ import { DIM_OPACITY, TEXT_BASELINE } from './Constants';
import { tryDrawCustomGlyph } from './customGlyphs/CustomGlyphRasterizer'; import { tryDrawCustomGlyph } from './customGlyphs/CustomGlyphRasterizer';
import { computeNextVariantOffset, treatGlyphAsBackgroundColor, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; import { computeNextVariantOffset, treatGlyphAsBackgroundColor, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
import { IBoundingBox, ICharAtlasConfig, IRasterizedGlyph, ITextureAtlas } from './Types'; import { IBoundingBox, ICharAtlasConfig, IRasterizedGlyph, ITextureAtlas } from './Types';
import { NULL_COLOR, channels, color, rgba } from 'common/primitives/Color'; import { NULL_COLOR, channels, color, rgba } from 'common/base/Color';
import { FourKeyMap } from 'common/primitives/MultiKeyMap'; import { FourKeyMap } from 'common/base/MultiKeyMap';
import { IdleTaskQueue } from 'common/primitives/TaskQueue'; import { IdleTaskQueue } from 'common/base/TaskQueue';
import { IColor } from 'common/primitives/Types'; import type { IColor } from 'common/base/ColorTypes';
import { AttributeData } from 'common/primitives/buffer/AttributeData'; import { AttributeData } from 'common/buffer/AttributeData';
import { Attributes, DEFAULT_COLOR, DEFAULT_EXT, UnderlineStyle } from 'common/primitives/buffer/Constants'; import { Attributes, DEFAULT_COLOR, DEFAULT_EXT, UnderlineStyle } from 'common/buffer/Constants';
import { ILogService, IUnicodeService } from 'common/services/Services'; import { ILogService, IUnicodeService } from 'common/services/Services';
import { Emitter } from 'common/primitives/Event'; import { Emitter } from 'common/base/Event';
/** /**
* A shared object which is used to draw nothing for a particular cell. * A shared object which is used to draw nothing for a particular cell.
@@ -69,7 +69,7 @@ export class TextureAtlas implements ITextureAtlas {
private _overflowSizePage: AtlasPage | undefined; private _overflowSizePage: AtlasPage | undefined;
private _tmpCanvas: HTMLCanvasElement; private _tmpCanvas: HTMLCanvasElement;
// A temporary context that glyphs are drawn to before being transferred to the atlas. // A temporary context that glyphs are drawn to before being transfered to the atlas.
private _tmpCtx: CanvasRenderingContext2D; private _tmpCtx: CanvasRenderingContext2D;
private _workBoundingBox: IBoundingBox = { top: 0, left: 0, bottom: 0, right: 0 }; private _workBoundingBox: IBoundingBox = { top: 0, left: 0, bottom: 0, right: 0 };
@@ -152,7 +152,7 @@ export class TextureAtlas implements ITextureAtlas {
} }
private _createNewPage(): AtlasPage { private _createNewPage(): AtlasPage {
// Try merge the set of the 4 most used pages of the largest size. This is deferred to a // Try merge the set of the 4 most used pages of the largest size. This is is deferred to a
// microtask to ensure it does not interrupt textures that will be rendered in the current // microtask to ensure it does not interrupt textures that will be rendered in the current
// animation frame which would result in blank rendered areas. This is actually not that // animation frame which would result in blank rendered areas. This is actually not that
// expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. // expensive relative to drawing the glyphs, so there is no need to wait for an idle callback.
@@ -191,7 +191,7 @@ export class TextureAtlas implements ITextureAtlas {
return newPage; return newPage;
} }
const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a - b); const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a > b ? 1 : -1);
const mergedPageIndex = this.pages.length - mergingPages.length; const mergedPageIndex = this.pages.length - mergingPages.length;
// Merge into the new page // Merge into the new page
@@ -1080,33 +1080,17 @@ class AtlasPage {
size: number, size: number,
sourcePages?: AtlasPage[] sourcePages?: AtlasPage[]
) { ) {
if (sourcePages) {
for (const p of sourcePages) {
this._glyphs.push(...p.glyphs);
this._usedPixels += p._usedPixels;
}
}
this.canvas = createCanvas(document, size, size); this.canvas = createCanvas(document, size, size);
// The canvas needs alpha because we use clearColor to convert the background color to alpha. // The canvas needs alpha because we use clearColor to convert the background color to alpha.
// It might also contain some characters with transparent backgrounds if allowTransparency is // It might also contain some characters with transparent backgrounds if allowTransparency is
// set. // set.
this.ctx = throwIfFalsy(this.canvas.getContext('2d', { alpha: true })); this.ctx = throwIfFalsy(this.canvas.getContext('2d', { alpha: true }));
if (sourcePages) {
if (sourcePages.length === 4) {
// optimized for quadmerge
this._glyphs = this._glyphs.concat(
sourcePages[0].glyphs,
sourcePages[1].glyphs,
sourcePages[2].glyphs,
sourcePages[3].glyphs
);
this._usedPixels = sourcePages[0]._usedPixels +
sourcePages[1]._usedPixels +
sourcePages[2]._usedPixels +
sourcePages[3]._usedPixels;
} else {
// fallback for non quadmerges (should never be used)
for (let i = 0; i < sourcePages.length; ++i) {
this._glyphs = this._glyphs.concat(sourcePages[i].glyphs);
this._usedPixels += sourcePages[i]._usedPixels;
}
}
}
} }
public clear(): void { public clear(): void {
@@ -1141,7 +1125,7 @@ function clearColor(imageData: ImageData, bg: IColor, fg: IColor, enableThreshol
// were covered (fg=#8ae234, bg=#c4a000). // were covered (fg=#8ae234, bg=#c4a000).
const threshold = Math.floor((Math.abs(r - fgR) + Math.abs(g - fgG) + Math.abs(b - fgB)) / 12); const threshold = Math.floor((Math.abs(r - fgR) + Math.abs(g - fgG) + Math.abs(b - fgB)) / 12);
// Set alpha channel of relevant pixels to 0 // Set alpha channel of relevent pixels to 0
let isEmpty = true; let isEmpty = true;
for (let offset = 0; offset < imageData.data.length; offset += 4) { for (let offset = 0; offset < imageData.data.length; offset += 4) {
// Check exact match // Check exact match
+3 -2
View File
@@ -6,8 +6,9 @@
import { FontWeight } from '@xterm/xterm'; import { FontWeight } from '@xterm/xterm';
import { IColorSet } from 'browser/Types'; import { IColorSet } from 'browser/Types';
import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
import { CursorInactiveStyle, CursorStyle, type IDisposable } from 'common/primitives/Types'; import type { IDisposable } from 'common/base/Lifecycle';
import type { IEvent } from 'common/primitives/Event'; import { CursorInactiveStyle, CursorStyle } from 'common/services/Types';
import type { IEvent } from 'common/base/Event';
export interface IRenderModel { export interface IRenderModel {
cells: Uint32Array; cells: Uint32Array;
+4 -4
View File
@@ -7,12 +7,12 @@ import type { ITerminalAddon, Terminal } from '@xterm/xterm';
import type { IWebglAddonOptions, WebglAddon as IWebglApi } from '@xterm/addon-webgl'; import type { IWebglAddonOptions, WebglAddon as IWebglApi } from '@xterm/addon-webgl';
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
import { ITerminal } from 'browser/Types'; import { ITerminal } from 'browser/Types';
import { Disposable, toDisposable } from 'common/primitives/Lifecycle'; import { Disposable, toDisposable } from 'common/base/Lifecycle';
import { getSafariVersion, isSafari } from 'common/primitives/Platform'; import { getSafariVersion, isSafari } from 'common/base/Platform';
import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services'; import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services';
import { IWebGL2RenderingContext } from './Types'; import { IWebGL2RenderingContext } from './Types';
import { WebglRenderer } from './WebglRenderer'; import { WebglRenderer } from './WebglRenderer';
import { Emitter, EventUtils } from 'common/primitives/Event'; import { Emitter, EventUtils } from 'common/base/Event';
export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi { export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi {
private _terminal?: Terminal; private _terminal?: Terminal;
@@ -40,7 +40,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi
}; };
const gl = document.createElement('canvas').getContext('webgl2', contextAttributes) as IWebGL2RenderingContext; const gl = document.createElement('canvas').getContext('webgl2', contextAttributes) as IWebGL2RenderingContext;
if (!gl) { if (!gl) {
throw new Error('WebGL2 is only supported on Safari 16 and above'); throw new Error('Webgl2 is only supported on Safari 16 and above');
} }
} }
super(); super();
+14 -29
View File
@@ -10,10 +10,10 @@ import { CursorBlinkStateManager } from './CursorBlinkStateManager';
import { observeDevicePixelDimensions } from './DevicePixelObserver'; import { observeDevicePixelDimensions } from './DevicePixelObserver';
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { CharData, IBufferLine, ICellData } from 'common/primitives/buffer/Types'; import type { CharData, IBufferLine, ICellData } from 'common/buffer/CellTypes';
import { AttributeData } from 'common/primitives/buffer/AttributeData'; import { AttributeData } from 'common/buffer/AttributeData';
import { CellData } from 'common/primitives/buffer/CellData'; import { CellData } from 'common/buffer/CellData';
import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/primitives/buffer/Constants'; import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager'; import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager';
import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services'; import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services';
import { Terminal } from '@xterm/xterm'; import { Terminal } from '@xterm/xterm';
@@ -23,9 +23,9 @@ import { COMBINED_CHAR_BIT_MASK, RenderModel, RenderModelConstants } from './Ren
import { IWebGL2RenderingContext, type ITextureAtlas } from './Types'; import { IWebGL2RenderingContext, type ITextureAtlas } from './Types';
import { LinkRenderLayer } from './renderLayer/LinkRenderLayer'; import { LinkRenderLayer } from './renderLayer/LinkRenderLayer';
import { IRenderLayer } from './renderLayer/Types'; import { IRenderLayer } from './renderLayer/Types';
import { Emitter, EventUtils } from 'common/primitives/Event'; import { Emitter, EventUtils } from 'common/base/Event';
import { addDisposableListener } from 'browser/Dom'; import { addDisposableListener } from 'browser/Dom';
import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'common/primitives/Lifecycle'; import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'common/base/Lifecycle';
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils'; import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
const enum Constants { const enum Constants {
@@ -456,18 +456,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
for (y = start; y <= end; y++) { for (y = start; y <= end; y++) {
row = y + terminal.buffer.ydisp; row = y + terminal.buffer.ydisp;
const bufferLine = terminal.buffer.lines.get(row); line = terminal.buffer.lines.get(row)!;
if (!bufferLine) {
this._model.lineLengths[y] = 0;
for (x = 0; x < terminal.cols; x++) {
j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
modelUpdated = true;
this._nullModelCell(x, y, j, 0, 0, 0);
}
this._setRowBlinkState(y, false);
continue;
}
line = bufferLine;
let rowHasBlinkingCells = false; let rowHasBlinkingCells = false;
this._model.lineLengths[y] = 0; this._model.lineLengths[y] = 0;
isCursorRow = cursorY === row; isCursorRow = cursorY === row;
@@ -598,9 +587,13 @@ export class WebglRenderer extends Disposable implements IRenderer {
// Null out non-first cells // Null out non-first cells
for (x++; x <= lastCharX; x++) { for (x++; x <= lastCharX; x++) {
j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL; j = ((y * terminal.cols) + x) * RenderModelConstants.INDICIES_PER_CELL;
this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0);
this._model.cells[j] = NULL_CELL_CODE;
// Don't re-resolve the cell color since multi-colored ligature backgrounds are not // Don't re-resolve the cell color since multi-colored ligature backgrounds are not
// supported // supported
this._nullModelCell(x, y, j, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext); this._model.cells[j + RenderModelConstants.BG_OFFSET] = this._cellColorResolver.result.bg;
this._model.cells[j + RenderModelConstants.FG_OFFSET] = this._cellColorResolver.result.fg;
this._model.cells[j + RenderModelConstants.EXT_OFFSET] = this._cellColorResolver.result.ext;
} }
x--; // Go back to the previous update cell for next iteration x--; // Go back to the previous update cell for next iteration
} }
@@ -614,14 +607,6 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._updateTextBlinkState(); this._updateTextBlinkState();
} }
private _nullModelCell(x: number, y: number, cellIndex: number, bg: number, fg: number, ext: number): void {
this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, bg, fg, ext, NULL_CELL_CHAR, 0, 0);
this._model.cells[cellIndex] = NULL_CELL_CODE;
this._model.cells[cellIndex + RenderModelConstants.BG_OFFSET] = bg;
this._model.cells[cellIndex + RenderModelConstants.FG_OFFSET] = fg;
this._model.cells[cellIndex + RenderModelConstants.EXT_OFFSET] = ext;
}
private _resetBlinkingRowState(): void { private _resetBlinkingRowState(): void {
this._rowHasBlinkingCells = new Array(this._terminal.rows).fill(false); this._rowHasBlinkingCells = new Array(this._terminal.rows).fill(false);
this._rowHasBlinkingCellsCount = 0; this._rowHasBlinkingCellsCount = 0;
@@ -679,10 +664,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
this.dimensions.device.canvas.height = this._terminal.rows * this.dimensions.device.cell.height; this.dimensions.device.canvas.height = this._terminal.rows * this.dimensions.device.cell.height;
this.dimensions.device.canvas.width = this._terminal.cols * this.dimensions.device.cell.width; this.dimensions.device.canvas.width = this._terminal.cols * this.dimensions.device.cell.width;
// The size of the canvas on the page. It's important that this rounds to nearest integer // The the size of the canvas on the page. It's important that this rounds to nearest integer
// and not ceils as browsers often have floating point precision issues where // and not ceils as browsers often have floating point precision issues where
// `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when // `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when
// it's actually 1.1. Ceiling may cause blurriness as the backing canvas image is 1 pixel too // it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too
// large for the canvas element size. // large for the canvas element size.
this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / this._devicePixelRatio); this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / this._devicePixelRatio);
this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / this._devicePixelRatio); this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / this._devicePixelRatio);
@@ -5,7 +5,7 @@
import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphCharacterDefinition, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition } from './Types'; import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphCharacterDefinition, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition } from './Types';
/* oxlint-disable @stylistic/max-len -- generated glyph path data */ /* eslint-disable max-len */
const enum Shapes { const enum Shapes {
/** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1', /** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1',
@@ -221,8 +221,8 @@ function drawPathDefinitionCharacter(
const rx = parseFloat(args[0]) * deviceCellWidth; const rx = parseFloat(args[0]) * deviceCellWidth;
const ry = parseFloat(args[1]) * deviceCellHeight; const ry = parseFloat(args[1]) * deviceCellHeight;
const xAxisRotation = parseFloat(args[2]) * Math.PI / 180; const xAxisRotation = parseFloat(args[2]) * Math.PI / 180;
const largeArcFlag = parseInt(args[3], 10); const largeArcFlag = parseInt(args[3]);
const sweepFlag = parseInt(args[4], 10); const sweepFlag = parseInt(args[4]);
const x = xOffset + parseFloat(args[5]) * deviceCellWidth; const x = xOffset + parseFloat(args[5]) * deviceCellWidth;
const y = yOffset + parseFloat(args[6]) * deviceCellHeight; const y = yOffset + parseFloat(args[6]) * deviceCellHeight;
drawSvgArc(ctx, currentX, currentY, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y); drawSvgArc(ctx, currentX, currentY, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y);
@@ -7,8 +7,8 @@ import { ReadonlyColorSet } from 'browser/Types';
import { acquireTextureAtlas } from '../CharAtlasCache'; import { acquireTextureAtlas } from '../CharAtlasCache';
import { IRenderDimensions } from 'browser/renderer/shared/Types'; import { IRenderDimensions } from 'browser/renderer/shared/Types';
import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { Disposable, toDisposable } from 'common/primitives/Lifecycle'; import { Disposable, toDisposable } from 'common/base/Lifecycle';
import { CellData } from 'common/primitives/buffer/CellData'; import { CellData } from 'common/buffer/CellData';
import { IOptionsService } from 'common/services/Services'; import { IOptionsService } from 'common/services/Services';
import { Terminal } from '@xterm/xterm'; import { Terminal } from '@xterm/xterm';
import { IRenderLayer } from './Types'; import { IRenderLayer } from './Types';
+2 -2
View File
@@ -23,12 +23,12 @@ declare module '@xterm/addon-webgl' {
public readonly onChangeTextureAtlas: IEvent<HTMLCanvasElement>; public readonly onChangeTextureAtlas: IEvent<HTMLCanvasElement>;
/** /**
* An event that is fired when a new page is added to the texture atlas. * An event that is fired when the a new page is added to the texture atlas.
*/ */
public readonly onAddTextureAtlasCanvas: IEvent<HTMLCanvasElement>; public readonly onAddTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
/** /**
* An event that is fired when a page is removed from the texture atlas. * An event that is fired when the a page is removed from the texture atlas.
*/ */
public readonly onRemoveTextureAtlasCanvas: IEvent<HTMLCanvasElement>; public readonly onRemoveTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
+2 -5
View File
@@ -19,17 +19,14 @@ for (const addon of addons) {
// Demo job - This requires the others to be built so it's not included when building all // Demo job - This requires the others to be built so it's not included when building all
// jobs.push(createJob('demo-client', [`--demo-client`])); // jobs.push(createJob('demo-client', [`--demo-client`]));
const codes = await Promise.all(jobs.map(job => { await Promise.all(jobs.map((job, i) => {
return new Promise(r => { return new Promise(r => {
job.cp.on('exit', code => { job.cp.on('exit', code => {
log(`Finished \x1b[32m${job.name}\x1b[0m${code ? ' \x1b[31mwith errors\x1b[0m' : ''}`); log(`Finished \x1b[32m${job.name}\x1b[0m${code ? ' \x1b[31mwith errors\x1b[0m' : ''}`);
r(code ?? 1); r(code);
}); });
}); });
})); }));
if (codes.some(code => code !== 0)) {
process.exit(1);
}
/** /**
* @param {string} message * @param {string} message
+10 -40
View File
@@ -33,47 +33,17 @@ if (files.length === 0) {
console.log(`Linting ${files.length} changed file(s)...`); console.log(`Linting ${files.length} changed file(s)...`);
const cwd = path.join(__dirname, '..'); const eslintArgs = ['--max-warnings', '0', '--no-warn-ignored'];
const isWin = process.platform === 'win32';
/**
* @param {string} command
* @param {string[]} args
* @returns {Promise<void>}
*/
function run(command, args) {
return new Promise(/** @type {(resolve: () => void, reject: (reason: Error) => void) => void} */ ((resolve, reject) => {
const child = spawn(command, args, {
stdio: 'inherit',
cwd,
shell: isWin
});
child.on('close', code => {
if (code === 0) {
resolve();
} else {
reject(new Error(`${command} exited with code ${code}`));
}
});
}));
}
async function main() {
const oxlintArgs = ['--type-aware', '--max-warnings', '0'];
if (fix) { if (fix) {
oxlintArgs.push('--fix'); eslintArgs.push('--fix');
} }
oxlintArgs.push(...files); eslintArgs.push(...files);
const oxlint = isWin ? 'oxlint.cmd' : 'oxlint'; const eslint = process.platform === 'win32' ? 'eslint.cmd' : 'eslint';
await run(oxlint, oxlintArgs); const child = spawn(eslint, eslintArgs, {
stdio: 'inherit',
const eslintArgs = ['--config', 'eslint.config.naming.mjs', '--max-warnings', '0', ...files]; cwd: path.join(__dirname, '..'),
const eslint = isWin ? 'eslint.cmd' : 'eslint'; shell: process.platform === 'win32'
await run(eslint, eslintArgs);
}
main().catch(err => {
console.error(err.message);
process.exit(1);
}); });
child.on('close', code => process.exit(code ?? 0));
+1 -1
View File
@@ -81,7 +81,7 @@ function checkAndPublishPackage(packageDir, repoCommit, peerDependencies, update
// Update Version.ts with the new version // Update Version.ts with the new version
if (updateVersionTs) { if (updateVersionTs) {
const versionTsPath = path.join(packageDir, 'src/common/Version.ts'); const versionTsPath = path.join(packageDir, 'src/common/base/Version.ts');
const versionTsContent = fs.readFileSync(versionTsPath, 'utf8'); const versionTsContent = fs.readFileSync(versionTsPath, 'utf8');
const updatedVersionTs = versionTsContent.replace( const updatedVersionTs = versionTsContent.replace(
/export const XTERM_VERSION = '[^']+';/, /export const XTERM_VERSION = '[^']+';/,
+2 -2
View File
@@ -5,7 +5,7 @@ import { spawnSync } from 'node:child_process';
import { basename, dirname, resolve, sep } from 'node:path'; import { basename, dirname, resolve, sep } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
const nodeModulesPath = resolve(repoRoot, 'node_modules'); const nodeModulesPath = resolve(repoRoot, 'node_modules');
const npmExecutable = process.platform === 'win32' ? 'npm.cmd' : 'npm'; const npmExecutable = process.platform === 'win32' ? 'npm.cmd' : 'npm';
@@ -13,7 +13,7 @@ const npmExecutable = process.platform === 'win32' ? 'npm.cmd' : 'npm';
/** @param {string} message */ /** @param {string} message */
function log(message) { function log(message) {
console.info(`[setup-full] ${message}`); console.info(`[setup-fast] ${message}`);
} }
/** @param {string[]} args */ /** @param {string[]} args */
+3 -12
View File
@@ -12,7 +12,7 @@ let argv = process.argv.slice(2);
let suiteFilter = undefined; let suiteFilter = undefined;
while (argv.some(e => e.startsWith('--suite='))) { while (argv.some(e => e.startsWith('--suite='))) {
const i = argv.findIndex(e => e.startsWith('--suite=')); const i = argv.findIndex(e => e.startsWith('--suite='));
const match = argv[i].match(/--suite=(?<suitename>.+)/); const match = argv[i].match(/--suite=(?<suitename>.+)/)
suiteFilter = match?.groups?.suitename ?? undefined; suiteFilter = match?.groups?.suitename ?? undefined;
argv.splice(i, 1); argv.splice(i, 1);
} }
@@ -42,18 +42,12 @@ if (suiteFilter) {
configs = configs.filter(e => e.name === suiteFilter); configs = configs.filter(e => e.name === suiteFilter);
} }
if (suiteFilter && configs.length === 0) {
console.error(`Unknown suite: ${suiteFilter}`);
process.exit(1);
}
function npmBinScript(script) { function npmBinScript(script) {
return path.resolve(__dirname, `../node_modules/.bin/` + (process.platform === 'win32' ? return path.resolve(__dirname, `../node_modules/.bin/` + (process.platform === 'win32' ?
`${script}.cmd` : script)); `${script}.cmd` : script));
} }
async function run() { async function run() {
let exitCode = 0;
for (const config of configs) { for (const config of configs) {
const command = npmBinScript('playwright'); const command = npmBinScript('playwright');
const args = ['test', '-c', config.path, ...argv]; const args = ['test', '-c', config.path, ...argv];
@@ -68,13 +62,10 @@ async function run() {
if (run.error) { if (run.error) {
console.error(run.error); console.error(run.error);
process.exit(run.status ?? 1); process.exit(run.status ?? -1);
} }
if (run.status) { process.exit(run.status);
exitCode = run.status;
} }
} }
process.exit(exitCode);
}
run(); run();
+1 -1
View File
@@ -177,7 +177,7 @@ const ENC = {
} }
function printMenu() { function printMenu() {
console.log('\x1b[2J\x1b[HTest mouse reports [Ctrl-C to exit]'); console.log('\x1b[2J\x1b\[HTest mouse reports [Ctrl-C to exit]');
console.log(); console.log();
console.log(' Selected protocol [Ctrl-A to switch]'); console.log(' Selected protocol [Ctrl-A to switch]');
const protocols = Object.keys(PROTOCOLS); const protocols = Object.keys(PROTOCOLS);
+5 -3
View File
@@ -8,7 +8,7 @@ const path = require('path');
const COVERAGE_LINES_THRESHOLD = 40; const COVERAGE_LINES_THRESHOLD = 40;
// Add `out-esbuild` to NODE_PATH so addon path-mapped imports (common/*, browser/*) resolve. // Add `out` to the NODE_PATH so absolute paths can be resolved.
const env = { ...process.env }; const env = { ...process.env };
env.NODE_PATH = path.resolve(__dirname, '../out-esbuild'); env.NODE_PATH = path.resolve(__dirname, '../out-esbuild');
@@ -21,7 +21,7 @@ if (process.argv.length > 2) {
flagArgs = args.filter(e => e.startsWith('--')); flagArgs = args.filter(e => e.startsWith('--'));
// ability to inject particular test files via // ability to inject particular test files via
// npm run test [testFileA testFileB ...] // npm run test [testFileA testFileB ...]
const files = args.filter(e => !e.startsWith('--')); files = args.filter(e => !e.startsWith('--'));
if (files.length) { if (files.length) {
testFiles = files; testFiles = files;
} }
@@ -39,6 +39,8 @@ if (checkCoverage) {
...testFiles, ...testFiles,
...flagArgs ...flagArgs
]; ];
console.info('executable', executable);
console.info('args', args);
const run = cp.spawnSync( const run = cp.spawnSync(
executable, executable,
args, args,
@@ -49,7 +51,7 @@ if (checkCoverage) {
stdio: 'inherit' stdio: 'inherit'
} }
); );
process.exit(run.status ?? -1); process.exit(run.status);
} }
const run = cp.spawnSync( const run = cp.spawnSync(
@@ -7,7 +7,7 @@ import { BaseWindow } from './baseWindow';
import type { IControlWindow } from '../controlBar'; import type { IControlWindow } from '../controlBar';
import type { IBufferCell } from '@xterm/xterm'; import type { IBufferCell } from '@xterm/xterm';
// Underline style values from common/primitives/buffer/Constants.ts // Underline style values from common/buffer/Constants.ts
const enum UnderlineStyle { const enum UnderlineStyle {
NONE = 0, NONE = 0,
SINGLE = 1, SINGLE = 1,
+1 -1
View File
@@ -43,7 +43,7 @@ export class GpuWindow extends BaseWindow implements IControlWindow {
} }
private _styleAtlasPage(canvas: HTMLCanvasElement): void { private _styleAtlasPage(canvas: HTMLCanvasElement): void {
// oxlint-disable-next-line eslint-js/no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
const dpr = window.devicePixelRatio; const dpr = window.devicePixelRatio;
canvas.style.width = `${canvas.width / dpr}px`; canvas.style.width = `${canvas.width / dpr}px`;
canvas.style.height = `${canvas.height / dpr}px`; canvas.style.height = `${canvas.height / dpr}px`;
+1 -55
View File
@@ -11,33 +11,8 @@ export class WebglWindow extends BaseWindow implements IControlWindow {
public readonly label = 'webgl'; public readonly label = 'webgl';
private _textureAtlasContainer!: HTMLElement; private _textureAtlasContainer!: HTMLElement;
private _stressRunning = false;
private _stressScrolling: HTMLInputElement | undefined;
public build(container: HTMLElement): void { public build(container: HTMLElement): void {
const stressDiv = document.createElement('div');
const stressStart = document.createElement('button');
stressStart.id = 'stress-start';
stressStart.textContent = 'atlas stress start';
stressStart.addEventListener('click', () => this._stress());
stressDiv.appendChild(stressStart);
const stressStop = document.createElement('button');
stressStop.id = 'stress-stop';
stressStop.textContent = 'atlas stress stop';
stressStop.addEventListener('click', () => this._stressRunning = false);
stressDiv.appendChild(stressStop);
this._stressScrolling = document.createElement('input');
this._stressScrolling.type = 'checkbox';
this._stressScrolling.id = 'stress-scrolling';
stressDiv.appendChild(this._stressScrolling);
const stressScrollingLabel = document.createElement('label');
stressScrollingLabel.htmlFor = 'stress-scrolling';
stressScrollingLabel.textContent = 'stress scrolling';
stressDiv.appendChild(stressScrollingLabel);
container.appendChild(stressDiv);
const zoomCheckbox = document.createElement('input'); const zoomCheckbox = document.createElement('input');
zoomCheckbox.type = 'checkbox'; zoomCheckbox.type = 'checkbox';
zoomCheckbox.id = 'texture-atlas-zoom'; zoomCheckbox.id = 'texture-atlas-zoom';
@@ -68,38 +43,9 @@ export class WebglWindow extends BaseWindow implements IControlWindow {
} }
private _styleAtlasPage(canvas: HTMLCanvasElement): void { private _styleAtlasPage(canvas: HTMLCanvasElement): void {
// oxlint-disable-next-line eslint-js/no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
const dpr = window.devicePixelRatio; const dpr = window.devicePixelRatio;
canvas.style.width = `${canvas.width / dpr}px`; canvas.style.width = `${canvas.width / dpr}px`;
canvas.style.height = `${canvas.height / dpr}px`; canvas.style.height = `${canvas.height / dpr}px`;
} }
private async _stress(): Promise<void> {
const TEXT = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
if (this._stressRunning) {
return;
}
this._stressRunning = true;
const scrolling = !!(this._stressScrolling?.checked);
for (let r = 0; r < 256; r += 2) {
for (let g = 0; g < 256; g += 2) {
let s: string[] = [];
for (let b = 0; b < 256; b += 2) {
if (!this._stressRunning) {
return;
}
const rbg = `RGB: ${[r, g, b]}`;
s.push(`\r\x1b[38;2;${r};${g};${b}m${rbg.padEnd(18, ' ')}${TEXT}`);
if (s.length >= 16) {
if (scrolling) {
await new Promise<void>(r => this._terminal.write(s.join('\r\n') + '\r\n', r));
} else {
await new Promise<void>(r => this._terminal.write('\x1b[H' + s.join('\r\n'), r));
}
s = [];
}
}
}
}
}
} }
+14 -42
View File
@@ -2,54 +2,26 @@
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "commonjs",
"target": "es2021", "target": "es2021",
"types": [ "types": ["node"],
"node"
],
"outDir": "../out-demo/client", "outDir": "../out-demo/client",
"rootDir": ".", "rootDir": ".",
"sourceMap": true, "sourceMap": true,
"strict": true, "strict": true,
"skipLibCheck": true, "skipLibCheck": true,
"paths": { "paths": {
"@xterm/addon-attach": [ "@xterm/addon-attach": ["../../addons/addon-attach"],
"../../addons/addon-attach" "@xterm/addon-clipboard": ["../../addons/addon-clipboard"],
], "@xterm/addon-fit": ["../../addons/addon-fit"],
"@xterm/addon-clipboard": [ "@xterm/addon-image": ["../../addons/addon-image"],
"../../addons/addon-clipboard" "@xterm/addon-progress": ["../../addons/addon-progress"],
], "@xterm/addon-search": ["../../addons/addon-search"],
"@xterm/addon-fit": [ "@xterm/addon-serialize": ["../../addons/addon-serialize"],
"../../addons/addon-fit" "@xterm/addon-web-fonts": ["../../addons/addon-web-fonts"],
], "@xterm/addon-web-links": ["../../addons/addon-web-links"],
"@xterm/addon-image": [ "@xterm/addon-webgl": ["../../addons/addon-webgl"],
"../../addons/addon-image" "@xterm/addon-unicode11": ["../../addons/addon-unicode11"],
], "@xterm/addon-unicode-graphemes": ["../../addons/addon-unicode-graphemes"],
"@xterm/addon-progress": [ "@xterm/addon-ligatures": ["../../addons/addon-ligatures"],
"../../addons/addon-progress"
],
"@xterm/addon-search": [
"../../addons/addon-search"
],
"@xterm/addon-serialize": [
"../../addons/addon-serialize"
],
"@xterm/addon-web-fonts": [
"../../addons/addon-web-fonts"
],
"@xterm/addon-web-links": [
"../../addons/addon-web-links"
],
"@xterm/addon-webgl": [
"../../addons/addon-webgl"
],
"@xterm/addon-unicode11": [
"../../addons/addon-unicode11"
],
"@xterm/addon-unicode-graphemes": [
"../../addons/addon-unicode-graphemes"
],
"@xterm/addon-ligatures": [
"../../addons/addon-ligatures"
],
"*": ["./*"] "*": ["./*"]
} }
}, },
-71
View File
@@ -1,71 +0,0 @@
// @ts-check
import tseslint from 'typescript-eslint';
const ignores = [
'addons/*/src/third-party/*.ts',
'**/out/*',
'**/out-test/*',
'**/out-esbuild/*',
'**/out-esbuild-test/*',
'**/inwasm-sdks/*',
'**/node_modules',
'**/*.js',
'**/*.mjs'
];
const namingConventionMain = [
'warn',
{ selector: 'default', format: ['camelCase'], filter: { regex: '^[a-z]', match: true } },
{ selector: 'variable', format: ['camelCase', 'UPPER_CASE'] },
{ selector: 'variable', filter: '^I.+Service$', format: ['PascalCase'], prefix: ['I'] },
{ selector: 'memberLike', modifiers: ['private'], format: ['camelCase'], leadingUnderscore: 'require' },
{ selector: 'memberLike', modifiers: ['protected'], format: ['camelCase'], leadingUnderscore: 'require' },
{ selector: 'enumMember', format: ['UPPER_CASE'] },
{ selector: 'property', modifiers: ['public'], format: ['camelCase', 'UPPER_CASE'], filter: { regex: '^[a-z]', match: true } },
{ selector: 'method', modifiers: ['public'], format: ['camelCase', 'UPPER_CASE'], custom: { regex: '^on[A-Z].+', match: false } },
{ selector: 'method', modifiers: ['private'], format: ['camelCase'], leadingUnderscore: 'require', custom: { regex: '^on[A-Z].+', match: false } },
{ selector: 'method', modifiers: ['protected'], format: ['camelCase'], leadingUnderscore: 'require', custom: { regex: '^on[A-Z].+', match: false } },
{ selector: 'typeLike', format: ['PascalCase'] },
{ selector: 'interface', format: ['PascalCase'], prefix: ['I'] }
];
const namingConventionTypings = [
'warn',
{ selector: 'typeLike', format: ['PascalCase'] },
{ selector: 'interface', format: ['PascalCase'], prefix: ['I'] }
];
export default tseslint.config(
{
ignores
},
{
files: ['**/*.ts'],
ignores: ['typings/**/*.d.ts', 'addons/**/typings/**'],
plugins: {
'@typescript-eslint': tseslint.plugin
},
languageOptions: {
parser: tseslint.parser,
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname
}
},
rules: {
'@typescript-eslint/naming-convention': namingConventionMain
}
},
{
files: ['typings/**/*.d.ts'],
plugins: {
'@typescript-eslint': tseslint.plugin
},
languageOptions: {
parser: tseslint.parser
},
rules: {
'@typescript-eslint/naming-convention': namingConventionTypings
}
}
);
+36 -516
View File
@@ -32,7 +32,6 @@
"esbuild": "^0.28.0", "esbuild": "^0.28.0",
"eslint": "^10.3.0", "eslint": "^10.3.0",
"eslint-plugin-jsdoc": "^62.9.0", "eslint-plugin-jsdoc": "^62.9.0",
"eslint-plugin-oxlint": "^1.67.0",
"express": "^5.2.1", "express": "^5.2.1",
"express-ws": "^5.0.2", "express-ws": "^5.0.2",
"jsdom": "^29.1.1", "jsdom": "^29.1.1",
@@ -40,9 +39,6 @@
"mustache": "^4.2.0", "mustache": "^4.2.0",
"node-pty": "^1.2.0-beta.9", "node-pty": "^1.2.0-beta.9",
"nyc": "^18.0.0", "nyc": "^18.0.0",
"oxlint": "^1.67.0",
"oxlint-plugin-eslint": "^1.67.0",
"oxlint-tsgolint": "^0.23.0",
"source-map-loader": "^5.0.0", "source-map-loader": "^5.0.0",
"source-map-support": "^0.5.20", "source-map-support": "^0.5.20",
"ts-loader": "^9.5.7", "ts-loader": "^9.5.7",
@@ -1512,413 +1508,6 @@
"pako": "^2.0.4" "pako": "^2.0.4"
} }
}, },
"node_modules/@oxlint-tsgolint/darwin-arm64": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/@oxlint-tsgolint/darwin-arm64/-/darwin-arm64-0.23.0.tgz",
"integrity": "sha512-gOs9PVr2wEg4ox9z0aJo+RKhhImW86YL5N6yav8BK/rgPsIrwN/igSZ+pbRr723NFvUNKde9fgMhRA6JrXAOZw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@oxlint-tsgolint/darwin-x64": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/@oxlint-tsgolint/darwin-x64/-/darwin-x64-0.23.0.tgz",
"integrity": "sha512-kjJ8B+7n4tB9VJdxS5A9GdJt6/bYpzbu4lXp2uO1S3sRmCB5gDEABlGoiePNApRWaW+xqL4b4xgiE727jSLhuA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@oxlint-tsgolint/linux-arm64": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/@oxlint-tsgolint/linux-arm64/-/linux-arm64-0.23.0.tgz",
"integrity": "sha512-6dCZuKNu135seMXilkRk9SpCx6i1XgmiipYGalLij5WVRX6ZYS8c4xI7preN/zv9fCXhsQclTIMDu2Y/cytTjw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@oxlint-tsgolint/linux-x64": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/@oxlint-tsgolint/linux-x64/-/linux-x64-0.23.0.tgz",
"integrity": "sha512-3bdilnyA7kmSTjK27rvjIjSxL5SIg3wt7vwNiRkouWB83ytssyKnuGvxSYJxgMEmFpSutzaBzcCUM2jDtPGcgA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@oxlint-tsgolint/win32-arm64": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/@oxlint-tsgolint/win32-arm64/-/win32-arm64-0.23.0.tgz",
"integrity": "sha512-j+OEp44SVYiQ+ZD+uttsX7u6L9SvmbbQ77SO1pSFCcJlsVMeCk8qZsjhKfGKuT/jIA+ipOJMVs/+pqUfObBWNw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@oxlint-tsgolint/win32-x64": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/@oxlint-tsgolint/win32-x64/-/win32-x64-0.23.0.tgz",
"integrity": "sha512-5MyjFuqf+g8OUPJBSGWHJtmoWnzFJYyOg4To9WMQshZYEWig/vtu7JtJ03VWnzHv9LJkAUeApY0gVCOywFR/iQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@oxlint/binding-android-arm-eabi": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.67.0.tgz",
"integrity": "sha512-VrSi571rDv1N8HaEDM+DEX8nmT0y9jJo8tzzW13vsOWTx59xQczCIJx68n2zWOXRT5YKZsOZXp4qkHN/10x4mw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-android-arm64": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.67.0.tgz",
"integrity": "sha512-l6+NdYxMoRohix5r5bbigW16LPicceCwGcQ6LKKuE1kUdjgFfQolJjrJsQYPFetIs78Gxj/G/f5TEGoTCwj9nQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-darwin-arm64": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.67.0.tgz",
"integrity": "sha512-jOzXxS1AxFxhImLIRbtGIMrEwaXcgMw3gR57WB1cRk8ai+vpr6726kxXqVvlNsrXtJ/FrmOm8RxlC0m8SW24Qg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-darwin-x64": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.67.0.tgz",
"integrity": "sha512-3DFAVY94OqjIZHXIPz37yGRSWwOFTAqChQ64/M69GYLawzP0KiwdhDNfqdKKYT0bTR/DNxmMnQsj3ns+8+X/Lg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-freebsd-x64": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.67.0.tgz",
"integrity": "sha512-e4dDKZuLu8TR9DEBssWSDahlPgZBwojTTHZUvnjBRJfJJbpxYCjfjKfi0Z1+CSLMiJBwI2yCDtRM1XJQaARjmg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-arm-gnueabihf": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.67.0.tgz",
"integrity": "sha512-BKytFdcQzbITV3xlnzDUDTEDtbUMCCiC4EaNTDZ4FyT8gdNvBC4gfiLucXp/sQl0XU3p7syTlorUWVVVBZab2g==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-arm-musleabihf": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.67.0.tgz",
"integrity": "sha512-XYAv0esBDX7BpTzRDjVX2Vdj+zndd8ll2dFQiaeQ6zTZr7A8GRDTN7fH3FP3jU+O0vCDx85oH/EtG7BzPgAXuw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-arm64-gnu": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.67.0.tgz",
"integrity": "sha512-zizRMjA0i6u/2B0evgda04iycu+MoNuf1pBy6Eh+1CjC5wMEG7qN5zdDKTCvFc0KSYSDM9QTG3gjZHirgtQuKg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-arm64-musl": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.67.0.tgz",
"integrity": "sha512-zB/Tf6sUjmmvvbva9Gj3JTJ8rJ9t4I8/U0o6vSRtd0DRIsIuyegBwJAzhSUFQHdMijIRJkW0exs/yBhpw2S20w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-ppc64-gnu": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.67.0.tgz",
"integrity": "sha512-kgU40Gt74CK0TCsF51KZymkIwN9U0BajKsMijB52zPqOeZU9NAHkA/NSQkZDHEaCakx42DxhXkODiAqf2b4Gug==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-riscv64-gnu": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.67.0.tgz",
"integrity": "sha512-tOYhkk/iaG9aD3FvGpBFd1Lrw0x0RaVoJBxjUkfNzS50rC5NS5BteNCwgr8A2zCdADrIIoze6D7u6U5Ic++/iQ==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-riscv64-musl": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.67.0.tgz",
"integrity": "sha512-sEtywrPb+0b+tHYl1SDCrw903fiC4eyKoNqzP3v+f2JT3Xcv4NEYG+P8rj+eEnX7IWhqV/xj8/JmcmVj21CXaA==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-s390x-gnu": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.67.0.tgz",
"integrity": "sha512-BvR8Moa0zCLxroOx4vZaZN9nUfwAUpSTwjZdxZyKy4bv3PrzrXrxKR/ZQ0L9wNSvlPhnMJeZfa3q5w6ZCTuN6Q==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-x64-gnu": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.67.0.tgz",
"integrity": "sha512-mm2cxM6fksOpq6l0uFws8BUGKAR4dNa/cZCn37Npq7PFbhD5HDJqWfnoIvTaeRKMy5XdS2tO0MA0qbHDrnXAAA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-linux-x64-musl": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.67.0.tgz",
"integrity": "sha512-WmbMuLapKyDlobMkXAaAL0Y+Uczh4LETfIfQsUpbId4Ip8Ai82/jqeYTOoUCkuuhBFapgqP253+d83tLKOksJg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-openharmony-arm64": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.67.0.tgz",
"integrity": "sha512-9g/PqxYJelzzTAOR5Y+RiRqdeydhEuXv2KxNeFcAKQ7UsvnWSY1OP4MsuPMbTO2Pf70tz7mFhl1j13H3fyh+8g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-win32-arm64-msvc": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.67.0.tgz",
"integrity": "sha512-2VhwE6Gatb0vJGnN0TBuQMbKCOiZlSQ/zJvVWYLK4a9d4iDiJOen/yVQkGpmsJ90MuH66fzi0kEKI0jRQMDxGA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-win32-ia32-msvc": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.67.0.tgz",
"integrity": "sha512-EQ3VExXfeM1InbE5+JjufhZZTWy+kHUwgt3yZR7gQ47Je/mE0WspQPan0OJznh493L5anM210YNJtH1PXjTSFg==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxlint/binding-win32-x64-msvc": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.67.0.tgz",
"integrity": "sha512-bw24y+/1MHS4QDkons3YyHkPT9uCMoLHHgQhb+mb8NOjTYwub1CZ+K9Ngr8aO5DMrDrkqHwTzlTwFP2vS8Y/ZQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@pkgjs/parseargs": { "node_modules/@pkgjs/parseargs": {
"version": "0.11.0", "version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -4063,19 +3652,6 @@
"url": "https://opencollective.com/eslint" "url": "https://opencollective.com/eslint"
} }
}, },
"node_modules/eslint-plugin-oxlint": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-oxlint/-/eslint-plugin-oxlint-1.67.0.tgz",
"integrity": "sha512-tS/RGy8x2+rIUp7gV8ICgEeSAgFH6Qg7phzCbUblW/dPUsqXkcedrzO0nOaKhce4fvUVDR9uTmWlLkCNSDKcsw==",
"dev": true,
"license": "MIT",
"dependencies": {
"jsonc-parser": "^3.3.1"
},
"peerDependencies": {
"oxlint": "~1.67.0"
}
},
"node_modules/eslint-scope": { "node_modules/eslint-scope": {
"version": "9.1.2", "version": "9.1.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
@@ -5469,13 +5045,6 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/jsonc-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
"integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
"dev": true,
"license": "MIT"
},
"node_modules/keyv": { "node_modules/keyv": {
"version": "4.5.4", "version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -6228,86 +5797,6 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/oxlint": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.67.0.tgz",
"integrity": "sha512-blwwaHPdoH8piQ5/z0KHeoHFR7FZgl12WluKJfu4qFLPkZl6mK04PkLE45Fw1NxfBRSlh40Gu7MkxHUw++ociQ==",
"dev": true,
"license": "MIT",
"bin": {
"oxlint": "bin/oxlint"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/sponsors/Boshen"
},
"optionalDependencies": {
"@oxlint/binding-android-arm-eabi": "1.67.0",
"@oxlint/binding-android-arm64": "1.67.0",
"@oxlint/binding-darwin-arm64": "1.67.0",
"@oxlint/binding-darwin-x64": "1.67.0",
"@oxlint/binding-freebsd-x64": "1.67.0",
"@oxlint/binding-linux-arm-gnueabihf": "1.67.0",
"@oxlint/binding-linux-arm-musleabihf": "1.67.0",
"@oxlint/binding-linux-arm64-gnu": "1.67.0",
"@oxlint/binding-linux-arm64-musl": "1.67.0",
"@oxlint/binding-linux-ppc64-gnu": "1.67.0",
"@oxlint/binding-linux-riscv64-gnu": "1.67.0",
"@oxlint/binding-linux-riscv64-musl": "1.67.0",
"@oxlint/binding-linux-s390x-gnu": "1.67.0",
"@oxlint/binding-linux-x64-gnu": "1.67.0",
"@oxlint/binding-linux-x64-musl": "1.67.0",
"@oxlint/binding-openharmony-arm64": "1.67.0",
"@oxlint/binding-win32-arm64-msvc": "1.67.0",
"@oxlint/binding-win32-ia32-msvc": "1.67.0",
"@oxlint/binding-win32-x64-msvc": "1.67.0"
},
"peerDependencies": {
"oxlint-tsgolint": ">=0.22.1",
"vite-plus": "*"
},
"peerDependenciesMeta": {
"oxlint-tsgolint": {
"optional": true
},
"vite-plus": {
"optional": true
}
}
},
"node_modules/oxlint-plugin-eslint": {
"version": "1.67.0",
"resolved": "https://registry.npmjs.org/oxlint-plugin-eslint/-/oxlint-plugin-eslint-1.67.0.tgz",
"integrity": "sha512-MfpO9t/l/1qWVKym4sTkz0bhErAkUn/cOOQsFegI5ngQkR1cZEp6G23FKIhGcALoYytPBRmu47yjiEa6XWFBRw==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/sponsors/Boshen"
}
},
"node_modules/oxlint-tsgolint": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/oxlint-tsgolint/-/oxlint-tsgolint-0.23.0.tgz",
"integrity": "sha512-3mBv3CoPbh8dFbzfDGIWa2ytZjn2v+3EX4aKRXjIhsoGFzG8GCjfRirz3rwZf1wYbZzsNLTSgpw8VjQuWdp/jA==",
"dev": true,
"license": "MIT",
"bin": {
"tsgolint": "bin/tsgolint.js"
},
"optionalDependencies": {
"@oxlint-tsgolint/darwin-arm64": "0.23.0",
"@oxlint-tsgolint/darwin-x64": "0.23.0",
"@oxlint-tsgolint/linux-arm64": "0.23.0",
"@oxlint-tsgolint/linux-x64": "0.23.0",
"@oxlint-tsgolint/win32-arm64": "0.23.0",
"@oxlint-tsgolint/win32-x64": "0.23.0"
}
},
"node_modules/p-limit": { "node_modules/p-limit": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -6689,6 +6178,16 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "^5.1.0"
}
},
"node_modules/range-parser": { "node_modules/range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -6922,6 +6421,27 @@
"tslib": "^2.1.0" "tslib": "^2.1.0"
} }
}, },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safer-buffer": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -7047,13 +6567,13 @@
} }
}, },
"node_modules/serialize-javascript": { "node_modules/serialize-javascript": {
"version": "7.0.5", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
"integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"dev": true, "dev": true,
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"engines": { "dependencies": {
"node": ">=20.0.0" "randombytes": "^2.1.0"
} }
}, },
"node_modules/serve-static": { "node_modules/serve-static": {
+3 -10
View File
@@ -11,9 +11,6 @@
"workspaces": [ "workspaces": [
"addons/*" "addons/*"
], ],
"overrides": {
"serialize-javascript": "^7.0.3"
},
"keywords": [ "keywords": [
"cli", "cli",
"command-line", "command-line",
@@ -51,11 +48,11 @@
"esbuild-demo-server-watch": "node bin/esbuild.mjs --demo-server --watch", "esbuild-demo-server-watch": "node bin/esbuild.mjs --demo-server --watch",
"test": "npm run test-unit", "test": "npm run test-unit",
"posttest": "npm run lint", "posttest": "npm run lint",
"lint": "oxlint --type-aware --max-warnings 0 src addons demo test && eslint --config eslint.config.naming.mjs --max-warnings 0 src addons demo test", "lint": "eslint --max-warnings 0 src/ addons/ demo/ test/",
"lint-changes": "node ./bin/lint_changes.js", "lint-changes": "node ./bin/lint_changes.js",
"lint-changes-fix": "node ./bin/lint_changes.js --fix", "lint-changes-fix": "node ./bin/lint_changes.js --fix",
"lint-fix": "oxlint --type-aware --fix --max-warnings 0 src addons demo test", "lint-fix": "eslint --fix src/ addons/ demo/ test/",
"lint-api": "oxlint --max-warnings 0 -c .oxlintrc.typings.json typings && eslint --config eslint.config.naming.mjs --max-warnings 0 typings", "lint-api": "eslint --config eslint.config.typings.mjs --max-warnings 0 typings/",
"test-unit": "node ./bin/test_unit.js", "test-unit": "node ./bin/test_unit.js",
"test-unit-slow-tests": "npm run test-unit | grep \"ms)\"", "test-unit-slow-tests": "npm run test-unit | grep \"ms)\"",
"test-unit-coverage": "node ./bin/test_unit.js --coverage", "test-unit-coverage": "node ./bin/test_unit.js --coverage",
@@ -98,7 +95,6 @@
"esbuild": "^0.28.0", "esbuild": "^0.28.0",
"eslint": "^10.3.0", "eslint": "^10.3.0",
"eslint-plugin-jsdoc": "^62.9.0", "eslint-plugin-jsdoc": "^62.9.0",
"eslint-plugin-oxlint": "^1.67.0",
"express": "^5.2.1", "express": "^5.2.1",
"express-ws": "^5.0.2", "express-ws": "^5.0.2",
"jsdom": "^29.1.1", "jsdom": "^29.1.1",
@@ -106,9 +102,6 @@
"mustache": "^4.2.0", "mustache": "^4.2.0",
"node-pty": "^1.2.0-beta.9", "node-pty": "^1.2.0-beta.9",
"nyc": "^18.0.0", "nyc": "^18.0.0",
"oxlint": "^1.67.0",
"oxlint-plugin-eslint": "^1.67.0",
"oxlint-tsgolint": "^0.23.0",
"source-map-loader": "^5.0.0", "source-map-loader": "^5.0.0",
"source-map-support": "^0.5.20", "source-map-support": "^0.5.20",
"ts-loader": "^9.5.7", "ts-loader": "^9.5.7",
+8 -8
View File
@@ -3,14 +3,14 @@
* @license MIT * @license MIT
*/ */
import * as Strings from './LocalizableStrings'; import * as Strings from 'browser/LocalizableStrings';
import { ITerminal, IRenderDebouncer } from './Types'; import { ITerminal, IRenderDebouncer } from 'browser/Types';
import { TimeBasedDebouncer } from './TimeBasedDebouncer'; import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer';
import { Disposable, toDisposable } from '../common/primitives/Lifecycle'; import { Disposable, toDisposable } from 'common/base/Lifecycle';
import { ICoreBrowserService, IRenderService } from './services/Services'; import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
import { IBuffer } from '../common/primitives/buffer/Types'; import { IBuffer } from 'common/buffer/Types';
import { IInstantiationService } from '../common/services/Services'; import { IInstantiationService } from 'common/services/Services';
import { addDisposableListener } from './Dom'; import { addDisposableListener } from 'browser/Dom';
const enum Constants { const enum Constants {
MAX_ROWS_TO_READ = 20 MAX_ROWS_TO_READ = 20
+1 -1
View File
@@ -4,7 +4,7 @@
*/ */
import { assert } from 'chai'; import { assert } from 'chai';
import * as Clipboard from './Clipboard'; import * as Clipboard from 'browser/Clipboard';
describe('evaluatePastedTextProcessing', () => { describe('evaluatePastedTextProcessing', () => {
it('should replace carriage return and/or line feed with carriage return', () => { it('should replace carriage return and/or line feed with carriage return', () => {
+2 -2
View File
@@ -3,8 +3,8 @@
* @license MIT * @license MIT
*/ */
import { ISelectionService } from './services/Services'; import { ISelectionService } from 'browser/services/Services';
import { ICoreService, IOptionsService } from '../common/services/Services'; import { ICoreService, IOptionsService } from 'common/services/Services';
/** /**
* Prepares text to be pasted into the terminal by normalizing the line endings * Prepares text to be pasted into the terminal by normalizing the line endings
+1 -1
View File
@@ -4,7 +4,7 @@
*/ */
import { assert } from 'chai'; import { assert } from 'chai';
import { ColorContrastCache } from './ColorContrastCache'; import { ColorContrastCache } from 'browser/ColorContrastCache';
describe('ColorContrastCache', () => { describe('ColorContrastCache', () => {
let cache: ColorContrastCache; let cache: ColorContrastCache;
+3 -3
View File
@@ -3,9 +3,9 @@
* @license MIT * @license MIT
*/ */
import { IColorContrastCache } from './Types'; import { IColorContrastCache } from 'browser/Types';
import { IColor } from '../common/primitives/Types'; import type { IColor } from 'common/base/ColorTypes';
import { TwoKeyMap } from '../common/primitives/MultiKeyMap'; import { TwoKeyMap } from 'common/base/MultiKeyMap';
export class ColorContrastCache implements IColorContrastCache { export class ColorContrastCache implements IColorContrastCache {
private _color: TwoKeyMap</* bg */number, /* fg */number, IColor | null> = new TwoKeyMap(); private _color: TwoKeyMap</* bg */number, /* fg */number, IColor | null> = new TwoKeyMap();
+43 -38
View File
@@ -22,44 +22,45 @@
*/ */
import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker, IRenderDimensions as IRenderDimensionsApi } from '@xterm/xterm'; import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker, IRenderDimensions as IRenderDimensionsApi } from '@xterm/xterm';
import { copyHandler, handlePasteEvent, moveTextAreaUnderMouseCursor, paste, rightClickHandler } from './Clipboard'; import { copyHandler, handlePasteEvent, moveTextAreaUnderMouseCursor, paste, rightClickHandler } from 'browser/Clipboard';
import * as Strings from './LocalizableStrings'; import * as Strings from 'browser/LocalizableStrings';
import { OscLinkProvider } from './OscLinkProvider'; import { OscLinkProvider } from 'browser/OscLinkProvider';
import { CharacterJoinerHandler, CustomKeyEventHandler, CustomWheelEventHandler, IBrowser, IBufferRange, ICompositionHelper, ILinkifier2, ITerminal } from './Types'; import { CharacterJoinerHandler, CustomKeyEventHandler, CustomWheelEventHandler, IBrowser, IBufferRange, ICompositionHelper, ILinkifier2, ITerminal } from 'browser/Types';
import { Viewport } from './Viewport'; import { Viewport } from 'browser/Viewport';
import { BufferDecorationRenderer } from './decorations/BufferDecorationRenderer'; import { BufferDecorationRenderer } from 'browser/decorations/BufferDecorationRenderer';
import { OverviewRulerRenderer } from './decorations/OverviewRulerRenderer'; import { OverviewRulerRenderer } from 'browser/decorations/OverviewRulerRenderer';
import { CompositionHelper } from './input/CompositionHelper'; import { CompositionHelper } from 'browser/input/CompositionHelper';
import { DomRenderer } from './renderer/dom/DomRenderer'; import { DomRenderer } from 'browser/renderer/dom/DomRenderer';
import { IRenderer } from './renderer/shared/Types'; import { IRenderer } from 'browser/renderer/shared/Types';
import { CharSizeService } from './services/CharSizeService'; import { CharSizeService } from 'browser/services/CharSizeService';
import { CharacterJoinerService } from './services/CharacterJoinerService'; import { CharacterJoinerService } from 'browser/services/CharacterJoinerService';
import { CoreBrowserService } from './services/CoreBrowserService'; import { CoreBrowserService } from 'browser/services/CoreBrowserService';
import { LinkProviderService } from './services/LinkProviderService'; import { LinkProviderService } from 'browser/services/LinkProviderService';
import { MouseCoordsService } from './services/MouseCoordsService'; import { MouseCoordsService } from 'browser/services/MouseCoordsService';
import { MouseEventCssClasses, MouseService } from './services/MouseService'; import { MouseEventCssClasses, MouseService } from 'browser/services/MouseService';
import { RenderService } from './services/RenderService'; import { RenderService } from 'browser/services/RenderService';
import { SelectionService } from './services/SelectionService'; import { SelectionService } from 'browser/services/SelectionService';
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IKeyboardService, ILinkProviderService, IMouseCoordsService, IMouseService, IRenderService, ISelectionService, IThemeService } from './services/Services'; import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IKeyboardService, ILinkProviderService, IMouseCoordsService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
import { ThemeService } from './services/ThemeService'; import { ThemeService } from 'browser/services/ThemeService';
import { KeyboardService } from './services/KeyboardService'; import { KeyboardService } from 'browser/services/KeyboardService';
import { channels, color, rgb } from '../common/primitives/Color'; import { channels, color, rgb } from 'common/base/Color';
import { CoreTerminal } from '../common/CoreTerminal'; import { CoreTerminal } from 'common/terminal/CoreTerminal';
import * as Browser from '../common/primitives/Platform'; import * as Browser from 'common/base/Platform';
import { ColorRequestType, IColorEvent, KeyboardResultType, SpecialColorIndex } from '../common/primitives/Types'; import type { ITerminalOptions } from 'common/services/Services';
import { ITerminalOptions } from '../common/Types'; import { ColorRequestType, IColorEvent, SpecialColorIndex } from 'common/terminal/InputHandlerTypes';
import { DEFAULT_ATTR_DATA } from '../common/primitives/buffer/BufferLine'; import { KeyboardResultType } from 'common/input/KeyboardTypes';
import { IBuffer } from '../common/primitives/buffer/Types'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { C0, C1ESCAPED } from '../common/primitives/data/EscapeSequences'; import { IBuffer } from 'common/buffer/Types';
import { toRgbString } from '../common/primitives/input/XParseColor'; import { C0, C1ESCAPED } from 'common/base/EscapeSequences';
import { DecorationService } from '../common/services/DecorationService'; import { toRgbString } from 'common/input/XParseColor';
import { IDecorationService } from '../common/services/Services'; import { DecorationService } from 'common/services/DecorationService';
import { WindowsOptionsReportType } from '../common/InputHandler'; import { IDecorationService } from 'common/services/Services';
import { WindowsOptionsReportType } from '../common/terminal/InputHandler';
import { AccessibilityManager } from './AccessibilityManager'; import { AccessibilityManager } from './AccessibilityManager';
import { Linkifier } from './Linkifier'; import { Linkifier } from './Linkifier';
import { Emitter, EventUtils, type IEvent } from '../common/primitives/Event'; import { Emitter, EventUtils, type IEvent } from 'common/base/Event';
import { addDisposableListener } from './Dom'; import { addDisposableListener } from 'browser/Dom';
import { MutableDisposable, toDisposable } from '../common/primitives/Lifecycle'; import { MutableDisposable, toDisposable } from 'common/base/Lifecycle';
export class CoreBrowserTerminal extends CoreTerminal implements ITerminal { export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
public textarea: HTMLTextAreaElement | undefined; public textarea: HTMLTextAreaElement | undefined;
@@ -103,7 +104,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
private _keyDownHandled: boolean = false; private _keyDownHandled: boolean = false;
/** /**
* Records whether a keydown event has occurred since the last keyup event, i.e. whether a key * Records whether a keydown event has occured since the last keyup event, i.e. whether a key
* is currently "pressed". * is currently "pressed".
*/ */
private _keyDownSeen: boolean = false; private _keyDownSeen: boolean = false;
@@ -506,7 +507,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
this.textarea, this.textarea,
parent.ownerDocument.defaultView ?? window, parent.ownerDocument.defaultView ?? window,
// Force unsafe null in node.js environment for tests // Force unsafe null in node.js environment for tests
this._document ?? ((typeof window !== 'undefined') ? window.document : null as any) this._document ?? (typeof window !== 'undefined') ? window.document : null as any
)); ));
this._instantiationService.setService(ICoreBrowserService, this._coreBrowserService); this._instantiationService.setService(ICoreBrowserService, this._coreBrowserService);
@@ -1073,6 +1074,10 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
* Clear the entire buffer, making the prompt line the new first line. * Clear the entire buffer, making the prompt line the new first line.
*/ */
public clear(): void { public clear(): void {
if (this.buffer.ybase === 0 && this.buffer.y === 0) {
// Don't clear if it's already clear
return;
}
this.buffer.clearAllMarkers(); this.buffer.clearAllMarkers();
this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!); this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!);
this.buffer.lines.length = 1; this.buffer.lines.length = 1;
+2 -2
View File
@@ -5,8 +5,8 @@
* Minimal DOM helpers for xterm.js browser code. * Minimal DOM helpers for xterm.js browser code.
*/ */
import { IntervalTimer } from '../common/primitives/Async'; import { IntervalTimer } from 'common/base/Async';
import { IDisposable } from '../common/primitives/Lifecycle'; import { IDisposable } from 'common/base/Lifecycle';
export function getWindow(e: Node | UIEvent | undefined | null): Window { export function getWindow(e: Node | UIEvent | undefined | null): Window {
const candidateNode = e as Node | undefined | null; const candidateNode = e as Node | undefined | null;
+4 -4
View File
@@ -4,11 +4,11 @@
*/ */
import { assert } from 'chai'; import { assert } from 'chai';
import { IBufferService } from '../common/services/Services'; import { IBufferService } from 'common/services/Services';
import { Linkifier } from './Linkifier'; import { Linkifier } from './Linkifier';
import { MockBufferService } from '../common/TestUtils.test'; import { MockBufferService } from 'common/terminal/TestUtils.test';
import { ILink } from './Types'; import { ILink } from 'browser/Types';
import { LinkProviderService } from './services/LinkProviderService'; import { LinkProviderService } from 'browser/services/LinkProviderService';
import jsdom = require('jsdom'); import jsdom = require('jsdom');
class TestLinkifier2 extends Linkifier { class TestLinkifier2 extends Linkifier {
+6 -6
View File
@@ -3,13 +3,13 @@
* @license MIT * @license MIT
*/ */
import { IBufferCellPosition, ILink, ILinkDecorations, ILinkWithState, ILinkifier2, ILinkifierEvent } from './Types'; import { IBufferCellPosition, ILink, ILinkDecorations, ILinkWithState, ILinkifier2, ILinkifierEvent } from 'browser/Types';
import { Disposable, dispose, toDisposable } from '../common/primitives/Lifecycle'; import { Disposable, dispose, toDisposable } from 'common/base/Lifecycle';
import { IDisposable } from '../common/primitives/Types'; import type { IDisposable } from 'common/base/Lifecycle';
import { IBufferService } from '../common/services/Services'; import { IBufferService } from 'common/services/Services';
import { ILinkProviderService, IMouseCoordsService, IRenderService } from './services/Services'; import { ILinkProviderService, IMouseCoordsService, IRenderService } from './services/Services';
import { Emitter } from '../common/primitives/Event'; import { Emitter } from 'common/base/Event';
import { addDisposableListener } from './Dom'; import { addDisposableListener } from 'browser/Dom';
export class Linkifier extends Disposable implements ILinkifier2 { export class Linkifier extends Disposable implements ILinkifier2 {
public get currentLink(): ILinkWithState | undefined { return this._currentLink; } public get currentLink(): ILinkWithState | undefined { return this._currentLink; }
+6 -6
View File
@@ -4,12 +4,12 @@
*/ */
import { assert } from 'chai'; import { assert } from 'chai';
import { OscLinkProvider } from './OscLinkProvider'; import { OscLinkProvider } from 'browser/OscLinkProvider';
import { ILink } from './Types'; import { ILink } from 'browser/Types';
import { createCellData, MockBufferService, MockOptionsService } from '../common/TestUtils.test'; import { createCellData, MockBufferService, MockOptionsService } from 'common/terminal/TestUtils.test';
import { IBufferService, IOscLinkService } from '../common/services/Services'; import { IBufferService, IOscLinkService } from 'common/services/Services';
import { IOscLinkData } from '../common/primitives/Types'; import type { IBufferLine } from 'common/buffer/CellTypes';
import { IBufferLine } from '../common/primitives/buffer/Types'; import { IOscLinkData } from 'common/services/Types';
class TestOscLinkService implements IOscLinkService { class TestOscLinkService implements IOscLinkService {
public serviceBrand: any; public serviceBrand: any;
+5 -5
View File
@@ -3,11 +3,11 @@
* @license MIT * @license MIT
*/ */
import { IBufferRange, ILink } from './Types'; import { IBufferRange, ILink } from 'browser/Types';
import { ILinkProvider } from './services/Services'; import { ILinkProvider } from 'browser/services/Services';
import { CellData } from '../common/primitives/buffer/CellData'; import { CellData } from 'common/buffer/CellData';
import { IBufferLine } from '../common/primitives/buffer/Types'; import type { IBufferLine } from 'common/buffer/CellTypes';
import { IBufferService, IOptionsService, IOscLinkService } from '../common/services/Services'; import { IBufferService, IOptionsService, IOscLinkService } from 'common/services/Services';
export class OscLinkProvider implements ILinkProvider { export class OscLinkProvider implements ILinkProvider {
private readonly _workCell = new CellData(); private readonly _workCell = new CellData();
+7 -5
View File
@@ -3,8 +3,8 @@
* @license MIT * @license MIT
*/ */
import { IRenderDebouncerWithCallback } from './Types'; import { IRenderDebouncerWithCallback } from 'browser/Types';
import { ICoreBrowserService } from './services/Services'; import { ICoreBrowserService } from 'browser/services/Services';
/** /**
* Debounces calls to render terminal rows using animation frames. * Debounces calls to render terminal rows using animation frames.
@@ -23,7 +23,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback {
} }
public dispose(): void { public dispose(): void {
if (this._animationFrame !== undefined) { if (this._animationFrame) {
this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame); this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
this._animationFrame = undefined; this._animationFrame = undefined;
} }
@@ -31,7 +31,9 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback {
public addRefreshCallback(callback: FrameRequestCallback): number { public addRefreshCallback(callback: FrameRequestCallback): number {
this._refreshCallbacks.push(callback); this._refreshCallbacks.push(callback);
this._animationFrame ??= this._coreBrowserService.window.requestAnimationFrame(() => this._innerRefresh()); if (!this._animationFrame) {
this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._innerRefresh());
}
return this._animationFrame; return this._animationFrame;
} }
@@ -44,7 +46,7 @@ export class RenderDebouncer implements IRenderDebouncerWithCallback {
this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart; this._rowStart = this._rowStart !== undefined ? Math.min(this._rowStart, rowStart) : rowStart;
this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd; this._rowEnd = this._rowEnd !== undefined ? Math.max(this._rowEnd, rowEnd) : rowEnd;
if (this._animationFrame !== undefined) { if (this._animationFrame) {
return; return;
} }
+7 -7
View File
@@ -3,13 +3,13 @@
* @license MIT * @license MIT
*/ */
import { MockCompositionHelper, MockRenderer, MockViewport, TestTerminal } from './TestUtils.test'; import { MockCompositionHelper, MockRenderer, MockViewport, TestTerminal } from 'browser/TestUtils.test';
import type { IBrowser } from './Types'; import type { IBrowser } from 'browser/Types';
import { assert } from 'chai'; import { assert } from 'chai';
import { DEFAULT_ATTR_DATA } from '../common/primitives/buffer/BufferLine'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { CellData } from '../common/primitives/buffer/CellData'; import { CellData } from 'common/buffer/CellData';
import { MockUnicodeService, createCellData } from '../common/TestUtils.test'; import { MockUnicodeService, createCellData } from 'common/terminal/TestUtils.test';
import { IMarker } from '../common/primitives/buffer/Types'; import type { IMarker } from 'common/buffer/CellTypes';
const INIT_COLS = 80; const INIT_COLS = 80;
const INIT_ROWS = 24; const INIT_ROWS = 24;
@@ -1146,7 +1146,7 @@ describe('Terminal', () => {
assert.equal(termConverting.buffer.lines.get(1)!.translateToString(true), 'World'); assert.equal(termConverting.buffer.lines.get(1)!.translateToString(true), 'World');
}); });
// FIXME: move to common/CoreTerminal.test once the trimming is moved over // FIXME: move to common/terminal/CoreTerminal.test once the trimming is moved over
describe('marker lifecycle', () => { describe('marker lifecycle', () => {
// create a 10x5 terminal with markers on every line // create a 10x5 terminal with markers on every line
// to test marker lifecycle under various terminal actions // to test marker lifecycle under various terminal actions
+1 -1
View File
@@ -7,7 +7,7 @@ import * as path from 'path';
import * as os from 'os'; import * as os from 'os';
import * as fs from 'fs'; import * as fs from 'fs';
import * as pty from 'node-pty'; import * as pty from 'node-pty';
import { CoreBrowserTerminal } from './CoreBrowserTerminal'; import { CoreBrowserTerminal } from 'browser/CoreBrowserTerminal';
import { IDisposable } from '@xterm/xterm'; import { IDisposable } from '@xterm/xterm';
// all test files expect terminal in 80x25 // all test files expect terminal in 80x25
+20 -17
View File
@@ -4,23 +4,26 @@
*/ */
import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration, IRenderDimensions as IRenderDimensionsApi } from '@xterm/xterm'; import { IDisposable, IMarker, ILinkProvider, IDecorationOptions, IDecoration, IRenderDimensions as IRenderDimensionsApi } from '@xterm/xterm';
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from './services/Services'; import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from './renderer/shared/Types'; import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from './Types'; import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from 'browser/Types';
import { IAttributeData, IBuffer, IBufferLine, IBufferSet, ICellData } from '../common/primitives/buffer/Types'; import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { ICircularList } from '../common/primitives/CircularList'; import type { ICircularList } from 'common/base/CircularList';
import { XtermListener, ICharset, ColorIndex } from '../common/primitives/Types'; import type { IBufferLine, ICellData, IAttributeData } from 'common/buffer/CellTypes';
import { ITerminalOptions } from '../common/Types'; import type { ICharset } from 'common/base/Charsets';
import { Buffer } from '../common/primitives/buffer/Buffer'; import type { ITerminalOptions } from 'common/services/Services';
import * as Browser from '../common/primitives/Platform'; import type { XtermListener } from 'common/terminal/CoreTerminalTypes';
import { CoreBrowserTerminal } from './CoreBrowserTerminal'; import { ColorIndex } from 'common/terminal/InputHandlerTypes';
import { IUnicodeService, IOptionsService, ICoreService, IMouseStateService } from '../common/services/Services'; import { Buffer } from 'common/buffer/Buffer';
import { IFunctionIdentifier, IParams } from '../common/primitives/parser/Types'; import * as Browser from 'common/base/Platform';
import { AttributeData } from '../common/primitives/buffer/AttributeData'; import { CoreBrowserTerminal } from 'browser/CoreBrowserTerminal';
import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from './selection/Types'; import { IUnicodeService, IOptionsService, ICoreService, IMouseStateService } from 'common/services/Services';
import { css } from '../common/primitives/Color'; import { IFunctionIdentifier, IParams } from 'common/parser/Types';
import { createRenderDimensions } from './renderer/shared/RendererUtils'; import { AttributeData } from 'common/buffer/AttributeData';
import { Emitter, type IEvent } from '../common/primitives/Event'; import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types';
import { css } from 'common/base/Color';
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
import { Emitter, type IEvent } from 'common/base/Event';
export class TestTerminal extends CoreBrowserTerminal { export class TestTerminal extends CoreBrowserTerminal {
public get curAttrData(): IAttributeData { return (this as any)._inputHandler._curAttrData; } public get curAttrData(): IAttributeData { return (this as any)._inputHandler._curAttrData; }
+3 -10
View File
@@ -3,10 +3,10 @@
* @license MIT * @license MIT
*/ */
import { IRenderDebouncer } from './Types';
const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second
import { IRenderDebouncer } from 'browser/Types';
/** /**
* Debounces calls to update screen readers to update at most once configurable interval of time. * Debounces calls to update screen readers to update at most once configurable interval of time.
*/ */
@@ -31,9 +31,7 @@ export class TimeBasedDebouncer implements IRenderDebouncer {
public dispose(): void { public dispose(): void {
if (this._refreshTimeoutID) { if (this._refreshTimeoutID) {
clearTimeout(this._refreshTimeoutID); clearTimeout(this._refreshTimeoutID);
this._refreshTimeoutID = undefined;
} }
this._additionalRefreshRequested = false;
} }
public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void { public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void {
@@ -49,12 +47,7 @@ export class TimeBasedDebouncer implements IRenderDebouncer {
// enough time to pass before refreshing again. // enough time to pass before refreshing again.
const refreshRequestTime: number = performance.now(); const refreshRequestTime: number = performance.now();
if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) { if (refreshRequestTime - this._lastRefreshMs >= this._debounceThresholdMS) {
// Enough time has elapsed since the last refresh; refresh immediately // Enough time has lapsed since the last refresh; refresh immediately
if (this._refreshTimeoutID !== undefined) {
clearTimeout(this._refreshTimeoutID);
this._refreshTimeoutID = undefined;
this._additionalRefreshRequested = false;
}
this._lastRefreshMs = refreshRequestTime; this._lastRefreshMs = refreshRequestTime;
this._innerRefresh(); this._innerRefresh();
} else if (!this._additionalRefreshRequested) { } else if (!this._additionalRefreshRequested) {
+7 -6
View File
@@ -3,13 +3,14 @@
* @license MIT * @license MIT
*/ */
import { IColor } from '../common/primitives/Types'; import type { CharData } from 'common/buffer/CellTypes';
import { ITerminalOptions } from '../common/Types'; import type { IColor } from 'common/base/ColorTypes';
import { CharData, IBuffer } from '../common/primitives/buffer/Types'; import type { ICoreTerminal } from 'common/terminal/CoreTerminalTypes';
import { ICoreTerminal } from '../common/CoreTerminal'; import type { ITerminalOptions } from 'common/services/Services';
import { IBuffer } from 'common/buffer/Types';
import { IDisposable, IRenderDimensions as IRenderDimensionsApi, Terminal as ITerminalApi } from '@xterm/xterm'; import { IDisposable, IRenderDimensions as IRenderDimensionsApi, Terminal as ITerminalApi } from '@xterm/xterm';
import { channels, css } from '../common/primitives/Color'; import { channels, css } from 'common/base/Color';
import type { IEvent } from '../common/primitives/Event'; import type { IEvent } from 'common/base/Event';
/** /**
* A portion of the public API that are implemented identially internally and simply passed through. * A portion of the public API that are implemented identially internally and simply passed through.
+10 -10
View File
@@ -3,16 +3,16 @@
* @license MIT * @license MIT
*/ */
import { ICoreBrowserService, IRenderService, IThemeService } from './services/Services'; import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
import { ViewportConstants } from './shared/Constants'; import { ViewportConstants } from 'browser/shared/Constants';
import { Disposable, toDisposable } from '../common/primitives/Lifecycle'; import { Disposable, toDisposable } from 'common/base/Lifecycle';
import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from '../common/services/Services'; import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from 'common/services/Services';
import { CoreMouseEventType } from '../common/primitives/Types'; import { CoreMouseEventType } from 'common/services/Types';
import { scheduleAtNextAnimationFrame } from './Dom'; import { scheduleAtNextAnimationFrame } from 'browser/Dom';
import { SmoothScrollableElement } from './scrollable/scrollableElement'; import { SmoothScrollableElement } from 'browser/scrollable/scrollableElement';
import type { IScrollableElementChangeOptions } from './scrollable/scrollableElementOptions'; import type { IScrollableElementChangeOptions } from 'browser/scrollable/scrollableElementOptions';
import { Emitter, EventUtils } from '../common/primitives/Event'; import { Emitter, EventUtils } from 'common/base/Event';
import { Scrollable, ScrollbarVisibility, type IScrollEvent } from './scrollable/scrollable'; import { Scrollable, ScrollbarVisibility, type IScrollEvent } from 'browser/scrollable/scrollable';
export class Viewport extends Disposable { export class Viewport extends Disposable {
@@ -3,9 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ICoreBrowserService, IRenderService } from '../services/Services'; import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
import { Disposable, toDisposable } from '../../common/primitives/Lifecycle'; import { Disposable, toDisposable } from 'common/base/Lifecycle';
import { IBufferService, IDecorationService, IInternalDecoration } from '../../common/services/Services'; import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services';
export class BufferDecorationRenderer extends Disposable { export class BufferDecorationRenderer extends Disposable {
private readonly _container: HTMLElement; private readonly _container: HTMLElement;
@@ -4,7 +4,7 @@
*/ */
import { assert } from 'chai'; import { assert } from 'chai';
import { ColorZoneStore } from './ColorZoneStore'; import { ColorZoneStore } from 'browser/decorations/ColorZoneStore';
const optionsRedFull = { const optionsRedFull = {
overviewRulerOptions: { overviewRulerOptions: {
+1 -1
View File
@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { IInternalDecoration } from '../../common/services/Services'; import { IInternalDecoration } from 'common/services/Services';
export interface IColorZoneStore { export interface IColorZoneStore {
readonly zones: IColorZone[]; readonly zones: IColorZone[];
@@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ColorZoneStore, IColorZone, IColorZoneStore } from './ColorZoneStore'; import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore';
import { ICoreBrowserService, IRenderService, IThemeService } from '../services/Services'; import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
import { Disposable, toDisposable } from '../../common/primitives/Lifecycle'; import { Disposable, toDisposable } from 'common/base/Lifecycle';
import { IBufferService, IDecorationService, IOptionsService } from '../../common/services/Services'; import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
const enum Constants { const enum Constants {
OVERVIEW_RULER_BORDER_WIDTH = 1 OVERVIEW_RULER_BORDER_WIDTH = 1
+3 -3
View File
@@ -4,9 +4,9 @@
*/ */
import { assert } from 'chai'; import { assert } from 'chai';
import { CompositionHelper } from './CompositionHelper'; import { CompositionHelper } from 'browser/input/CompositionHelper';
import { MockRenderService } from '../TestUtils.test'; import { MockRenderService } from 'browser/TestUtils.test';
import { MockCoreService, MockBufferService, MockOptionsService } from '../../common/TestUtils.test'; import { MockCoreService, MockBufferService, MockOptionsService } from 'common/terminal/TestUtils.test';
describe('CompositionHelper', () => { describe('CompositionHelper', () => {
let compositionHelper: CompositionHelper; let compositionHelper: CompositionHelper;
+3 -3
View File
@@ -3,9 +3,9 @@
* @license MIT * @license MIT
*/ */
import { IRenderService } from '../services/Services'; import { IRenderService } from 'browser/services/Services';
import { IBufferService, ICoreService, IOptionsService } from '../../common/services/Services'; import { IBufferService, ICoreService, IOptionsService } from 'common/services/Services';
import { C0 } from '../../common/primitives/data/EscapeSequences'; import { C0 } from 'common/base/EscapeSequences';
interface IPosition { interface IPosition {
start: number; start: number;
+1 -1
View File
@@ -5,7 +5,7 @@
import jsdom = require('jsdom'); import jsdom = require('jsdom');
import { assert } from 'chai'; import { assert } from 'chai';
import { getCoords } from './Mouse'; import { getCoords } from 'browser/input/Mouse';
const CHAR_WIDTH = 10; const CHAR_WIDTH = 10;
const CHAR_HEIGHT = 20; const CHAR_HEIGHT = 20;
+9 -5
View File
@@ -6,8 +6,8 @@
export function getCoordsRelativeToElement(window: Pick<Window, 'getComputedStyle'>, event: {clientX: number, clientY: number}, element: HTMLElement): [number, number] { export function getCoordsRelativeToElement(window: Pick<Window, 'getComputedStyle'>, event: {clientX: number, clientY: number}, element: HTMLElement): [number, number] {
const rect = element.getBoundingClientRect(); const rect = element.getBoundingClientRect();
const elementStyle = window.getComputedStyle(element); const elementStyle = window.getComputedStyle(element);
const leftPadding = parseInt(elementStyle.getPropertyValue('padding-left'), 10); const leftPadding = parseInt(elementStyle.getPropertyValue('padding-left'));
const topPadding = parseInt(elementStyle.getPropertyValue('padding-top'), 10); const topPadding = parseInt(elementStyle.getPropertyValue('padding-top'));
return [ return [
event.clientX - rect.left - leftPadding, event.clientX - rect.left - leftPadding,
event.clientY - rect.top - topPadding event.clientY - rect.top - topPadding
@@ -22,7 +22,7 @@ export function getCoordsRelativeToElement(window: Pick<Window, 'getComputedStyl
* @param event The mouse event. * @param event The mouse event.
* @param element The terminal's container element. * @param element The terminal's container element.
* @param colCount The number of columns in the terminal. * @param colCount The number of columns in the terminal.
* @param rowCount The number of rows in the terminal. * @param rowCount The number of rows n the terminal.
* @param hasValidCharSize Whether there is a valid character size available. * @param hasValidCharSize Whether there is a valid character size available.
* @param cssCellWidth The cell width device pixel render dimensions. * @param cssCellWidth The cell width device pixel render dimensions.
* @param cssCellHeight The cell height device pixel render dimensions. * @param cssCellHeight The cell height device pixel render dimensions.
@@ -31,17 +31,21 @@ export function getCoordsRelativeToElement(window: Pick<Window, 'getComputedStyl
* select that cell and the right half will select the next cell. * select that cell and the right half will select the next cell.
*/ */
export function getCoords(window: Pick<Window, 'getComputedStyle'>, event: Pick<MouseEvent, 'clientX' | 'clientY'>, element: HTMLElement, colCount: number, rowCount: number, hasValidCharSize: boolean, cssCellWidth: number, cssCellHeight: number, isSelection?: boolean): [number, number] | undefined { export function getCoords(window: Pick<Window, 'getComputedStyle'>, event: Pick<MouseEvent, 'clientX' | 'clientY'>, element: HTMLElement, colCount: number, rowCount: number, hasValidCharSize: boolean, cssCellWidth: number, cssCellHeight: number, isSelection?: boolean): [number, number] | undefined {
// Coordinates cannot be measured if there is no valid character size. // Coordinates cannot be measured if there are no valid
if (!hasValidCharSize) { if (!hasValidCharSize) {
return undefined; return undefined;
} }
const coords = getCoordsRelativeToElement(window, event, element); const coords = getCoordsRelativeToElement(window, event, element);
if (!coords) {
return undefined;
}
coords[0] = Math.ceil((coords[0] + (isSelection ? cssCellWidth / 2 : 0)) / cssCellWidth); coords[0] = Math.ceil((coords[0] + (isSelection ? cssCellWidth / 2 : 0)) / cssCellWidth);
coords[1] = Math.ceil(coords[1] / cssCellHeight); coords[1] = Math.ceil(coords[1] / cssCellHeight);
// Ensure coordinates are within the terminal viewport. Note that selections // Ensure coordinates are within the terminal viewport. Note that selections
// need an additional point of precision to cover the end point (as characters // need an addition point of precision to cover the end point (as characters
// cover half of one char and half of the next). // cover half of one char and half of the next).
coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0)); coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0));
coords[1] = Math.min(Math.max(coords[1], 1), rowCount); coords[1] = Math.min(Math.max(coords[1], 1), rowCount);
+2 -2
View File
@@ -4,8 +4,8 @@
*/ */
import { assert } from 'chai'; import { assert } from 'chai';
import { IBufferService } from '../../common/services/Services'; import { IBufferService } from 'common/services/Services';
import { MockBufferService } from '../../common/TestUtils.test'; import { MockBufferService } from 'common/terminal/TestUtils.test';
import { moveToCellSequence } from './MoveToCell'; import { moveToCellSequence } from './MoveToCell';
describe('MoveToCell', () => { describe('MoveToCell', () => {
+3 -3
View File
@@ -3,8 +3,8 @@
* @license MIT * @license MIT
*/ */
import { C0 } from '../../common/primitives/data/EscapeSequences'; import { C0 } from 'common/base/EscapeSequences';
import { IBufferService } from '../../common/services/Services'; import { IBufferService } from 'common/services/Services';
const enum Direction { const enum Direction {
UP = 'A', UP = 'A',
@@ -156,7 +156,7 @@ function wrappedRowsForRow(currentRow: number, bufferService: IBufferService): n
*/ */
function horizontalDirection(startX: number, startY: number, targetX: number, targetY: number, bufferService: IBufferService, applicationCursor: boolean): Direction { function horizontalDirection(startX: number, startY: number, targetX: number, targetY: number, bufferService: IBufferService, applicationCursor: boolean): Direction {
let startRow; let startRow;
if (moveToRequestedRow(startY, targetY, bufferService, applicationCursor).length > 0) { if (moveToRequestedRow(targetX, targetY, bufferService, applicationCursor).length > 0) {
startRow = targetY - wrappedRowsForRow(targetY, bufferService); startRow = targetY - wrappedRowsForRow(targetY, bufferService);
} else { } else {
startRow = startY; startRow = startY;

Some files were not shown because too many files have changed in this diff Show More