Compare commits
56
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3395d051a9 | ||
|
|
31b8409120 | ||
|
|
3eca55a80d | ||
|
|
936442c813 | ||
|
|
dfb65ca66f | ||
|
|
35c91cbf2d | ||
|
|
587856e2ca | ||
|
|
4aa04c6b3f | ||
|
|
28eadddb79 | ||
|
|
59d1ec7bf0 | ||
|
|
7c82c1a815 | ||
|
|
04a0b7347c | ||
|
|
5d8da19c44 | ||
|
|
17ad33dc3d | ||
|
|
f76c7aa8ea | ||
|
|
43e8365f0e | ||
|
|
c7a94fc0bc | ||
|
|
4bb9c06b5f | ||
|
|
00e54532b3 | ||
|
|
e96133603b | ||
|
|
b2003861ec | ||
|
|
38f77fd4ca | ||
|
|
d3352cf1d5 | ||
|
|
0c4ec34df8 | ||
|
|
743852b60c | ||
|
|
2069176528 | ||
|
|
acd2472961 | ||
|
|
1b95efa8c8 | ||
|
|
ce1f5f9608 | ||
|
|
bf8af4a871 | ||
|
|
f8e9a81d0e | ||
|
|
7711d780c2 | ||
|
|
f8cef535c7 | ||
|
|
5ad56446b8 | ||
|
|
fbb2a9710f | ||
|
|
5a6427e867 | ||
|
|
8519ac6d99 | ||
|
|
ae611694de | ||
|
|
772df0cb98 | ||
|
|
aa93189cc5 | ||
|
|
f82a8029d2 | ||
|
|
a2f321c887 | ||
|
|
eb58cefa8e | ||
|
|
d92ca87cbe | ||
|
|
224167bbbe | ||
|
|
740fa44ae4 | ||
|
|
636baa8d79 | ||
|
|
44a27dc958 | ||
|
|
dc1ae4514b | ||
|
|
494cf1f2e1 | ||
|
|
645865efec | ||
|
|
83e3657a82 | ||
|
|
53554a05f7 | ||
|
|
51c8d8abf8 | ||
|
|
05ea5ac324 | ||
|
|
9e27c7debb |
@@ -25,4 +25,4 @@ When writing benchmark instructions:
|
|||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- Benchmarks run from built JS in `out-test/benchmark/*.benchmark.js`.
|
- Benchmarks run from built JS in `out-test/benchmark/*.benchmark.js`.
|
||||||
- Keep `NODE_PATH=./out` (handled by the npm script).
|
- Benchmark sources import compiled modules from `../../out/common/` and `../../out/browser/` (run `npm run build` first).
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ docs/
|
|||||||
npm-debug.log
|
npm-debug.log
|
||||||
/.idea/
|
/.idea/
|
||||||
.env
|
.env
|
||||||
|
mise.toml
|
||||||
build/
|
build/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
|||||||
+372
@@ -0,0 +1,372 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||||
|
"plugins": [
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
{
|
||||||
|
"$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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
-3
@@ -15,9 +15,6 @@
|
|||||||
"--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"
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ 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
|
||||||
@@ -98,3 +99,15 @@ const cell = line?.getCell(0);
|
|||||||
## Writing unit tests
|
## Writing unit tests
|
||||||
|
|
||||||
- Unit tests live alongside the source code file of the thing it's testing with a .test.ts suffix.
|
- Unit tests live alongside the source code file of the thing it's testing with a .test.ts suffix.
|
||||||
|
|
||||||
|
## Cursor Cloud specific instructions
|
||||||
|
|
||||||
|
**Demo server**: Start with `npm start` (port 3000). The demo server uses node-pty to spawn real shell sessions over WebSocket. Integration tests auto-start it via Playwright's `webServer` config, so you don't need to start it manually for `npm run test-integration`.
|
||||||
|
|
||||||
|
**Build before testing**: Always run `npm run build && npm run esbuild` before `npm run test-unit`. Integration tests also need `npm run esbuild-demo-client` and `npm run esbuild-demo-server`. The update script handles this automatically on session start.
|
||||||
|
|
||||||
|
**No external services**: This project has zero external dependencies (no databases, Docker, or APIs). Everything runs locally with Node.js.
|
||||||
|
|
||||||
|
**TypeScript compiler**: The project uses `tsgo` (native TypeScript compiler preview) rather than standard `tsc`. It's installed via the `@typescript/native-preview` package.
|
||||||
|
|
||||||
|
**Lint only changed files**: Prefer `npm run lint-changes` over `npm run lint` when iterating on code changes — it's significantly faster.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
## @xterm/addon-image
|
## @xterm/addon-image
|
||||||
|
|
||||||
Inline image output in xterm.js. Supports SIXEL and iTerm's inline image protocol (IIP).
|
Inline image output in xterm.js. Supports SIXEL, iTerm's inline image protocol (IIP)
|
||||||
|
and partially Kitty's terminal graphics prtocol (TGP).
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
@@ -43,9 +44,6 @@ terminal.loadAddon(imageAddon);
|
|||||||
|
|
||||||
### General Notes
|
### General Notes
|
||||||
|
|
||||||
- *IMPORTANT:* The worker approach as done in previous releases got removed.
|
|
||||||
The addon contructor no longer expects a worker path as first argument.
|
|
||||||
|
|
||||||
- By default the addon will activate these `windowOptions` reports on the terminal:
|
- By default the addon will activate these `windowOptions` reports on the terminal:
|
||||||
- getWinSizePixels (CSI 14 t)
|
- getWinSizePixels (CSI 14 t)
|
||||||
- getCellSizePixels (CSI 16 t)
|
- getCellSizePixels (CSI 16 t)
|
||||||
@@ -73,7 +71,7 @@ terminal.loadAddon(imageAddon);
|
|||||||
|
|
||||||
- **Cursor Positioning**
|
- **Cursor Positioning**
|
||||||
If scrolling is set, the cursor will be placed at the first image column of the last image row (VT340 mode).
|
If scrolling is set, the cursor will be placed at the first image column of the last image row (VT340 mode).
|
||||||
Other cursor positioning modes as used by xterm or mintty are not supported.
|
Other cursor positioning modes as used by xterm or mintty are not supported for sixel.
|
||||||
|
|
||||||
- **SIXEL Palette Handling**
|
- **SIXEL Palette Handling**
|
||||||
By default the addon limits the palette size to 256 registers (as demanded by the DEC specification).
|
By default the addon limits the palette size to 256 registers (as demanded by the DEC specification).
|
||||||
@@ -100,17 +98,24 @@ terminal.loadAddon(imageAddon);
|
|||||||
Set by default, change it with `{iipSupport: true}`.
|
Set by default, change it with `{iipSupport: true}`.
|
||||||
|
|
||||||
The IIP implementation has the following features / restrictions (sequence will silently fail for unmet conditions):
|
The IIP implementation has the following features / restrictions (sequence will silently fail for unmet conditions):
|
||||||
- Supported formats: PNG, JPEG and GIF
|
- Supported formats: PNG, JPEG, GIF and QOI
|
||||||
- No animation support.
|
- No animation support.
|
||||||
- Image type hinting is not supported (always deducted from data header).
|
- Image type hinting is not supported (always deducted from data header).
|
||||||
- File download is not supported.
|
- File download is not supported.
|
||||||
- Filename gets parsed but not used.
|
- Filename gets parsed but not used.
|
||||||
- Strict base64 handling as of RFC4648 §4 (standard alphabet, optional padding, no separator bytes allowed).
|
- Strict base64 handling as of RFC4648 §4 (standard alphabet, optional padding, no separator bytes allowed).
|
||||||
- Payload size may not exceed CEIL(sizeParameter * 4 / 3).
|
|
||||||
- Image scaling beyond terminal viewport size is allowed (e.g. `width=200%`).
|
- Image scaling beyond terminal viewport size is allowed (e.g. `width=200%`).
|
||||||
- Image pixel size is restricted by `pixelLimit` (pre- and post resizing).
|
- Image pixel size is restricted by `pixelLimit` (pre- and post resizing).
|
||||||
- Size parameter is restricted by `iipSizeLimit`.
|
- Playload size is restricted by `iipSizeLimit`.
|
||||||
- Cursor positioning behaves the same as for sixel (see above).
|
- size parameter is not mandatory anymore.
|
||||||
|
- Cursor gets positioned at the next cell of the last image line.
|
||||||
|
- ReportCellSize sequence is supported.
|
||||||
|
- Hi-res display support is currently limited (image gets blurred).
|
||||||
|
|
||||||
|
- **Kitty Graphics Support (TGP)**
|
||||||
|
Set by default, change it with `{kittySupport: true}`.
|
||||||
|
|
||||||
|
Note that the kitty graphics support is still WIP.
|
||||||
|
|
||||||
|
|
||||||
### Storage and Drawing Settings
|
### Storage and Drawing Settings
|
||||||
@@ -127,6 +132,7 @@ The addon exposes two properties to interact with the storage limits at runtime:
|
|||||||
By default the addon will show a placeholder pattern for evicted images that are still part
|
By default the addon will show a placeholder pattern for evicted images that are still part
|
||||||
of the terminal (e.g. in the scrollback). The pattern can be deactivated by toggling `showPlaceholder`.
|
of the terminal (e.g. in the scrollback). The pattern can be deactivated by toggling `showPlaceholder`.
|
||||||
|
|
||||||
|
|
||||||
### Image Data Retrieval
|
### Image Data Retrieval
|
||||||
|
|
||||||
The addon provides the following API endpoints to retrieve raw image data as canvas:
|
The addon provides the following API endpoints to retrieve raw image data as canvas:
|
||||||
@@ -140,6 +146,7 @@ The addon provides the following API endpoints to retrieve raw image data as can
|
|||||||
The buffer position is the 0-based absolute index (including scrollback at top).
|
The buffer position is the 0-based absolute index (including scrollback at top).
|
||||||
Note that the canvas gets created and data copied over for every call, thus it is not suitable for performance critical actions.
|
Note that the canvas gets created and data copied over for every call, thus it is not suitable for performance critical actions.
|
||||||
|
|
||||||
|
|
||||||
### 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, lets 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:
|
||||||
@@ -180,8 +187,8 @@ _How can I adjust the memory usage?_
|
|||||||
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.
|
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 memory peaking for 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`
|
- `sixelSizeLimit | iipSizeLimit | kittySizeLimit`
|
||||||
A constructor setting. This has only a small impact on peaking memory during decoding. It is meant to avoid processing of overly big or broken SIXEL sequences at an earlier phase, thus may stop the decoder from entering its memory intensive task 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
|
||||||
@@ -211,23 +218,5 @@ _How can I adjust the memory usage?_
|
|||||||
|
|
||||||
### Status
|
### Status
|
||||||
|
|
||||||
- Sixel support and image handling in xterm.js is considered beta quality.
|
- Sixel and IIP support and image handling in xterm.js is considered beta quality.
|
||||||
- IIP support is in alpha stage. Please file a bug for any awkwardities.
|
- Kitty support is in alpha stage. Please file a bug for any awkwardities or missing features.
|
||||||
|
|
||||||
|
|
||||||
### Changelog
|
|
||||||
|
|
||||||
- 0.5.0 integrate with xtermjs base repo (at v0.4.3)
|
|
||||||
- 0.4.3 defer canvas creation
|
|
||||||
- 0.4.2 fix image canvas resize
|
|
||||||
- 0.4.1 compat release for xterm.js 5.2.0
|
|
||||||
- 0.4.0 IIP support
|
|
||||||
- 0.3.1 compat release for xterm.js 5.1.0
|
|
||||||
- 0.3.0 important change: worker removed from addon
|
|
||||||
- 0.2.0 compat release for xterm.js 5.0.0
|
|
||||||
- 0.1.3 bugfix: avoid striping
|
|
||||||
- 0.1.2 bugfix: reset clear flag
|
|
||||||
- 0.1.1 bugfixes:
|
|
||||||
- clear sticky image tiles on render
|
|
||||||
- create folder from bootstrap.sh
|
|
||||||
- fix peer dependency in package.json
|
|
||||||
|
|||||||
Binary file not shown.
@@ -8,7 +8,7 @@ import { IIPImageStorage } from './IIPImageStorage';
|
|||||||
import { CELL_SIZE_DEFAULT } from './ImageStorage';
|
import { CELL_SIZE_DEFAULT } from './ImageStorage';
|
||||||
import Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm';
|
import Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm';
|
||||||
import QoiDecoder from 'xterm-wasm-parts/lib/qoi/QoiDecoder.wasm';
|
import QoiDecoder from 'xterm-wasm-parts/lib/qoi/QoiDecoder.wasm';
|
||||||
import { HeaderParser, IHeaderFields, HeaderState } from './IIPHeaderParser';
|
import { HeaderParser, IHeaderFields, HeaderState, SequenceType } from './IIPHeaderParser';
|
||||||
import { imageType, UNSUPPORTED_TYPE } from './IIPMetrics';
|
import { imageType, UNSUPPORTED_TYPE } from './IIPMetrics';
|
||||||
|
|
||||||
// Local const enum mirror - esbuild can't inline const enums from external packages
|
// Local const enum mirror - esbuild can't inline const enums from external packages
|
||||||
@@ -23,6 +23,7 @@ const enum DecoderConst {
|
|||||||
|
|
||||||
// default IIP header values
|
// default IIP header values
|
||||||
const DEFAULT_HEADER: IHeaderFields = {
|
const DEFAULT_HEADER: IHeaderFields = {
|
||||||
|
type: SequenceType.INVALID,
|
||||||
name: 'Unnamed file',
|
name: 'Unnamed file',
|
||||||
size: 0,
|
size: 0,
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
@@ -39,6 +40,8 @@ export class IIPHandler implements IOscHandler, IResetHandler {
|
|||||||
private _dec: Base64Decoder;
|
private _dec: Base64Decoder;
|
||||||
private _qoiDec: QoiDecoder;
|
private _qoiDec: QoiDecoder;
|
||||||
private _metrics = UNSUPPORTED_TYPE;
|
private _metrics = UNSUPPORTED_TYPE;
|
||||||
|
private _isMultipart = false;
|
||||||
|
private _abortMulti = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _opts: IImageAddonOptions,
|
private readonly _opts: IImageAddonOptions,
|
||||||
@@ -52,11 +55,14 @@ export class IIPHandler implements IOscHandler, IResetHandler {
|
|||||||
this._qoiDec = new QoiDecoder(DecoderConst.KEEP_DATA);
|
this._qoiDec = new QoiDecoder(DecoderConst.KEEP_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
public reset(): void {}
|
public reset(): void {
|
||||||
|
this._hp.reset();
|
||||||
|
this._dec.release();
|
||||||
|
this._qoiDec.release();
|
||||||
|
}
|
||||||
|
|
||||||
public start(): void {
|
public start(): void {
|
||||||
this._aborted = false;
|
this._aborted = false;
|
||||||
this._header = DEFAULT_HEADER;
|
|
||||||
this._metrics = UNSUPPORTED_TYPE;
|
this._metrics = UNSUPPORTED_TYPE;
|
||||||
this._hp.reset();
|
this._hp.reset();
|
||||||
}
|
}
|
||||||
@@ -76,15 +82,27 @@ export class IIPHandler implements IOscHandler, IResetHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (dataPos > 0) {
|
if (dataPos > 0) {
|
||||||
this._header = Object.assign({}, DEFAULT_HEADER, this._hp.fields);
|
const seqType = this._hp.fields.type;
|
||||||
if (!this._header.inline || !this._header.size || this._header.size > this._opts.iipSizeLimit) {
|
if (seqType === SequenceType.FILE) {
|
||||||
|
if (this._isMultipart) {
|
||||||
|
this._isMultipart = false;
|
||||||
|
this._abortMulti = false;
|
||||||
|
this._dec.release();
|
||||||
|
}
|
||||||
|
this._header = Object.assign({}, DEFAULT_HEADER, this._hp.fields);
|
||||||
|
if (!this._header.inline) {
|
||||||
|
this._aborted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._dec.init();
|
||||||
|
} else if (this._abortMulti) {
|
||||||
this._aborted = true;
|
this._aborted = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._dec.init();
|
|
||||||
if ((this._dec.put(data.subarray(dataPos, end)) as number) !== DecoderConst.OK) {
|
if ((this._dec.put(data.subarray(dataPos, end)) as number) !== DecoderConst.OK) {
|
||||||
this._dec.release();
|
this._dec.release();
|
||||||
this._aborted = true;
|
this._aborted = true;
|
||||||
|
if (this._isMultipart) this._abortMulti = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,22 +111,58 @@ export class IIPHandler implements IOscHandler, IResetHandler {
|
|||||||
public end(success: boolean): boolean | Promise<boolean> {
|
public end(success: boolean): boolean | Promise<boolean> {
|
||||||
if (this._aborted) return true;
|
if (this._aborted) return true;
|
||||||
|
|
||||||
|
if (this._hp.state !== HeaderState.END) {
|
||||||
|
if (this._hp.end()) return true;
|
||||||
|
}
|
||||||
|
const seqType = this._hp.fields.type;
|
||||||
|
|
||||||
|
if (seqType === SequenceType.FILEPART) return true;
|
||||||
|
|
||||||
|
if (seqType === SequenceType.REPORTCELLSIZE) {
|
||||||
|
// OSC 1337 ; ReportCellSize=[height];[width];[scale] ST
|
||||||
|
let width = CELL_SIZE_DEFAULT.width;
|
||||||
|
let height = CELL_SIZE_DEFAULT.height;
|
||||||
|
if (this._renderer.dimensions) {
|
||||||
|
width = this._renderer.dimensions.css.canvas.width / this._coreTerminal.cols;
|
||||||
|
height = this._renderer.dimensions.css.canvas.height / this._coreTerminal.rows;
|
||||||
|
}
|
||||||
|
const scale = this._coreTerminal._core._coreBrowserService?.dpr ?? 1;
|
||||||
|
const report = `\x1b]1337;ReportCellSize=${height.toFixed(3)};${width.toFixed(3)};${scale.toFixed(3)}\x1b\\`;
|
||||||
|
this._coreTerminal.input(report, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seqType === SequenceType.MULTIPARTFILE) {
|
||||||
|
this._header = Object.assign({}, DEFAULT_HEADER, this._hp.fields);
|
||||||
|
this._isMultipart = true;
|
||||||
|
this._abortMulti = false;
|
||||||
|
this._dec.release();
|
||||||
|
this._dec.init();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seqType === SequenceType.FILEEND) {
|
||||||
|
if (!this._isMultipart) return true;
|
||||||
|
this._isMultipart = false;
|
||||||
|
if (this._abortMulti || this._header.type !== SequenceType.MULTIPARTFILE) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallthrough for SequenceType.FILE & SequenceType.FILEEND
|
||||||
|
|
||||||
let w = 0;
|
let w = 0;
|
||||||
let h = 0;
|
let h = 0;
|
||||||
|
|
||||||
// early exit condition chain
|
// early exit condition chain
|
||||||
let cond: number | boolean = true;
|
let cond: number | boolean;
|
||||||
if (cond = success) {
|
if (cond = success) {
|
||||||
if (cond = !this._dec.end()) {
|
if (cond = !this._dec.end()) {
|
||||||
if (cond = this._dec.data8.length === this._header.size) {
|
this._metrics = imageType(this._dec.data8);
|
||||||
this._metrics = imageType(this._dec.data8);
|
if (cond = this._metrics.mime !== 'unsupported') {
|
||||||
if (cond = this._metrics.mime !== 'unsupported') {
|
w = this._metrics.width;
|
||||||
w = this._metrics.width;
|
h = this._metrics.height;
|
||||||
h = this._metrics.height;
|
if (cond = w && h && w * h < this._opts.pixelLimit) {
|
||||||
if (cond = w && h && w * h < this._opts.pixelLimit) {
|
[w, h] = this._resize(w, h).map(Math.floor);
|
||||||
[w, h] = this._resize(w, h).map(Math.floor);
|
cond = w && h && w * h < this._opts.pixelLimit;
|
||||||
cond = w && h && w * h < this._opts.pixelLimit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { HeaderParser, HeaderState, IHeaderFields } from './IIPHeaderParser';
|
import { HeaderParser, HeaderState, IHeaderFields, SequenceType } from './IIPHeaderParser';
|
||||||
|
|
||||||
|
|
||||||
const CASES: [string, IHeaderFields][] = [
|
const CASES: [string, IHeaderFields][] = [
|
||||||
['File=size=123456;name=dGVzdA==:', {name: 'test', size: 123456}],
|
['File=size=123456;name=dGVzdA==:', {type: SequenceType.FILE, name: 'test', size: 123456}],
|
||||||
['File=size=123456;name=dGVzdA:', {name: 'test', size: 123456}],
|
['File=size=123456;name=dGVzdA:', {type: SequenceType.FILE, name: 'test', size: 123456}],
|
||||||
// utf-8 encoding in name
|
// utf-8 encoding in name
|
||||||
['File=size=123456;name=w7xtbMOkdXTDnw==:', {name: 'ümläutß', size: 123456}],
|
['File=size=123456;name=w7xtbMOkdXTDnw==:', {type: SequenceType.FILE, name: 'ümläutß', size: 123456}],
|
||||||
['File=size=123456;name=w7xtbMOkdXTDnw:', {name: 'ümläutß', size: 123456}],
|
['File=size=123456;name=w7xtbMOkdXTDnw:', {type: SequenceType.FILE, name: 'ümläutß', size: 123456}],
|
||||||
// full header spec
|
// full header spec
|
||||||
[
|
[
|
||||||
'File=inline=1;width=10px;height=20%;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw:',
|
'File=inline=1;width=10px;height=20%;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw:',
|
||||||
{
|
{
|
||||||
|
type: SequenceType.FILE,
|
||||||
inline: 1,
|
inline: 1,
|
||||||
width: '10px',
|
width: '10px',
|
||||||
height: '20%',
|
height: '20%',
|
||||||
@@ -28,6 +29,7 @@ const CASES: [string, IHeaderFields][] = [
|
|||||||
[
|
[
|
||||||
'File=inline=1;width=auto;height=20;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw:',
|
'File=inline=1;width=auto;height=20;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw:',
|
||||||
{
|
{
|
||||||
|
type: SequenceType.FILE,
|
||||||
inline: 1,
|
inline: 1,
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
height: '20',
|
height: '20',
|
||||||
@@ -101,7 +103,7 @@ describe('IIPHeaderParser', () => {
|
|||||||
let res = hp.parse(inp, 0, inp.length);
|
let res = hp.parse(inp, 0, inp.length);
|
||||||
assert.strictEqual(res, inp.length);
|
assert.strictEqual(res, inp.length);
|
||||||
assert.strictEqual(hp.state, HeaderState.END);
|
assert.strictEqual(hp.state, HeaderState.END);
|
||||||
assert.deepEqual(hp.fields, {name: 'test', size: 0});
|
assert.deepEqual(hp.fields, {type: SequenceType.FILE, name: 'test', size: 0});
|
||||||
hp.reset();
|
hp.reset();
|
||||||
inp = fromBs(CASES[0][0]);
|
inp = fromBs(CASES[0][0]);
|
||||||
res = hp.parse(inp, 0, inp.length);
|
res = hp.parse(inp, 0, inp.length);
|
||||||
@@ -115,7 +117,7 @@ describe('IIPHeaderParser', () => {
|
|||||||
let res = hp.parse(inp, 0, inp.length);
|
let res = hp.parse(inp, 0, inp.length);
|
||||||
assert.strictEqual(res, inp.length);
|
assert.strictEqual(res, inp.length);
|
||||||
assert.strictEqual(hp.state, HeaderState.END);
|
assert.strictEqual(hp.state, HeaderState.END);
|
||||||
assert.deepEqual(hp.fields, {name: '', size: 123456});
|
assert.deepEqual(hp.fields, {type: SequenceType.FILE, name: '', size: 123456});
|
||||||
hp.reset();
|
hp.reset();
|
||||||
inp = fromBs(CASES[0][0]);
|
inp = fromBs(CASES[0][0]);
|
||||||
res = hp.parse(inp, 0, inp.length);
|
res = hp.parse(inp, 0, inp.length);
|
||||||
@@ -135,4 +137,124 @@ describe('IIPHeaderParser', () => {
|
|||||||
assert.strictEqual(hp.state, HeaderState.END);
|
assert.strictEqual(hp.state, HeaderState.END);
|
||||||
assert.deepEqual(hp.fields, CASES[0][1]);
|
assert.deepEqual(hp.fields, CASES[0][1]);
|
||||||
});
|
});
|
||||||
|
it('FilePart sequence', () => {
|
||||||
|
const hp = new HeaderParser();
|
||||||
|
const inp = fromBs('FilePart=w7xtbMOkdXTDnw');
|
||||||
|
const res = hp.parse(inp, 0, inp.length);
|
||||||
|
assert.strictEqual(res, 9);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.END);
|
||||||
|
assert.deepEqual(hp.fields.type, SequenceType.FILEPART);
|
||||||
|
});
|
||||||
|
it('FilePart sequence - bytewise', () => {
|
||||||
|
const hp = new HeaderParser();
|
||||||
|
const inp = fromBs('FilePart=w7xtbMOkdXTDnw');
|
||||||
|
let res = -2;
|
||||||
|
let pos = 0;
|
||||||
|
while (res === -2 && pos < inp.length) {
|
||||||
|
res = hp.parse(new Uint32Array([inp[pos++]]), 0, 1);
|
||||||
|
}
|
||||||
|
assert.strictEqual(res, 1);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.END);
|
||||||
|
assert.deepEqual(hp.fields.type, SequenceType.FILEPART);
|
||||||
|
});
|
||||||
|
it('MultipartFile sequence', () => {
|
||||||
|
const hp = new HeaderParser();
|
||||||
|
const inp = fromBs('MultipartFile=inline=1;width=10px;height=20%;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw');
|
||||||
|
let res = hp.parse(inp, 0, inp.length);
|
||||||
|
assert.strictEqual(res, -2);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.VALUE);
|
||||||
|
res = hp.end();
|
||||||
|
assert.strictEqual(res, 0);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.END);
|
||||||
|
assert.deepEqual(
|
||||||
|
hp.fields,
|
||||||
|
{
|
||||||
|
type: SequenceType.MULTIPARTFILE,
|
||||||
|
inline: 1,
|
||||||
|
width: '10px',
|
||||||
|
height: '20%',
|
||||||
|
preserveAspectRatio: 1,
|
||||||
|
size: 123456,
|
||||||
|
name: 'ümläutß'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('MultipartFile sequence - bytewise', () => {
|
||||||
|
const hp = new HeaderParser();
|
||||||
|
const inp = fromBs('MultipartFile=inline=1;width=10px;height=20%;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw');
|
||||||
|
let res = -2;
|
||||||
|
let pos = 0;
|
||||||
|
while (res === -2 && pos < inp.length) {
|
||||||
|
res = hp.parse(new Uint32Array([inp[pos++]]), 0, 1);
|
||||||
|
}
|
||||||
|
assert.strictEqual(res, -2);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.VALUE);
|
||||||
|
res = hp.end();
|
||||||
|
assert.strictEqual(res, 0);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.END);
|
||||||
|
assert.deepEqual(
|
||||||
|
hp.fields,
|
||||||
|
{
|
||||||
|
type: SequenceType.MULTIPARTFILE,
|
||||||
|
inline: 1,
|
||||||
|
width: '10px',
|
||||||
|
height: '20%',
|
||||||
|
preserveAspectRatio: 1,
|
||||||
|
size: 123456,
|
||||||
|
name: 'ümläutß'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('FileEnd sequence', () => {
|
||||||
|
const hp = new HeaderParser();
|
||||||
|
const inp = fromBs('FileEnd');
|
||||||
|
let res = hp.parse(inp, 0, inp.length);
|
||||||
|
assert.strictEqual(res, -2);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.START);
|
||||||
|
res = hp.end();
|
||||||
|
assert.strictEqual(res, 0);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.END);
|
||||||
|
assert.deepEqual(hp.fields.type, SequenceType.FILEEND);
|
||||||
|
});
|
||||||
|
it('FileEnd sequence - bytewise', () => {
|
||||||
|
const hp = new HeaderParser();
|
||||||
|
const inp = fromBs('FileEnd');
|
||||||
|
let res = -2;
|
||||||
|
let pos = 0;
|
||||||
|
while (res === -2 && pos < inp.length) {
|
||||||
|
res = hp.parse(new Uint32Array([inp[pos++]]), 0, 1);
|
||||||
|
}
|
||||||
|
assert.strictEqual(res, -2);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.START);
|
||||||
|
res = hp.end();
|
||||||
|
assert.strictEqual(res, 0);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.END);
|
||||||
|
assert.deepEqual(hp.fields.type, SequenceType.FILEEND);
|
||||||
|
});
|
||||||
|
it('ReportCellSize sequence', () => {
|
||||||
|
const hp = new HeaderParser();
|
||||||
|
const inp = fromBs('ReportCellSize');
|
||||||
|
let res = hp.parse(inp, 0, inp.length);
|
||||||
|
assert.strictEqual(res, -2);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.START);
|
||||||
|
res = hp.end();
|
||||||
|
assert.strictEqual(res, 0);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.END);
|
||||||
|
assert.deepEqual(hp.fields, { type: SequenceType.REPORTCELLSIZE });
|
||||||
|
});
|
||||||
|
it('ReportCellSize sequence - bytewise', () => {
|
||||||
|
const hp = new HeaderParser();
|
||||||
|
const inp = fromBs('ReportCellSize');
|
||||||
|
let res = -2;
|
||||||
|
let pos = 0;
|
||||||
|
while (res === -2 && pos < inp.length) {
|
||||||
|
res = hp.parse(new Uint32Array([inp[pos++]]), 0, 1);
|
||||||
|
}
|
||||||
|
assert.strictEqual(res, -2);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.START);
|
||||||
|
res = hp.end();
|
||||||
|
assert.strictEqual(res, 0);
|
||||||
|
assert.strictEqual(hp.state, HeaderState.END);
|
||||||
|
assert.deepEqual(hp.fields, { type: SequenceType.REPORTCELLSIZE });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,9 +6,27 @@
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
declare const Buffer: any;
|
declare const Buffer: any;
|
||||||
|
|
||||||
|
export const enum HeaderState {
|
||||||
|
START = 0,
|
||||||
|
ABORT = 1,
|
||||||
|
KEY = 2,
|
||||||
|
VALUE = 3,
|
||||||
|
END = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum SequenceType {
|
||||||
|
INVALID = 0,
|
||||||
|
FILE = 1,
|
||||||
|
MULTIPARTFILE = 2,
|
||||||
|
FILEPART = 3,
|
||||||
|
FILEEND = 4,
|
||||||
|
REPORTCELLSIZE = 5
|
||||||
|
}
|
||||||
|
|
||||||
export interface IHeaderFields {
|
export interface IHeaderFields {
|
||||||
[key: string]: number | string | Uint32Array | null | undefined;
|
[key: string]: number | string | Uint32Array | null | undefined;
|
||||||
|
// sequence type
|
||||||
|
type: SequenceType;
|
||||||
// base-64 encoded filename. Defaults to "Unnamed file".
|
// base-64 encoded filename. Defaults to "Unnamed file".
|
||||||
name: string;
|
name: string;
|
||||||
// File size in bytes. The file transfer will be canceled if this size is exceeded.
|
// File size in bytes. The file transfer will be canceled if this size is exceeded.
|
||||||
@@ -29,14 +47,6 @@ export interface IHeaderFields {
|
|||||||
inline?: number;
|
inline?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum HeaderState {
|
|
||||||
START = 0,
|
|
||||||
ABORT = 1,
|
|
||||||
KEY = 2,
|
|
||||||
VALUE = 3,
|
|
||||||
END = 4
|
|
||||||
}
|
|
||||||
|
|
||||||
// field value decoders
|
// field value decoders
|
||||||
|
|
||||||
// ASCII bytes to string
|
// ASCII bytes to string
|
||||||
@@ -92,7 +102,19 @@ const DECODERS: {[key: string]: (v: Uint32Array) => number | string} = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// sequence type markers
|
||||||
|
// File
|
||||||
const FILE_MARKER = [70, 105, 108, 101];
|
const FILE_MARKER = [70, 105, 108, 101];
|
||||||
|
// MultipartFile
|
||||||
|
const MULTIPARTFILE_MARKER = [77, 117, 108, 116, 105, 112, 97, 114, 116, 70, 105, 108, 101];
|
||||||
|
// FilePart
|
||||||
|
const FILEPART_MARKER = [70, 105, 108, 101, 80, 97, 114, 116];
|
||||||
|
// FileEnd
|
||||||
|
const FILEEND_MARKER = [70, 105, 108, 101, 69, 110, 100];
|
||||||
|
// ReportCellSize
|
||||||
|
const REPORTCELLSIZE_MARKER = [82, 101, 112, 111, 114, 116, 67, 101, 108, 108, 83, 105, 122, 101];
|
||||||
|
|
||||||
|
// max allowed chars for sequence header
|
||||||
const MAX_FIELDCHARS = 1024;
|
const MAX_FIELDCHARS = 1024;
|
||||||
|
|
||||||
|
|
||||||
@@ -111,12 +133,43 @@ export class HeaderParser {
|
|||||||
this._key = '';
|
this._key = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public end(): number {
|
||||||
|
if (this.state === HeaderState.START) {
|
||||||
|
if (this._position === FILEEND_MARKER.length) {
|
||||||
|
for (let k = 0; k < FILEEND_MARKER.length; ++k) {
|
||||||
|
if (this._buffer[k] !== FILEEND_MARKER[k]) return this._a();
|
||||||
|
}
|
||||||
|
this.fields['type'] = SequenceType.FILEEND;
|
||||||
|
this.state = HeaderState.END;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (this._position === REPORTCELLSIZE_MARKER.length) {
|
||||||
|
for (let k = 0; k < REPORTCELLSIZE_MARKER.length; ++k) {
|
||||||
|
if (this._buffer[k] !== REPORTCELLSIZE_MARKER[k]) return this._a();
|
||||||
|
}
|
||||||
|
this.fields['type'] = SequenceType.REPORTCELLSIZE;
|
||||||
|
this.state = HeaderState.END;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return this._a();
|
||||||
|
}
|
||||||
|
if (this.state === HeaderState.END) return 0;
|
||||||
|
if (this.state === HeaderState.VALUE
|
||||||
|
&& this.fields.type === SequenceType.MULTIPARTFILE
|
||||||
|
) {
|
||||||
|
if (!this._storeValue(this._position)) return this._a();
|
||||||
|
this.state = HeaderState.END;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return this._a();
|
||||||
|
}
|
||||||
|
|
||||||
public parse(data: Uint32Array, start: number, end: number): number {
|
public parse(data: Uint32Array, start: number, end: number): number {
|
||||||
let state = this.state;
|
let state = this.state;
|
||||||
let pos = this._position;
|
let pos = this._position;
|
||||||
const buffer = this._buffer;
|
const buffer = this._buffer;
|
||||||
if (state === HeaderState.ABORT || state === HeaderState.END) return -1;
|
if (state === HeaderState.ABORT || state === HeaderState.END) return -1;
|
||||||
if (state === HeaderState.START && pos > 6) return -1;
|
if (state === HeaderState.START && pos > 14) return -1;
|
||||||
for (let i = start; i < end; ++i) {
|
for (let i = start; i < end; ++i) {
|
||||||
const c = data[i];
|
const c = data[i];
|
||||||
switch (c) {
|
switch (c) {
|
||||||
@@ -127,8 +180,29 @@ export class HeaderParser {
|
|||||||
break;
|
break;
|
||||||
case 61: // =
|
case 61: // =
|
||||||
if (state === HeaderState.START) {
|
if (state === HeaderState.START) {
|
||||||
for (let k = 0; k < FILE_MARKER.length; ++k) {
|
if (buffer[0] === 70) {
|
||||||
if (buffer[k] !== FILE_MARKER[k]) return this._a();
|
// 'File' or 'FilePart'
|
||||||
|
let k = 0;
|
||||||
|
for (; k < FILE_MARKER.length; ++k) {
|
||||||
|
if (buffer[k] !== FILE_MARKER[k]) return this._a();
|
||||||
|
}
|
||||||
|
this.fields['type'] = SequenceType.FILE;
|
||||||
|
if (pos === FILEPART_MARKER.length) {
|
||||||
|
for (; k < FILEPART_MARKER.length; ++k) {
|
||||||
|
if (buffer[k] !== FILEPART_MARKER[k]) return this._a();
|
||||||
|
}
|
||||||
|
this.fields['type'] = SequenceType.FILEPART;
|
||||||
|
this.state = HeaderState.END;
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
} else if (buffer[0] === 77) {
|
||||||
|
// 'MultipartFile'
|
||||||
|
for (let k = 0; k < MULTIPARTFILE_MARKER.length; ++k) {
|
||||||
|
if (buffer[k] !== MULTIPARTFILE_MARKER[k]) return this._a();
|
||||||
|
}
|
||||||
|
this.fields['type'] = SequenceType.MULTIPARTFILE;
|
||||||
|
} else {
|
||||||
|
return this._a();
|
||||||
}
|
}
|
||||||
state = HeaderState.KEY;
|
state = HeaderState.KEY;
|
||||||
pos = 0;
|
pos = 0;
|
||||||
@@ -158,6 +232,7 @@ export class HeaderParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _a(): number {
|
private _a(): number {
|
||||||
|
this.fields.type = SequenceType.INVALID;
|
||||||
this.state = HeaderState.ABORT;
|
this.state = HeaderState.ABORT;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { IAddImageOpts } from './Types';
|
||||||
import { ImageStorage } from './ImageStorage';
|
import { ImageStorage } from './ImageStorage';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,6 +13,7 @@ import { ImageStorage } from './ImageStorage';
|
|||||||
* - Always uses scrolling mode (cursor advances with image)
|
* - Always uses scrolling mode (cursor advances with image)
|
||||||
*/
|
*/
|
||||||
export class IIPImageStorage {
|
export class IIPImageStorage {
|
||||||
|
private _addImageOpts: IAddImageOpts = { scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'iip' };
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _storage: ImageStorage
|
private readonly _storage: ImageStorage
|
||||||
) {}
|
) {}
|
||||||
@@ -21,6 +23,6 @@ export class IIPImageStorage {
|
|||||||
* Always uses scrolling mode — cursor advances past the image.
|
* Always uses scrolling mode — cursor advances past the image.
|
||||||
*/
|
*/
|
||||||
public addImage(img: HTMLCanvasElement | ImageBitmap): void {
|
public addImage(img: HTMLCanvasElement | ImageBitmap): void {
|
||||||
this._storage.addImage(img, true);
|
this._storage.addImage(img, this._addImageOpts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ const TEST_IMAGES: [string, IMetrics][] = [
|
|||||||
['iphone_hdr_YES.jpg', { mime: 'image/jpeg', width: 3264, height: 2448 }],
|
['iphone_hdr_YES.jpg', { mime: 'image/jpeg', width: 3264, height: 2448 }],
|
||||||
['nikon-e950.jpg', { mime: 'image/jpeg', width: 800, height: 600 }],
|
['nikon-e950.jpg', { mime: 'image/jpeg', width: 800, height: 600 }],
|
||||||
['agfa-makernotes.jpg', { mime: 'image/jpeg', width: 8, height: 8 }],
|
['agfa-makernotes.jpg', { mime: 'image/jpeg', width: 8, height: 8 }],
|
||||||
['sony-alpha-6000.jpg', { mime: 'image/jpeg', width: 6000, height: 4000 }]
|
['sony-alpha-6000.jpg', { mime: 'image/jpeg', width: 6000, height: 4000 }],
|
||||||
|
['dice.qoi', { mime: 'image/qoi', width: 800, height: 600 }],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -137,11 +137,6 @@ export class ImageAddon implements ITerminalAddon, IImageApi {
|
|||||||
|
|
||||||
// enable size reports
|
// enable size reports
|
||||||
if (this._opts.enableSizeReports) {
|
if (this._opts.enableSizeReports) {
|
||||||
// const windowOptions = terminal.getOption('windowOptions');
|
|
||||||
// windowOptions.getWinSizePixels = true;
|
|
||||||
// windowOptions.getCellSizePixels = true;
|
|
||||||
// windowOptions.getWinSizeChars = true;
|
|
||||||
// terminal.setOption('windowOptions', windowOptions);
|
|
||||||
const windowOps = terminal.options.windowOptions ?? {};
|
const windowOps = terminal.options.windowOptions ?? {};
|
||||||
windowOps.getWinSizePixels = true;
|
windowOps.getWinSizePixels = true;
|
||||||
windowOps.getCellSizePixels = true;
|
windowOps.getCellSizePixels = true;
|
||||||
@@ -260,7 +255,7 @@ export class ImageAddon implements ITerminalAddon, IImageApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _report(s: string): void {
|
private _report(s: string): void {
|
||||||
this._terminal?._core.coreService.triggerDataEvent(s);
|
this._terminal?._core.input(s, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _decset(params: (number | number[])[]): boolean {
|
private _decset(params: (number | number[])[]): boolean {
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ export class ImageRenderer extends Disposable implements IDisposable {
|
|||||||
public clearLines(start: number, end: number, layer?: ImageLayer): void {
|
public clearLines(start: number, end: number, layer?: ImageLayer): void {
|
||||||
const y = start * (this.dimensions?.css.cell.height || 0);
|
const y = start * (this.dimensions?.css.cell.height || 0);
|
||||||
const w = this.dimensions?.css.canvas.width || 0;
|
const w = this.dimensions?.css.canvas.width || 0;
|
||||||
const h = (++end - start) * (this.dimensions?.css.cell.height || 0);
|
const h = (end + 1 - start) * (this.dimensions?.css.cell.height || 0);
|
||||||
if (!layer || layer === 'top') {
|
if (!layer || layer === 'top') {
|
||||||
this._layers.get('top')?.clearRect(0, y, w, h);
|
this._layers.get('top')?.clearRect(0, y, w, h);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,11 @@
|
|||||||
|
|
||||||
import { IDisposable } from '@xterm/xterm';
|
import { IDisposable } from '@xterm/xterm';
|
||||||
import { ImageRenderer } from './ImageRenderer';
|
import { ImageRenderer } from './ImageRenderer';
|
||||||
import { ITerminalExt, IExtendedAttrsImage, IImageAddonOptions, IImageSpec, IBufferLineExt, BgFlags, Cell, Content, ICellSize, ExtFlags, Attributes, UnderlineStyle, ImageLayer } from './Types';
|
import {
|
||||||
|
ITerminalExt, IExtendedAttrsImage, IImageAddonOptions, IImageSpec,
|
||||||
|
IBufferLineExt, BgFlags, Cell, Content, ICellSize, ExtFlags, Attributes,
|
||||||
|
UnderlineStyle, IAddImageOpts
|
||||||
|
} from './Types';
|
||||||
|
|
||||||
|
|
||||||
// fallback default cell size
|
// fallback default cell size
|
||||||
@@ -235,13 +239,15 @@ export class ImageStorage implements IDisposable {
|
|||||||
/**
|
/**
|
||||||
* Method to add an image to the storage.
|
* Method to add an image to the storage.
|
||||||
* @param img - The image to add (canvas or bitmap).
|
* @param img - The image to add (canvas or bitmap).
|
||||||
* @param scrolling - When true, cursor advances with the image (lineFeed per row).
|
* @param opts - Options for addImage:
|
||||||
* When false, image is placed at (0,0) and cursor is restored (DECSET 80 / sixel origin mode).
|
* - scrolling: When true, cursor advances with the image.
|
||||||
* @param layer - Which canvas layer to render on ('top' or 'bottom').
|
* When false, image is placed at ORIGIN and cursor does not move.
|
||||||
* @param zIndex - Z-index for image layering within the same layer.
|
* - layer: Which canvas layer to render on ('top' or 'bottom').
|
||||||
|
* - zIndex: Z-index for image layering within the same layer.
|
||||||
|
* - cursorPos: 'vt340' for bottom-left, 'iip' for bottom.right.
|
||||||
* @returns The internal image ID assigned to the stored image.
|
* @returns The internal image ID assigned to the stored image.
|
||||||
*/
|
*/
|
||||||
public addImage(img: HTMLCanvasElement | ImageBitmap, scrolling: boolean, layer: ImageLayer = 'top', zIndex: number = 0): number {
|
public addImage(img: HTMLCanvasElement | ImageBitmap, opts: IAddImageOpts): number {
|
||||||
// never allow storage to exceed memory limit
|
// never allow storage to exceed memory limit
|
||||||
this._evictOldest(img.width * img.height);
|
this._evictOldest(img.width * img.height);
|
||||||
|
|
||||||
@@ -263,7 +269,7 @@ export class ImageStorage implements IDisposable {
|
|||||||
let offset = originX;
|
let offset = originX;
|
||||||
let tileCount = 0;
|
let tileCount = 0;
|
||||||
|
|
||||||
if (!scrolling) {
|
if (!opts.scrolling) {
|
||||||
buffer.x = 0;
|
buffer.x = 0;
|
||||||
buffer.y = 0;
|
buffer.y = 0;
|
||||||
offset = 0;
|
offset = 0;
|
||||||
@@ -277,7 +283,7 @@ export class ImageStorage implements IDisposable {
|
|||||||
this._writeToCell(line as IBufferLineExt, offset + col, imageId, row * cols + col);
|
this._writeToCell(line as IBufferLineExt, offset + col, imageId, row * cols + col);
|
||||||
tileCount++;
|
tileCount++;
|
||||||
}
|
}
|
||||||
if (scrolling) {
|
if (opts.scrolling) {
|
||||||
if (row < rows - 1) this._terminal._core._inputHandler.lineFeed();
|
if (row < rows - 1) this._terminal._core._inputHandler.lineFeed();
|
||||||
} else {
|
} else {
|
||||||
if (++buffer.y >= termRows) break;
|
if (++buffer.y >= termRows) break;
|
||||||
@@ -287,8 +293,12 @@ export class ImageStorage implements IDisposable {
|
|||||||
this._terminal._core._inputHandler._dirtyRowTracker.markDirty(buffer.y);
|
this._terminal._core._inputHandler._dirtyRowTracker.markDirty(buffer.y);
|
||||||
|
|
||||||
// cursor positioning modes
|
// cursor positioning modes
|
||||||
if (scrolling) {
|
if (opts.scrolling) {
|
||||||
buffer.x = offset;
|
if (opts.cursorPos === 'iip') {
|
||||||
|
buffer.x = Math.min(offset + cols, termCols);
|
||||||
|
} else {
|
||||||
|
buffer.x = offset;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
buffer.x = originX;
|
buffer.x = originX;
|
||||||
buffer.y = originY;
|
buffer.y = originY;
|
||||||
@@ -331,8 +341,8 @@ export class ImageStorage implements IDisposable {
|
|||||||
marker: endMarker || undefined,
|
marker: endMarker || undefined,
|
||||||
tileCount,
|
tileCount,
|
||||||
bufferType: this._terminal.buffer.active.type,
|
bufferType: this._terminal.buffer.active.type,
|
||||||
layer,
|
layer: opts.layer,
|
||||||
zIndex
|
zIndex: opts.zIndex
|
||||||
};
|
};
|
||||||
|
|
||||||
// finally add the image
|
// finally add the image
|
||||||
@@ -455,7 +465,6 @@ export class ImageStorage implements IDisposable {
|
|||||||
if (!nextE || nextE.imageId !== imageId || nextE.tileId !== startTile + count) {
|
if (!nextE || nextE.imageId !== imageId || nextE.tileId !== startTile + count) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
e = nextE;
|
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
col--;
|
col--;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage';
|
import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage';
|
||||||
import { IImageAddonOptions, ITerminalExt } from './Types';
|
import { IImageAddonOptions, ITerminalExt, IAddImageOpts } from './Types';
|
||||||
import { ImageRenderer } from './ImageRenderer';
|
import { ImageRenderer } from './ImageRenderer';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,6 +15,7 @@ import { ImageRenderer } from './ImageRenderer';
|
|||||||
* - advanceCursor for empty sixels carrying only height
|
* - advanceCursor for empty sixels carrying only height
|
||||||
*/
|
*/
|
||||||
export class SixelImageStorage {
|
export class SixelImageStorage {
|
||||||
|
private _addImageOpts: IAddImageOpts = { scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'vt340' };
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _storage: ImageStorage,
|
private readonly _storage: ImageStorage,
|
||||||
private readonly _opts: IImageAddonOptions,
|
private readonly _opts: IImageAddonOptions,
|
||||||
@@ -27,7 +28,8 @@ export class SixelImageStorage {
|
|||||||
* Cursor behavior depends on the sixelScrolling option (DECSET 80).
|
* Cursor behavior depends on the sixelScrolling option (DECSET 80).
|
||||||
*/
|
*/
|
||||||
public addImage(img: HTMLCanvasElement | ImageBitmap): void {
|
public addImage(img: HTMLCanvasElement | ImageBitmap): void {
|
||||||
this._storage.addImage(img, this._opts.sixelScrolling);
|
this._addImageOpts.scrolling = this._opts.sixelScrolling;
|
||||||
|
this._storage.addImage(img, this._addImageOpts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -100,6 +100,14 @@ export interface ICellSize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ImageLayer = 'top' | 'bottom';
|
export type ImageLayer = 'top' | 'bottom';
|
||||||
|
export type CursorPos = 'vt340' | 'iip';
|
||||||
|
|
||||||
|
export interface IAddImageOpts {
|
||||||
|
scrolling: boolean;
|
||||||
|
layer: ImageLayer;
|
||||||
|
zIndex: number;
|
||||||
|
cursorPos: CursorPos;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IImageSpec {
|
export interface IImageSpec {
|
||||||
orig: HTMLCanvasElement | ImageBitmap | undefined;
|
orig: HTMLCanvasElement | ImageBitmap | undefined;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { IDisposable } from '@xterm/xterm';
|
import { IDisposable } from '@xterm/xterm';
|
||||||
import { ImageStorage } from '../ImageStorage';
|
import { ImageStorage } from '../ImageStorage';
|
||||||
import { ImageLayer } from '../Types';
|
import { ImageLayer, IAddImageOpts } from '../Types';
|
||||||
import { IKittyImageData } from './KittyGraphicsTypes';
|
import { IKittyImageData } from './KittyGraphicsTypes';
|
||||||
|
|
||||||
// Kitty-specific image storage controller.
|
// Kitty-specific image storage controller.
|
||||||
@@ -42,6 +42,7 @@ export class KittyImageStorage implements IDisposable {
|
|||||||
this._images.delete(kittyId);
|
this._images.delete(kittyId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private _addImageOpts: IAddImageOpts = { scrolling: true, layer: 'top', zIndex: 0, cursorPos: 'iip' };
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _storage: ImageStorage
|
private readonly _storage: ImageStorage
|
||||||
@@ -98,7 +99,10 @@ export class KittyImageStorage implements IDisposable {
|
|||||||
if (oldStorageId !== undefined) {
|
if (oldStorageId !== undefined) {
|
||||||
this._storageIdToKittyId.delete(oldStorageId);
|
this._storageIdToKittyId.delete(oldStorageId);
|
||||||
}
|
}
|
||||||
const storageId = this._storage.addImage(image, scrolling, layer, zIndex);
|
this._addImageOpts.scrolling = scrolling;
|
||||||
|
this._addImageOpts.layer = layer;
|
||||||
|
this._addImageOpts.zIndex = zIndex;
|
||||||
|
const storageId = this._storage.addImage(image, this._addImageOpts);
|
||||||
this._kittyIdToStorageId.set(kittyId, storageId);
|
this._kittyIdToStorageId.set(kittyId, storageId);
|
||||||
this._storageIdToKittyId.set(storageId, kittyId);
|
this._storageIdToKittyId.set(storageId, kittyId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,6 +290,67 @@ test.describe('ImageAddon', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe('IIP support', () => {
|
||||||
|
test('size params is optional/informative', async () => {
|
||||||
|
// too small size param
|
||||||
|
await ctx.proxy.write(`\x1b]1337;File=inline=1;size=5:${PALETTE_PNG_BASE64}\x07`);
|
||||||
|
deepStrictEqual(await getOrigSize(1), [640, 80]);
|
||||||
|
// no size aparm at all
|
||||||
|
await ctx.proxy.write(`\x1b[10H\x1b]1337;File=inline=1:${PALETTE_QOI_BASE64}\x07`);
|
||||||
|
deepStrictEqual(await getOrigSize(2), [640, 80]);
|
||||||
|
});
|
||||||
|
test.skip('FilePart support - bytewise', async () => {
|
||||||
|
// opener
|
||||||
|
await ctx.proxy.write(`\x1b]1337;MultipartFile=inline=1\x07`);
|
||||||
|
// byte-wise
|
||||||
|
for (let i = 0; i < PALETTE_PNG_BASE64.length; ++i) {
|
||||||
|
await ctx.proxy.write(`\x1b]1337;FilePart=${PALETTE_PNG_BASE64[i]}\x07`);
|
||||||
|
}
|
||||||
|
// finalizer
|
||||||
|
await ctx.proxy.write(`\x1b]1337;FileEnd\x07`);
|
||||||
|
deepStrictEqual(await getOrigSize(1), [640, 80]);
|
||||||
|
});
|
||||||
|
test('FilePart support - chunks', async () => {
|
||||||
|
// opener
|
||||||
|
await ctx.proxy.write(`\x1b]1337;MultipartFile=inline=1\x07`);
|
||||||
|
// chunks
|
||||||
|
for (let i = 0; i < PALETTE_PNG_BASE64.length; i += 256) {
|
||||||
|
await ctx.proxy.write(`\x1b]1337;FilePart=${PALETTE_PNG_BASE64.slice(i, i + 256)}\x07`);
|
||||||
|
}
|
||||||
|
// finalizer
|
||||||
|
await ctx.proxy.write(`\x1b]1337;FileEnd\x07`);
|
||||||
|
deepStrictEqual(await getOrigSize(1), [640, 80]);
|
||||||
|
});
|
||||||
|
test('FilePart support - edgecases', async () => {
|
||||||
|
// multiple opener do not harm
|
||||||
|
for (let i = 0; i < 10; ++i) {
|
||||||
|
await ctx.proxy.write(`\x1b]1337;MultipartFile=inline=1\x07`);
|
||||||
|
}
|
||||||
|
// multiple finalizer do not harm
|
||||||
|
for (let i = 0; i < 5; ++i) {
|
||||||
|
await ctx.proxy.write(`\x1b]1337;FileEnd\x07`);
|
||||||
|
}
|
||||||
|
// chunks w'o opener are ignored
|
||||||
|
for (let i = 0; i < PALETTE_PNG_BASE64.length; i += 256) {
|
||||||
|
await ctx.proxy.write(`\x1b]1337;FilePart=${PALETTE_PNG_BASE64.slice(i, i + 256)}\x07`);
|
||||||
|
}
|
||||||
|
await ctx.proxy.write(`\x1b]1337;FileEnd\x07`);
|
||||||
|
pollFor(ctx.page, 'window.imageAddon._storage._images.size', 0);
|
||||||
|
});
|
||||||
|
test('FilePart support - other IIP sequence cancels chunked image', async () => {
|
||||||
|
// opener
|
||||||
|
await ctx.proxy.write(`\x1b]1337;MultipartFile=inline=1\x07`);
|
||||||
|
// chunks
|
||||||
|
for (let i = 0; i < PALETTE_PNG_BASE64.length; i += 256) {
|
||||||
|
await ctx.proxy.write(`\x1b]1337;FilePart=${PALETTE_PNG_BASE64.slice(i, i + 256)}\x07`);
|
||||||
|
}
|
||||||
|
// finalizer skipped
|
||||||
|
// write spinfox.png
|
||||||
|
await ctx.proxy.write(TESTDATA_IIP[1][0]);
|
||||||
|
deepStrictEqual(await getOrigSize(1), TESTDATA_IIP[1][1]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test.describe('IIP support - testimages', () => {
|
test.describe('IIP support - testimages', () => {
|
||||||
test('palette.png', async () => {
|
test('palette.png', async () => {
|
||||||
await ctx.proxy.write(TESTDATA_IIP[0][0]);
|
await ctx.proxy.write(TESTDATA_IIP[0][0]);
|
||||||
|
|||||||
@@ -539,7 +539,6 @@ test.describe('Kitty Graphics Protocol', () => {
|
|||||||
|
|
||||||
test.describe('Query support (a=q)', () => {
|
test.describe('Query support (a=q)', () => {
|
||||||
test('responds with OK for capability query without payload', async () => {
|
test('responds with OK for capability query without payload', async () => {
|
||||||
let response = '';
|
|
||||||
await ctx.page.evaluate(() => {
|
await ctx.page.evaluate(() => {
|
||||||
(window as any).kittyResponse = '';
|
(window as any).kittyResponse = '';
|
||||||
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
|
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
|
||||||
@@ -548,12 +547,11 @@ test.describe('Kitty Graphics Protocol', () => {
|
|||||||
await ctx.proxy.write('\x1b_Gi=31,a=q;\x1b\\');
|
await ctx.proxy.write('\x1b_Gi=31,a=q;\x1b\\');
|
||||||
await timeout(100);
|
await timeout(100);
|
||||||
|
|
||||||
response = await ctx.page.evaluate('window.kittyResponse');
|
const response = await ctx.page.evaluate('window.kittyResponse');
|
||||||
strictEqual(response, '\x1b_Gi=31;OK\x1b\\');
|
strictEqual(response, '\x1b_Gi=31;OK\x1b\\');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('responds with OK for valid PNG query', async () => {
|
test('responds with OK for valid PNG query', async () => {
|
||||||
let response = '';
|
|
||||||
await ctx.page.evaluate(() => {
|
await ctx.page.evaluate(() => {
|
||||||
(window as any).kittyResponse = '';
|
(window as any).kittyResponse = '';
|
||||||
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
|
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
|
||||||
@@ -562,7 +560,7 @@ test.describe('Kitty Graphics Protocol', () => {
|
|||||||
await ctx.proxy.write(`\x1b_Gi=42,a=q,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
|
await ctx.proxy.write(`\x1b_Gi=42,a=q,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
|
||||||
await timeout(100);
|
await timeout(100);
|
||||||
|
|
||||||
response = await ctx.page.evaluate('window.kittyResponse');
|
const response = await ctx.page.evaluate('window.kittyResponse');
|
||||||
strictEqual(response, '\x1b_Gi=42;OK\x1b\\');
|
strictEqual(response, '\x1b_Gi=42;OK\x1b\\');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -578,7 +576,6 @@ test.describe('Kitty Graphics Protocol', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('responds with error for invalid base64', async () => {
|
test('responds with error for invalid base64', async () => {
|
||||||
let response = '';
|
|
||||||
await ctx.page.evaluate(() => {
|
await ctx.page.evaluate(() => {
|
||||||
(window as any).kittyResponse = '';
|
(window as any).kittyResponse = '';
|
||||||
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
|
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
|
||||||
@@ -587,12 +584,11 @@ test.describe('Kitty Graphics Protocol', () => {
|
|||||||
await ctx.proxy.write('\x1b_Gi=60,a=q,f=100;!!!invalid!!!\x1b\\');
|
await ctx.proxy.write('\x1b_Gi=60,a=q,f=100;!!!invalid!!!\x1b\\');
|
||||||
await timeout(100);
|
await timeout(100);
|
||||||
|
|
||||||
response = await ctx.page.evaluate('window.kittyResponse');
|
const response: string = await ctx.page.evaluate('window.kittyResponse');
|
||||||
strictEqual(response.startsWith('\x1b_Gi=60;EINVAL:'), true);
|
strictEqual(response.startsWith('\x1b_Gi=60;EINVAL:'), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('responds with error for RGB data without dimensions', async () => {
|
test('responds with error for RGB data without dimensions', async () => {
|
||||||
let response = '';
|
|
||||||
await ctx.page.evaluate(() => {
|
await ctx.page.evaluate(() => {
|
||||||
(window as any).kittyResponse = '';
|
(window as any).kittyResponse = '';
|
||||||
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
|
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
|
||||||
@@ -601,7 +597,7 @@ test.describe('Kitty Graphics Protocol', () => {
|
|||||||
await ctx.proxy.write('\x1b_Gi=70,a=q,f=24;AAAA\x1b\\');
|
await ctx.proxy.write('\x1b_Gi=70,a=q,f=24;AAAA\x1b\\');
|
||||||
await timeout(100);
|
await timeout(100);
|
||||||
|
|
||||||
response = await ctx.page.evaluate('window.kittyResponse');
|
const response = await ctx.page.evaluate('window.kittyResponse');
|
||||||
strictEqual(response, '\x1b_Gi=70;EINVAL:width and height required for raw pixel data\x1b\\');
|
strictEqual(response, '\x1b_Gi=70;EINVAL:width and height required for raw pixel data\x1b\\');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -642,7 +638,6 @@ test.describe('Kitty Graphics Protocol', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('responds with EINVAL when both i and I keys are specified', async () => {
|
test('responds with EINVAL when both i and I keys are specified', async () => {
|
||||||
let response = '';
|
|
||||||
await ctx.page.evaluate(() => {
|
await ctx.page.evaluate(() => {
|
||||||
(window as any).kittyResponse = '';
|
(window as any).kittyResponse = '';
|
||||||
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
|
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
|
||||||
@@ -652,12 +647,11 @@ test.describe('Kitty Graphics Protocol', () => {
|
|||||||
await ctx.proxy.write(`\x1b_Gi=100,I=200,a=q,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
|
await ctx.proxy.write(`\x1b_Gi=100,I=200,a=q,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
|
||||||
await timeout(100);
|
await timeout(100);
|
||||||
|
|
||||||
response = await ctx.page.evaluate('window.kittyResponse');
|
const response = await ctx.page.evaluate('window.kittyResponse');
|
||||||
strictEqual(response, '\x1b_Gi=100;EINVAL:cannot specify both i and I keys\x1b\\');
|
strictEqual(response, '\x1b_Gi=100;EINVAL:cannot specify both i and I keys\x1b\\');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('responds with EINVAL for i+I conflict even without payload', async () => {
|
test('responds with EINVAL for i+I conflict even without payload', async () => {
|
||||||
let response = '';
|
|
||||||
await ctx.page.evaluate(() => {
|
await ctx.page.evaluate(() => {
|
||||||
(window as any).kittyResponse = '';
|
(window as any).kittyResponse = '';
|
||||||
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
|
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
|
||||||
@@ -667,7 +661,7 @@ test.describe('Kitty Graphics Protocol', () => {
|
|||||||
await ctx.proxy.write('\x1b_Gi=101,I=201,a=d\x1b\\');
|
await ctx.proxy.write('\x1b_Gi=101,I=201,a=d\x1b\\');
|
||||||
await timeout(100);
|
await timeout(100);
|
||||||
|
|
||||||
response = await ctx.page.evaluate('window.kittyResponse');
|
const response = await ctx.page.evaluate('window.kittyResponse');
|
||||||
strictEqual(response, '\x1b_Gi=101;EINVAL:cannot specify both i and I keys\x1b\\');
|
strictEqual(response, '\x1b_Gi=101;EINVAL:cannot specify both i and I keys\x1b\\');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,15 +31,11 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lru-cache": "^6.0.0",
|
"lru-cache": "^11.3.6",
|
||||||
"opentype.js": "^0.8.0"
|
"opentype.js": "^2.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/lru-cache": "^5.1.0",
|
"@types/opentype.js": "^1.3.9",
|
||||||
"@types/opentype.js": "^0.7.0",
|
"font-finder": "^1.1.0"
|
||||||
"axios": "^1.15.2",
|
|
||||||
"font-finder": "^1.1.0",
|
|
||||||
"mkdirp": "0.5.5",
|
|
||||||
"yauzl": "^3.2.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as opentype from 'opentype.js';
|
import * as opentype from 'opentype.js';
|
||||||
import LRUCache = require('lru-cache');
|
import { LRUCache } from 'lru-cache';
|
||||||
|
|
||||||
import { IFont, ILigatureData, IFlattenedLookupTree, ILookupTree, IOptions } from './types';
|
import { IFont, ILigatureData, IFlattenedLookupTree, ILookupTree, IOptions } from './types';
|
||||||
import mergeTrees from './merge';
|
import mergeTrees from './merge';
|
||||||
@@ -23,8 +23,8 @@ class FontImpl implements IFont {
|
|||||||
|
|
||||||
if (options.cacheSize > 0) {
|
if (options.cacheSize > 0) {
|
||||||
this._cache = new LRUCache({
|
this._cache = new LRUCache({
|
||||||
max: options.cacheSize,
|
maxSize: options.cacheSize,
|
||||||
length: ((val: ILigatureData | [number, number][], key: string) => key.length) as any
|
sizeCalculation: ((val: ILigatureData | [number, number][], key: string) => key.length) as any
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ export default function mergeRange(ranges: [number, number][], newRangeStart: nu
|
|||||||
// current range
|
// current range
|
||||||
ranges[i - 1][1] = Math.max(newRangeEnd, range[1]);
|
ranges[i - 1][1] = Math.max(newRangeEnd, range[1]);
|
||||||
ranges.splice(i, 1);
|
ranges.splice(i, 1);
|
||||||
inRange = false;
|
|
||||||
return ranges;
|
return ranges;
|
||||||
}
|
}
|
||||||
// Case 7: New range extends from previous range past the
|
// Case 7: New range extends from previous range past the
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ export class SearchEngine {
|
|||||||
this._terminal.clearSelection();
|
this._terminal.clearSelection();
|
||||||
|
|
||||||
let startRow = this._terminal.buffer.active.baseY + this._terminal.rows - 1;
|
let startRow = this._terminal.buffer.active.baseY + this._terminal.rows - 1;
|
||||||
let startCol = this._terminal.cols;
|
const startCol = this._terminal.cols;
|
||||||
const isReverseSearch = true;
|
const isReverseSearch = true;
|
||||||
|
|
||||||
this._lineCache.initLinesCache();
|
this._lineCache.initLinesCache();
|
||||||
@@ -186,14 +186,14 @@ export class SearchEngine {
|
|||||||
let result: ISearchResult | undefined;
|
let result: ISearchResult | undefined;
|
||||||
if (prevSelectedPos) {
|
if (prevSelectedPos) {
|
||||||
searchPosition.startRow = startRow = prevSelectedPos.start.y;
|
searchPosition.startRow = startRow = prevSelectedPos.start.y;
|
||||||
searchPosition.startCol = startCol = prevSelectedPos.start.x;
|
searchPosition.startCol = prevSelectedPos.start.x;
|
||||||
if (cachedSearchTerm !== term) {
|
if (cachedSearchTerm !== term) {
|
||||||
// Try to expand selection to right first.
|
// Try to expand selection to right first.
|
||||||
result = this._findInLine(term, searchPosition, searchOptions, false);
|
result = this._findInLine(term, searchPosition, searchOptions, false);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
// If selection was not able to be expanded to the right, then try reverse search
|
// If selection was not able to be expanded to the right, then try reverse search
|
||||||
searchPosition.startRow = startRow = prevSelectedPos.end.y;
|
searchPosition.startRow = startRow = prevSelectedPos.end.y;
|
||||||
searchPosition.startCol = startCol = prevSelectedPos.end.x;
|
searchPosition.startCol = prevSelectedPos.end.x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
"rootDir": "..",
|
"rootDir": "..",
|
||||||
"outDir": "../out-benchmark",
|
"outDir": "../out-benchmark",
|
||||||
"types": ["../../../node_modules/@types/node"],
|
"types": ["../../../node_modules/@types/node"],
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "nodenext",
|
||||||
"strict": false,
|
"strict": false,
|
||||||
"target": "es2021",
|
"target": "es2021",
|
||||||
"module": "commonjs",
|
"module": "nodenext",
|
||||||
"paths": {
|
"paths": {
|
||||||
"common/*": ["../../../src/common/*"],
|
"common/*": ["../../../src/common/*"],
|
||||||
"browser/*": ["../../../src/browser/*"],
|
"browser/*": ["../../../src/browser/*"],
|
||||||
|
|||||||
@@ -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_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json",
|
"benchmark": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json",
|
||||||
"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-baseline": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline 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"
|
"benchmark-eval": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
"rootDir": "..",
|
"rootDir": "..",
|
||||||
"outDir": "../out-benchmark",
|
"outDir": "../out-benchmark",
|
||||||
"types": ["../../../node_modules/@types/node"],
|
"types": ["../../../node_modules/@types/node"],
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "nodenext",
|
||||||
"strict": false,
|
"strict": false,
|
||||||
"target": "es2015",
|
"target": "es2015",
|
||||||
"module": "commonjs",
|
"module": "nodenext",
|
||||||
"paths": {
|
"paths": {
|
||||||
"common/*": ["../../../src/common/*"],
|
"common/*": ["../../../src/common/*"],
|
||||||
"browser/*": ["../../../src/browser/*"],
|
"browser/*": ["../../../src/browser/*"],
|
||||||
|
|||||||
@@ -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_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json out-benchmark/benchmark/*benchmark.js",
|
"benchmark": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json 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-baseline": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline 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"
|
"benchmark-eval": "../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,22 @@ test.describe('WebFontsAddon', () => {
|
|||||||
const data = await ctx.page.evaluate(`
|
const data = await ctx.page.evaluate(`
|
||||||
document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/fonts/kongtext.regular.ttf) format('truetype'); unicode-range: U+00A0-00FF}", 0);
|
document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/fonts/kongtext.regular.ttf) format('truetype'); unicode-range: U+00A0-00FF}", 0);
|
||||||
`);
|
`);
|
||||||
deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]);
|
let fonts = await getDocumentFonts();
|
||||||
|
// safari may have loaded the font, firefox & chrome never load it
|
||||||
|
if (browser.browserType().name() === 'webkit') {
|
||||||
|
try {
|
||||||
|
deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'loading' }]);
|
||||||
|
} catch (e) {
|
||||||
|
try {
|
||||||
|
deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'loaded' }]);
|
||||||
|
} catch (e) {
|
||||||
|
deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'unloaded' }]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'unloaded' }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// broken case: webfont in ctor without addon usage
|
// broken case: webfont in ctor without addon usage
|
||||||
await ctx.page.evaluate(`
|
await ctx.page.evaluate(`
|
||||||
@@ -86,15 +101,20 @@ test.describe('WebFontsAddon', () => {
|
|||||||
window.helperTerm.open(term.element);
|
window.helperTerm.open(term.element);
|
||||||
`);
|
`);
|
||||||
|
|
||||||
// safari loads the font, firefox & chrome dont
|
fonts = await getDocumentFonts();
|
||||||
|
// safari might load the font, firefox & chrome dont
|
||||||
if (browser.browserType().name() === 'webkit') {
|
if (browser.browserType().name() === 'webkit') {
|
||||||
try {
|
try {
|
||||||
deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loading' }]);
|
deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'loading' }]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }]);
|
try {
|
||||||
|
deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'loaded' }]);
|
||||||
|
} catch (e) {
|
||||||
|
deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'unloaded' }]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]);
|
deepStrictEqual(fonts, [{ family: 'Kongtext', status: 'unloaded' }]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// good case: addon fixes layout for webfont in ctor
|
// good case: addon fixes layout for webfont in ctor
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
"lib": [
|
"lib": [
|
||||||
"es2021",
|
"es2021",
|
||||||
],
|
],
|
||||||
// "downlevelIteration": true,
|
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"outDir": "../out-test",
|
"outDir": "../out-test",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
|
|||||||
@@ -113,8 +113,8 @@ export class LinkComputer {
|
|||||||
let line: IBufferLine | undefined;
|
let line: IBufferLine | undefined;
|
||||||
let topIdx = lineIndex;
|
let topIdx = lineIndex;
|
||||||
let bottomIdx = lineIndex;
|
let bottomIdx = lineIndex;
|
||||||
let length = 0;
|
let length;
|
||||||
let content = '';
|
let content;
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
|
|
||||||
if ((line = terminal.buffer.active.getLine(lineIndex))) {
|
if ((line = terminal.buffer.active.getLine(lineIndex))) {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
"lib": [
|
"lib": [
|
||||||
"es2021",
|
"es2021",
|
||||||
],
|
],
|
||||||
// "downlevelIteration": true,
|
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"outDir": "../out-test",
|
"outDir": "../out-test",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
|
|||||||
@@ -427,8 +427,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
|||||||
let line: IBufferLine;
|
let line: IBufferLine;
|
||||||
let joinedRanges: [number, number][];
|
let joinedRanges: [number, number][];
|
||||||
let isJoined: boolean;
|
let isJoined: boolean;
|
||||||
let skipJoinedCheckUntilX: number = 0;
|
let skipJoinedCheckUntilX: number;
|
||||||
let isValidJoinRange: boolean = true;
|
let isValidJoinRange: boolean;
|
||||||
let lastCharX: number;
|
let lastCharX: number;
|
||||||
let range: [number, number];
|
let range: [number, number];
|
||||||
let isCursorRow: boolean;
|
let isCursorRow: boolean;
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
/* eslint-disable max-len */
|
/* oxlint-disable @stylistic/max-len -- generated glyph path data */
|
||||||
|
|
||||||
const enum Shapes {
|
const enum Shapes {
|
||||||
/** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1',
|
/** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1',
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"downlevelIteration": true,
|
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"types": [
|
"types": [
|
||||||
"../../../node_modules/@types/mocha"
|
"../../../node_modules/@types/mocha"
|
||||||
|
|||||||
Executable
+86
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copyright (c) 2026 The xterm.js authors. All rights reserved.
|
||||||
|
# @license MIT
|
||||||
|
set -euo pipefail
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
cd "$ROOT"
|
||||||
|
|
||||||
|
if [[ ! -f src/common/parser/wasm/transition_table.h ]]; then
|
||||||
|
npm run build
|
||||||
|
node bin/gen_parser_transition_table.mjs
|
||||||
|
fi
|
||||||
|
|
||||||
|
WASM_DIR=src/common/parser/wasm
|
||||||
|
OUT="$WASM_DIR/parser.wasm"
|
||||||
|
|
||||||
|
# Apple Clang (Xcode CLT) has no wasm32 target; prefer Homebrew LLVM when needed.
|
||||||
|
wasm_clang_has_target() {
|
||||||
|
"$1" --print-targets 2>/dev/null | grep -q 'wasm32'
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_wasm_toolchain() {
|
||||||
|
local clang_bin=clang
|
||||||
|
if wasm_clang_has_target clang; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
local llvm_prefix=""
|
||||||
|
if command -v brew >/dev/null 2>&1; then
|
||||||
|
llvm_prefix="$(brew --prefix llvm 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
for prefix in "$llvm_prefix" /opt/homebrew/opt/llvm /usr/local/opt/llvm; do
|
||||||
|
if [[ -n "$prefix" && -x "$prefix/bin/clang" ]] && wasm_clang_has_target "$prefix/bin/clang"; then
|
||||||
|
clang_bin="$prefix/bin/clang"
|
||||||
|
local lld_prefix=""
|
||||||
|
if command -v brew >/dev/null 2>&1; then
|
||||||
|
lld_prefix="$(brew --prefix lld 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
for lld_dir in "$lld_prefix/bin" /opt/homebrew/opt/lld/bin /usr/local/opt/lld/bin; do
|
||||||
|
if [[ -n "$lld_dir" && -x "$lld_dir/lld" ]]; then
|
||||||
|
PATH="$lld_dir:$(dirname "$clang_bin"):$PATH"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
PATH="$(dirname "$clang_bin"):$PATH"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ! wasm_clang_has_target "$clang_bin"; then
|
||||||
|
echo "error: no clang with wasm32 support found (macOS: brew install llvm lld)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
WASM_CLANG="$clang_bin"
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_wasm_toolchain
|
||||||
|
|
||||||
|
"$WASM_CLANG" --target=wasm32-unknown-unknown -O2 -nostdlib -fuse-ld=lld \
|
||||||
|
-Wl,--no-entry \
|
||||||
|
-Wl,--export-memory \
|
||||||
|
-Wl,--export=reset \
|
||||||
|
-Wl,--export=scan \
|
||||||
|
-Wl,--export=probe_action \
|
||||||
|
-Wl,--export=set_state \
|
||||||
|
-Wl,--export=sync_params_from \
|
||||||
|
-Wl,--export=export_params_to \
|
||||||
|
-Wl,--export=get_params_len \
|
||||||
|
-Wl,--export=get_subparams_len \
|
||||||
|
-Wl,--export=get_subparams_ptr \
|
||||||
|
-Wl,--export=get_subparams_idx_ptr \
|
||||||
|
-Wl,--export=get_input_ptr \
|
||||||
|
-Wl,--export=get_kinds_ptr \
|
||||||
|
-Wl,--export=get_starts_ptr \
|
||||||
|
-Wl,--export=get_lengths_ptr \
|
||||||
|
-Wl,--export=get_aux_ptr \
|
||||||
|
-Wl,--export=get_param_starts_ptr \
|
||||||
|
-Wl,--export=get_param_counts_ptr \
|
||||||
|
-Wl,--export=get_params_ptr \
|
||||||
|
-Wl,--export=get_state_ptr \
|
||||||
|
-Wl,--export=get_header_ptr \
|
||||||
|
-Wl,--initial-memory=67108864 \
|
||||||
|
-Wl,--max-memory=67108864 \
|
||||||
|
"$WASM_DIR/parser.c" -o "$OUT"
|
||||||
|
|
||||||
|
node bin/embed_parser_wasm.mjs
|
||||||
|
|
||||||
|
echo "Built $OUT"
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { readFileSync, writeFileSync } from 'fs';
|
||||||
|
import { join, dirname } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const root = join(__dirname, '..');
|
||||||
|
const wasm = readFileSync(join(root, 'src/common/parser/wasm/parser.wasm'));
|
||||||
|
const b64 = wasm.toString('base64');
|
||||||
|
|
||||||
|
const out = `/**
|
||||||
|
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
/** Base64-encoded parser.wasm (generated by bin/build_parser_wasm.sh) */
|
||||||
|
export const PARSER_WASM_BASE64 = '${b64}';\n`;
|
||||||
|
|
||||||
|
writeFileSync(join(root, 'src/common/parser/wasm/parser.wasm.embed.ts'), out);
|
||||||
|
console.info(`Embedded parser.wasm (${wasm.length} bytes)`);
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 The xterm.js authors. All rights reserved.
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
// @ts-check
|
||||||
|
import { writeFileSync } from 'fs';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import { dirname, join } from 'path';
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const root = join(__dirname, '..');
|
||||||
|
|
||||||
|
// Import compiled transition table
|
||||||
|
const { VT500_TRANSITION_TABLE } = await import(join(root, 'out/common/parser/EscapeSequenceParser.js'));
|
||||||
|
|
||||||
|
const table = VT500_TRANSITION_TABLE.table;
|
||||||
|
let out = '/* Auto-generated by bin/gen_parser_transition_table.mjs - do not edit */\n';
|
||||||
|
out += `#ifndef PARSER_TRANSITION_TABLE_H\n#define PARSER_TRANSITION_TABLE_H\n\n`;
|
||||||
|
out += `#include <stdint.h>\n\n`;
|
||||||
|
out += `#define PARSER_TABLE_SIZE ${table.length}\n\n`;
|
||||||
|
out += `static const uint16_t PARSER_TRANSITION_TABLE[PARSER_TABLE_SIZE] = {\n`;
|
||||||
|
for (let i = 0; i < table.length; i++) {
|
||||||
|
if (i % 16 === 0) out += ' ';
|
||||||
|
out += `0x${table[i].toString(16)}`;
|
||||||
|
out += i < table.length - 1 ? ', ' : '';
|
||||||
|
if (i % 16 === 15) out += '\n';
|
||||||
|
}
|
||||||
|
if (table.length % 16 !== 0) out += '\n';
|
||||||
|
out += `};\n\n#endif\n`;
|
||||||
|
|
||||||
|
const headerPath = join(root, 'src/common/parser/wasm/transition_table.h');
|
||||||
|
writeFileSync(headerPath, out);
|
||||||
|
console.info(`Wrote ${headerPath} (${table.length} entries)`);
|
||||||
+41
-11
@@ -33,17 +33,47 @@ if (files.length === 0) {
|
|||||||
|
|
||||||
console.log(`Linting ${files.length} changed file(s)...`);
|
console.log(`Linting ${files.length} changed file(s)...`);
|
||||||
|
|
||||||
const eslintArgs = ['--max-warnings', '0', '--no-warn-ignored'];
|
const cwd = path.join(__dirname, '..');
|
||||||
if (fix) {
|
const isWin = process.platform === 'win32';
|
||||||
eslintArgs.push('--fix');
|
|
||||||
|
/**
|
||||||
|
* @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}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
eslintArgs.push(...files);
|
|
||||||
|
|
||||||
const eslint = process.platform === 'win32' ? 'eslint.cmd' : 'eslint';
|
async function main() {
|
||||||
const child = spawn(eslint, eslintArgs, {
|
const oxlintArgs = ['--type-aware', '--max-warnings', '0'];
|
||||||
stdio: 'inherit',
|
if (fix) {
|
||||||
cwd: path.join(__dirname, '..'),
|
oxlintArgs.push('--fix');
|
||||||
shell: process.platform === 'win32'
|
}
|
||||||
|
oxlintArgs.push(...files);
|
||||||
|
|
||||||
|
const oxlint = isWin ? 'oxlint.cmd' : 'oxlint';
|
||||||
|
await run(oxlint, oxlintArgs);
|
||||||
|
|
||||||
|
const eslintArgs = ['--config', 'eslint.config.naming.mjs', '--max-warnings', '0', ...files];
|
||||||
|
const eslint = isWin ? 'eslint.cmd' : 'eslint';
|
||||||
|
await run(eslint, eslintArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(err => {
|
||||||
|
console.error(err.message);
|
||||||
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
child.on('close', code => process.exit(code ?? 0));
|
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ const path = require('path');
|
|||||||
|
|
||||||
const COVERAGE_LINES_THRESHOLD = 40;
|
const COVERAGE_LINES_THRESHOLD = 40;
|
||||||
|
|
||||||
// Add `out` to the NODE_PATH so absolute paths can be resolved.
|
// Add `out-esbuild` to NODE_PATH so addon path-mapped imports (common/*, browser/*) resolve.
|
||||||
const env = { ...process.env };
|
const env = { ...process.env };
|
||||||
env.NODE_PATH = path.resolve(__dirname, '../out-esbuild');
|
env.NODE_PATH = path.resolve(__dirname, '../out-esbuild');
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ export class AddonImageWindow extends BaseWindow implements IControlWindow {
|
|||||||
const dtIip = document.createElement('dt');
|
const dtIip = document.createElement('dt');
|
||||||
dtIip.textContent = 'IIP (iTerm)';
|
dtIip.textContent = 'IIP (iTerm)';
|
||||||
dlIip.appendChild(dtIip);
|
dlIip.appendChild(dtIip);
|
||||||
this._addDdWithButton(dlIip, 'image-demo3', 'palette');
|
this._addDdWithButton(dlIip, 'image-demo3', 'palette (png File)');
|
||||||
|
this._addDdWithButton(dlIip, 'image-demo4', 'dice (qoi MultipartFile)');
|
||||||
container.appendChild(dlIip);
|
container.appendChild(dlIip);
|
||||||
|
|
||||||
// Kitty demos
|
// Kitty demos
|
||||||
@@ -141,6 +142,21 @@ export class AddonImageWindow extends BaseWindow implements IControlWindow {
|
|||||||
this._terminal.write(`\x1b]1337;File=inline=1;size=${data.length}:${btoa(sdata)}\x1b\\`);
|
this._terminal.write(`\x1b]1337;File=inline=1;size=${data.length}:${btoa(sdata)}\x1b\\`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const iipDemoMulti = (url: string) => () => fetch(url)
|
||||||
|
.then(resp => resp.arrayBuffer())
|
||||||
|
.then(buffer => {
|
||||||
|
const data = new Uint8Array(buffer);
|
||||||
|
let sdata = '';
|
||||||
|
for (let i = 0; i < data.length; ++i) sdata += String.fromCharCode(data[i]);
|
||||||
|
const encoded = btoa(sdata);
|
||||||
|
this._terminal.write('\r\n');
|
||||||
|
this._terminal.write(`\x1b]1337;MultipartFile=inline=1\x1b\\`);
|
||||||
|
for (let i = 0; i < encoded.length; i += 100) {
|
||||||
|
this._terminal.write(`\x1b]1337;FilePart=${encoded.slice(i, i + 100)}\x1b\\`);
|
||||||
|
}
|
||||||
|
this._terminal.write(`\x1b]1337;FileEnd\x1b\\`);
|
||||||
|
});
|
||||||
|
|
||||||
const kittyDemo = (url: string) => () => fetch(url)
|
const kittyDemo = (url: string) => () => fetch(url)
|
||||||
.then(resp => resp.arrayBuffer())
|
.then(resp => resp.arrayBuffer())
|
||||||
.then(buffer => {
|
.then(buffer => {
|
||||||
@@ -158,6 +174,8 @@ export class AddonImageWindow extends BaseWindow implements IControlWindow {
|
|||||||
sixelDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/testfiles/test2.sixel'));
|
sixelDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/testfiles/test2.sixel'));
|
||||||
document.getElementById('image-demo3')!.addEventListener('click',
|
document.getElementById('image-demo3')!.addEventListener('click',
|
||||||
iipDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/palette.png'));
|
iipDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/palette.png'));
|
||||||
|
document.getElementById('image-demo4')!.addEventListener('click',
|
||||||
|
iipDemoMulti('https://raw.githubusercontent.com/jerch/xterm-wasm-parts/master/fixtures/qoi/dice.qoi'));
|
||||||
document.getElementById('image-demo-kitty1')!.addEventListener('click',
|
document.getElementById('image-demo-kitty1')!.addEventListener('click',
|
||||||
kittyDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/palette.png'));
|
kittyDemo('https://raw.githubusercontent.com/jerch/node-sixel/master/palette.png'));
|
||||||
|
|
||||||
@@ -176,7 +194,7 @@ export class AddonImageWindow extends BaseWindow implements IControlWindow {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const pos = (this._terminal as any)._core._mouseService!.getCoords(ev, (this._terminal as any)._core.screenElement!, this._terminal.cols, this._terminal.rows);
|
const pos = (this._terminal as any)._core._mouseCoordsService!.getCoords(ev, (this._terminal as any)._core.screenElement!, this._terminal.cols, this._terminal.rows);
|
||||||
const x = pos[0] - 1;
|
const x = pos[0] - 1;
|
||||||
const y = pos[1] - 1;
|
const y = pos[1] - 1;
|
||||||
const canvas = ev.shiftKey
|
const canvas = ev.shiftKey
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export class GpuWindow extends BaseWindow implements IControlWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _styleAtlasPage(canvas: HTMLCanvasElement): void {
|
private _styleAtlasPage(canvas: HTMLCanvasElement): void {
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// oxlint-disable-next-line eslint-js/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`;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export class WebglWindow extends BaseWindow implements IControlWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _styleAtlasPage(canvas: HTMLCanvasElement): void {
|
private _styleAtlasPage(canvas: HTMLCanvasElement): void {
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
// oxlint-disable-next-line eslint-js/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`;
|
||||||
|
|||||||
+42
-13
@@ -2,25 +2,54 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"target": "es2021",
|
"target": "es2021",
|
||||||
|
"types": [
|
||||||
|
"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": ["../../addons/addon-attach"],
|
"@xterm/addon-attach": [
|
||||||
"@xterm/addon-clipboard": ["../../addons/addon-clipboard"],
|
"../../addons/addon-attach"
|
||||||
"@xterm/addon-fit": ["../../addons/addon-fit"],
|
],
|
||||||
"@xterm/addon-image": ["../../addons/addon-image"],
|
"@xterm/addon-clipboard": [
|
||||||
"@xterm/addon-progress": ["../../addons/addon-progress"],
|
"../../addons/addon-clipboard"
|
||||||
"@xterm/addon-search": ["../../addons/addon-search"],
|
],
|
||||||
"@xterm/addon-serialize": ["../../addons/addon-serialize"],
|
"@xterm/addon-fit": [
|
||||||
"@xterm/addon-web-fonts": ["../../addons/addon-web-fonts"],
|
"../../addons/addon-fit"
|
||||||
"@xterm/addon-web-links": ["../../addons/addon-web-links"],
|
],
|
||||||
"@xterm/addon-webgl": ["../../addons/addon-webgl"],
|
"@xterm/addon-image": [
|
||||||
"@xterm/addon-unicode11": ["../../addons/addon-unicode11"],
|
"../../addons/addon-image"
|
||||||
"@xterm/addon-unicode-graphemes": ["../../addons/addon-unicode-graphemes"],
|
],
|
||||||
"@xterm/addon-ligatures": ["../../addons/addon-ligatures"],
|
"@xterm/addon-progress": [
|
||||||
|
"../../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"
|
||||||
|
],
|
||||||
"*": ["./*"]
|
"*": ["./*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ function startServer(): void {
|
|||||||
});
|
});
|
||||||
|
|
||||||
appWs.ws('/terminals/:pid', function (ws, req) {
|
appWs.ws('/terminals/:pid', function (ws, req) {
|
||||||
const term = terminals[parseInt(req.params.pid)];
|
const term = terminals[parseInt(Array.isArray(req.params.pid) ? req.params.pid[0] : req.params.pid)];
|
||||||
console.log('Connected to terminal ' + term.pid);
|
console.log('Connected to terminal ' + term.pid);
|
||||||
temporaryDisposable[term.pid].dispose();
|
temporaryDisposable[term.pid].dispose();
|
||||||
delete temporaryDisposable[term.pid];
|
delete temporaryDisposable[term.pid];
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@ export default tseslint.config(
|
|||||||
'@stylistic/comma-spacing': ['warn', { before: false, after: true }],
|
'@stylistic/comma-spacing': ['warn', { before: false, after: true }],
|
||||||
'@stylistic/indent': ['warn', 2],
|
'@stylistic/indent': ['warn', 2],
|
||||||
'@stylistic/semi': ['warn', 'always'],
|
'@stylistic/semi': ['warn', 'always'],
|
||||||
'@stylistic/quotes': ['warn', 'single', { allowTemplateLiterals: true }],
|
'@stylistic/quotes': ['warn', 'single', { allowTemplateLiterals: 'always' }],
|
||||||
'@stylistic/member-delimiter-style': ['warn', {
|
'@stylistic/member-delimiter-style': ['warn', {
|
||||||
multiline: { delimiter: 'semi', requireLast: true },
|
multiline: { delimiter: 'semi', requireLast: true },
|
||||||
singleline: { delimiter: 'comma', requireLast: false }
|
singleline: { delimiter: 'comma', requireLast: false }
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
// @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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -17,7 +17,7 @@ export default tseslint.config(
|
|||||||
rules: {
|
rules: {
|
||||||
'@stylistic/indent': ['warn', 2],
|
'@stylistic/indent': ['warn', 2],
|
||||||
'@stylistic/semi': ['warn', 'always'],
|
'@stylistic/semi': ['warn', 'always'],
|
||||||
'@stylistic/quotes': ['warn', 'single', { allowTemplateLiterals: true }],
|
'@stylistic/quotes': ['warn', 'single', { allowTemplateLiterals: 'always' }],
|
||||||
'@stylistic/member-delimiter-style': ['warn', {
|
'@stylistic/member-delimiter-style': ['warn', {
|
||||||
multiline: { delimiter: 'semi', requireLast: true },
|
multiline: { delimiter: 'semi', requireLast: true },
|
||||||
singleline: { delimiter: 'comma', requireLast: false }
|
singleline: { delimiter: 'comma', requireLast: false }
|
||||||
|
|||||||
Generated
+3113
-2288
File diff suppressed because it is too large
Load Diff
+36
-30
@@ -34,6 +34,7 @@
|
|||||||
"start": "node demo/start",
|
"start": "node demo/start",
|
||||||
"dev": "concurrently -k -p [{name}] -n tsc,esbuild,esbuild-demo-client,esbuild-demo-server,server -c blue,yellow,cyan,green,magenta \"npm:tsc-watch\" \"npm:esbuild-watch\" \"npm:esbuild-demo-client-watch\" \"npm:esbuild-demo-server-watch\" \"npm:start\"",
|
"dev": "concurrently -k -p [{name}] -n tsc,esbuild,esbuild-demo-client,esbuild-demo-server,server -c blue,yellow,cyan,green,magenta \"npm:tsc-watch\" \"npm:esbuild-watch\" \"npm:esbuild-demo-client-watch\" \"npm:esbuild-demo-server-watch\" \"npm:start\"",
|
||||||
"build": "npm run tsc",
|
"build": "npm run tsc",
|
||||||
|
"build:parser-wasm": "bash bin/build_parser_wasm.sh",
|
||||||
"watch": "npm run tsc-watch",
|
"watch": "npm run tsc-watch",
|
||||||
"tsc": "tsgo -b ./tsconfig.all.json",
|
"tsc": "tsgo -b ./tsconfig.all.json",
|
||||||
"tsc-watch": "tsgo -b -w ./tsconfig.all.json --preserveWatchOutput",
|
"tsc-watch": "tsgo -b -w ./tsconfig.all.json --preserveWatchOutput",
|
||||||
@@ -48,11 +49,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": "eslint --max-warnings 0 src/ addons/ demo/ test/",
|
"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-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": "eslint --fix src/ addons/ demo/ test/",
|
"lint-fix": "oxlint --type-aware --fix --max-warnings 0 src addons demo test",
|
||||||
"lint-api": "eslint --config eslint.config.typings.mjs --max-warnings 0 typings/",
|
"lint-api": "oxlint --max-warnings 0 -c .oxlintrc.typings.json typings && eslint --config eslint.config.naming.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",
|
||||||
@@ -61,9 +62,9 @@
|
|||||||
"test-integration-firefox": "node ./bin/test_integration.js --workers=75% \"--project=FirefoxStable\"",
|
"test-integration-firefox": "node ./bin/test_integration.js --workers=75% \"--project=FirefoxStable\"",
|
||||||
"test-integration-webkit": "node ./bin/test_integration.js --workers=75% \"--project=WebKit\"",
|
"test-integration-webkit": "node ./bin/test_integration.js --workers=75% \"--project=WebKit\"",
|
||||||
"test-integration-debug": "node ./bin/test_integration.js --workers=1 --headed --timeout=30000",
|
"test-integration-debug": "node ./bin/test_integration.js --workers=1 --headed --timeout=30000",
|
||||||
"benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json",
|
"benchmark": "xterm-benchmark -r 5 -c test/benchmark/benchmark.json",
|
||||||
"benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/*benchmark.js",
|
"benchmark-baseline": "xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/*benchmark.js",
|
||||||
"benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/*benchmark.js",
|
"benchmark-eval": "xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/*benchmark.js",
|
||||||
"clean": "rm -rf lib out addons/*/lib addons/*/out",
|
"clean": "rm -rf lib out addons/*/lib addons/*/out",
|
||||||
"vtfeatures": "node bin/extract_vtfeatures.js src/*/*.ts src/*/*/*.ts src/*.ts addons/**/src/*.ts",
|
"vtfeatures": "node bin/extract_vtfeatures.js src/*/*.ts src/*/*/*.ts src/*.ts addons/**/src/*.ts",
|
||||||
"prepackage": "npm run build",
|
"prepackage": "npm run build",
|
||||||
@@ -75,40 +76,45 @@
|
|||||||
"prepublishOnly": "npm run package"
|
"prepublishOnly": "npm run package"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^10.0.1",
|
||||||
"@lunapaint/png-codec": "^0.2.0",
|
"@lunapaint/png-codec": "^0.2.0",
|
||||||
"@playwright/test": "^1.57.0",
|
"@playwright/test": "^1.59.1",
|
||||||
"@stylistic/eslint-plugin": "^4.4.1",
|
"@stylistic/eslint-plugin": "^5.10.0",
|
||||||
"@types/chai": "^4.2.22",
|
"@types/chai": "^5.2.3",
|
||||||
"@types/express": "4",
|
"@types/express": "^5.0.6",
|
||||||
"@types/express-ws": "^3.0.1",
|
"@types/express-ws": "^3.0.1",
|
||||||
"@types/jsdom": "^27.0.0",
|
"@types/jsdom": "^28.0.1",
|
||||||
"@types/mocha": "^9.0.0",
|
"@types/mocha": "^10.0.10",
|
||||||
"@types/node": "^22.19.3",
|
"@types/node": "^22.19.3",
|
||||||
"@types/webpack": "^5.28.0",
|
"@types/webpack": "^5.28.0",
|
||||||
"@types/ws": "^8.2.0",
|
"@types/ws": "^8.2.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.50.1",
|
"@typescript-eslint/eslint-plugin": "^8.59.2",
|
||||||
"@typescript-eslint/parser": "^8.50.1",
|
"@typescript-eslint/parser": "^8.59.2",
|
||||||
"@typescript/native-preview": "^7.0.0-dev.20260213.1",
|
"@typescript/native-preview": "^7.0.0-dev.20260509.2",
|
||||||
"chai": "^4.3.4",
|
"chai": "^6.2.2",
|
||||||
"concurrently": "^9.1.2",
|
"concurrently": "^9.1.2",
|
||||||
"esbuild": "~0.25.2",
|
"esbuild": "^0.28.0",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^10.3.0",
|
||||||
"eslint-plugin-jsdoc": "^50.8.0",
|
"eslint-plugin-jsdoc": "^62.9.0",
|
||||||
"express": "^4.19.2",
|
"eslint-plugin-oxlint": "^1.67.0",
|
||||||
|
"express": "^5.2.1",
|
||||||
"express-ws": "^5.0.2",
|
"express-ws": "^5.0.2",
|
||||||
"jsdom": "^27.3.0",
|
"jsdom": "^29.1.1",
|
||||||
"mocha": "^10.1.0",
|
"mocha": "^11.7.5",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
"node-pty": "^1.2.0-beta.9",
|
"node-pty": "^1.2.0-beta.9",
|
||||||
"nyc": "^17.1.0",
|
"nyc": "^18.0.0",
|
||||||
"source-map-loader": "^3.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-support": "^0.5.20",
|
"source-map-support": "^0.5.20",
|
||||||
"ts-loader": "^9.3.1",
|
"ts-loader": "^9.5.7",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"typescript-eslint": "^8.50.1",
|
"typescript-eslint": "^8.59.2",
|
||||||
"webpack": "^5.61.0",
|
"webpack": "^5.106.2",
|
||||||
"webpack-cli": "^4.9.1",
|
"webpack-cli": "^7.0.2",
|
||||||
"ws": "^8.2.3",
|
"ws": "^8.20.0",
|
||||||
"xterm-benchmark": "^0.3.1"
|
"xterm-benchmark": "^0.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as Strings from 'browser/LocalizableStrings';
|
import * as Strings from './LocalizableStrings';
|
||||||
import { ITerminal, IRenderDebouncer } from 'browser/Types';
|
import { ITerminal, IRenderDebouncer } from './Types';
|
||||||
import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer';
|
import { TimeBasedDebouncer } from './TimeBasedDebouncer';
|
||||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
import { Disposable, toDisposable } from '../common/Lifecycle';
|
||||||
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
import { ICoreBrowserService, IRenderService } from './services/Services';
|
||||||
import { IBuffer } from 'common/buffer/Types';
|
import { IBuffer } from '../common/buffer/Types';
|
||||||
import { IInstantiationService } from 'common/services/Services';
|
import { IInstantiationService } from '../common/services/Services';
|
||||||
import { addDisposableListener } from 'browser/Dom';
|
import { addDisposableListener } from './Dom';
|
||||||
|
|
||||||
const enum Constants {
|
const enum Constants {
|
||||||
MAX_ROWS_TO_READ = 20
|
MAX_ROWS_TO_READ = 20
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import * as Clipboard from 'browser/Clipboard';
|
import * as Clipboard from './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', () => {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ISelectionService } from 'browser/services/Services';
|
import { ISelectionService } from './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
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { ColorContrastCache } from 'browser/ColorContrastCache';
|
import { ColorContrastCache } from './ColorContrastCache';
|
||||||
|
|
||||||
describe('ColorContrastCache', () => {
|
describe('ColorContrastCache', () => {
|
||||||
let cache: ColorContrastCache;
|
let cache: ColorContrastCache;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IColorContrastCache } from 'browser/Types';
|
import { IColorContrastCache } from './Types';
|
||||||
import { IColor } from 'common/Types';
|
import { IColor } from '../common/Types';
|
||||||
import { TwoKeyMap } from 'common/MultiKeyMap';
|
import { TwoKeyMap } from '../common/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();
|
||||||
|
|||||||
@@ -22,43 +22,43 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
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 'browser/Clipboard';
|
import { copyHandler, handlePasteEvent, moveTextAreaUnderMouseCursor, paste, rightClickHandler } from './Clipboard';
|
||||||
import * as Strings from 'browser/LocalizableStrings';
|
import * as Strings from './LocalizableStrings';
|
||||||
import { OscLinkProvider } from 'browser/OscLinkProvider';
|
import { OscLinkProvider } from './OscLinkProvider';
|
||||||
import { CharacterJoinerHandler, CustomKeyEventHandler, CustomWheelEventHandler, IBrowser, IBufferRange, ICompositionHelper, ILinkifier2, ITerminal } from 'browser/Types';
|
import { CharacterJoinerHandler, CustomKeyEventHandler, CustomWheelEventHandler, IBrowser, IBufferRange, ICompositionHelper, ILinkifier2, ITerminal } from './Types';
|
||||||
import { Viewport } from 'browser/Viewport';
|
import { Viewport } from './Viewport';
|
||||||
import { BufferDecorationRenderer } from 'browser/decorations/BufferDecorationRenderer';
|
import { BufferDecorationRenderer } from './decorations/BufferDecorationRenderer';
|
||||||
import { OverviewRulerRenderer } from 'browser/decorations/OverviewRulerRenderer';
|
import { OverviewRulerRenderer } from './decorations/OverviewRulerRenderer';
|
||||||
import { CompositionHelper } from 'browser/input/CompositionHelper';
|
import { CompositionHelper } from './input/CompositionHelper';
|
||||||
import { DomRenderer } from 'browser/renderer/dom/DomRenderer';
|
import { DomRenderer } from './renderer/dom/DomRenderer';
|
||||||
import { IRenderer } from 'browser/renderer/shared/Types';
|
import { IRenderer } from './renderer/shared/Types';
|
||||||
import { CharSizeService } from 'browser/services/CharSizeService';
|
import { CharSizeService } from './services/CharSizeService';
|
||||||
import { CharacterJoinerService } from 'browser/services/CharacterJoinerService';
|
import { CharacterJoinerService } from './services/CharacterJoinerService';
|
||||||
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
|
import { CoreBrowserService } from './services/CoreBrowserService';
|
||||||
import { LinkProviderService } from 'browser/services/LinkProviderService';
|
import { LinkProviderService } from './services/LinkProviderService';
|
||||||
import { MouseCoordsService } from 'browser/services/MouseCoordsService';
|
import { MouseCoordsService } from './services/MouseCoordsService';
|
||||||
import { MouseService } from 'browser/services/MouseService';
|
import { MouseEventCssClasses, MouseService } from './services/MouseService';
|
||||||
import { RenderService } from 'browser/services/RenderService';
|
import { RenderService } from './services/RenderService';
|
||||||
import { SelectionService } from 'browser/services/SelectionService';
|
import { SelectionService } from './services/SelectionService';
|
||||||
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IKeyboardService, ILinkProviderService, IMouseCoordsService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
|
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IKeyboardService, ILinkProviderService, IMouseCoordsService, IMouseService, IRenderService, ISelectionService, IThemeService } from './services/Services';
|
||||||
import { ThemeService } from 'browser/services/ThemeService';
|
import { ThemeService } from './services/ThemeService';
|
||||||
import { KeyboardService } from 'browser/services/KeyboardService';
|
import { KeyboardService } from './services/KeyboardService';
|
||||||
import { channels, color, rgb } from 'common/Color';
|
import { channels, color, rgb } from '../common/Color';
|
||||||
import { CoreTerminal } from 'common/CoreTerminal';
|
import { CoreTerminal } from '../common/CoreTerminal';
|
||||||
import * as Browser from 'common/Platform';
|
import * as Browser from '../common/Platform';
|
||||||
import { ColorRequestType, IColorEvent, ITerminalOptions, KeyboardResultType, SpecialColorIndex } from 'common/Types';
|
import { ColorRequestType, IColorEvent, ITerminalOptions, KeyboardResultType, SpecialColorIndex } from '../common/Types';
|
||||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
import { DEFAULT_ATTR_DATA } from '../common/buffer/BufferLine';
|
||||||
import { IBuffer } from 'common/buffer/Types';
|
import { IBuffer } from '../common/buffer/Types';
|
||||||
import { C0, C1ESCAPED } from 'common/data/EscapeSequences';
|
import { C0, C1ESCAPED } from '../common/data/EscapeSequences';
|
||||||
import { toRgbString } from 'common/input/XParseColor';
|
import { toRgbString } from '../common/input/XParseColor';
|
||||||
import { DecorationService } from 'common/services/DecorationService';
|
import { DecorationService } from '../common/services/DecorationService';
|
||||||
import { IDecorationService } from 'common/services/Services';
|
import { IDecorationService } from '../common/services/Services';
|
||||||
import { WindowsOptionsReportType } from '../common/InputHandler';
|
import { WindowsOptionsReportType } from '../common/InputHandler';
|
||||||
import { AccessibilityManager } from './AccessibilityManager';
|
import { AccessibilityManager } from './AccessibilityManager';
|
||||||
import { Linkifier } from './Linkifier';
|
import { Linkifier } from './Linkifier';
|
||||||
import { Emitter, EventUtils, type IEvent } from 'common/Event';
|
import { Emitter, EventUtils, type IEvent } from '../common/Event';
|
||||||
import { addDisposableListener } from 'browser/Dom';
|
import { addDisposableListener } from './Dom';
|
||||||
import { MutableDisposable, toDisposable } from 'common/Lifecycle';
|
import { MutableDisposable, toDisposable } from '../common/Lifecycle';
|
||||||
|
|
||||||
export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||||
public textarea: HTMLTextAreaElement | undefined;
|
public textarea: HTMLTextAreaElement | undefined;
|
||||||
@@ -212,7 +212,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|||||||
if (!this._themeService) return;
|
if (!this._themeService) return;
|
||||||
for (const req of event) {
|
for (const req of event) {
|
||||||
let acc: 'foreground' | 'background' | 'cursor' | 'ansi';
|
let acc: 'foreground' | 'background' | 'cursor' | 'ansi';
|
||||||
let ident = '';
|
let ident: string;
|
||||||
switch (req.index) {
|
switch (req.index) {
|
||||||
case SpecialColorIndex.FOREGROUND: // OSC 10 | 110
|
case SpecialColorIndex.FOREGROUND: // OSC 10 | 110
|
||||||
acc = 'foreground';
|
acc = 'foreground';
|
||||||
@@ -618,11 +618,12 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
|||||||
this._register(addDisposableListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.handleMouseDown(e)));
|
this._register(addDisposableListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.handleMouseDown(e)));
|
||||||
|
|
||||||
// apply mouse event classes set by escape codes before terminal was attached
|
// apply mouse event classes set by escape codes before terminal was attached
|
||||||
if (this.mouseStateService.areMouseEventsActive) {
|
if (this.mouseStateService.areMouseEventsActive && !this.options.mouseEventsRequireAlt) {
|
||||||
this._selectionService.disable();
|
this._selectionService.disable();
|
||||||
this.element.classList.add('enable-mouse-events');
|
this.element.classList.add(MouseEventCssClasses.ENABLE_MOUSE_EVENTS);
|
||||||
} else {
|
} else {
|
||||||
this._selectionService.enable();
|
this._selectionService.enable();
|
||||||
|
this.element.classList.remove(MouseEventCssClasses.ENABLE_MOUSE_EVENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.options.screenReaderMode) {
|
if (this.options.screenReaderMode) {
|
||||||
|
|||||||
+2
-2
@@ -5,8 +5,8 @@
|
|||||||
* Minimal DOM helpers for xterm.js browser code.
|
* Minimal DOM helpers for xterm.js browser code.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IntervalTimer } from 'common/Async';
|
import { IntervalTimer } from '../common/Async';
|
||||||
import { IDisposable } from 'common/Lifecycle';
|
import { IDisposable } from '../common/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,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/TestUtils.test';
|
||||||
import { ILink } from 'browser/Types';
|
import { ILink } from './Types';
|
||||||
import { LinkProviderService } from 'browser/services/LinkProviderService';
|
import { LinkProviderService } from './services/LinkProviderService';
|
||||||
import jsdom = require('jsdom');
|
import jsdom = require('jsdom');
|
||||||
|
|
||||||
class TestLinkifier2 extends Linkifier {
|
class TestLinkifier2 extends Linkifier {
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IBufferCellPosition, ILink, ILinkDecorations, ILinkWithState, ILinkifier2, ILinkifierEvent } from 'browser/Types';
|
import { IBufferCellPosition, ILink, ILinkDecorations, ILinkWithState, ILinkifier2, ILinkifierEvent } from './Types';
|
||||||
import { Disposable, dispose, toDisposable } from 'common/Lifecycle';
|
import { Disposable, dispose, toDisposable } from '../common/Lifecycle';
|
||||||
import { IDisposable } from 'common/Types';
|
import { IDisposable } from '../common/Types';
|
||||||
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/Event';
|
import { Emitter } from '../common/Event';
|
||||||
import { addDisposableListener } from 'browser/Dom';
|
import { addDisposableListener } from './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; }
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { OscLinkProvider } from 'browser/OscLinkProvider';
|
import { OscLinkProvider } from './OscLinkProvider';
|
||||||
import { ILink } from 'browser/Types';
|
import { ILink } from './Types';
|
||||||
import { createCellData, MockBufferService, MockOptionsService } from 'common/TestUtils.test';
|
import { createCellData, MockBufferService, MockOptionsService } from '../common/TestUtils.test';
|
||||||
import { IBufferService, IOscLinkService } from 'common/services/Services';
|
import { IBufferService, IOscLinkService } from '../common/services/Services';
|
||||||
import { IBufferLine, IOscLinkData } from 'common/Types';
|
import { IBufferLine, IOscLinkData } from '../common/Types';
|
||||||
|
|
||||||
class TestOscLinkService implements IOscLinkService {
|
class TestOscLinkService implements IOscLinkService {
|
||||||
public serviceBrand: any;
|
public serviceBrand: any;
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IBufferRange, ILink } from 'browser/Types';
|
import { IBufferRange, ILink } from './Types';
|
||||||
import { ILinkProvider } from 'browser/services/Services';
|
import { ILinkProvider } from './services/Services';
|
||||||
import { CellData } from 'common/buffer/CellData';
|
import { CellData } from '../common/buffer/CellData';
|
||||||
import { IBufferLine } from 'common/Types';
|
import { IBufferLine } from '../common/Types';
|
||||||
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();
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IRenderDebouncerWithCallback } from 'browser/Types';
|
import { IRenderDebouncerWithCallback } from './Types';
|
||||||
import { ICoreBrowserService } from 'browser/services/Services';
|
import { ICoreBrowserService } from './services/Services';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debounces calls to render terminal rows using animation frames.
|
* Debounces calls to render terminal rows using animation frames.
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MockCompositionHelper, MockRenderer, MockViewport, TestTerminal } from 'browser/TestUtils.test';
|
import { MockCompositionHelper, MockRenderer, MockViewport, TestTerminal } from './TestUtils.test';
|
||||||
import type { IBrowser } from 'browser/Types';
|
import type { IBrowser } from './Types';
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
import { DEFAULT_ATTR_DATA } from '../common/buffer/BufferLine';
|
||||||
import { CellData } from 'common/buffer/CellData';
|
import { CellData } from '../common/buffer/CellData';
|
||||||
import { MockUnicodeService, createCellData } from 'common/TestUtils.test';
|
import { MockUnicodeService, createCellData } from '../common/TestUtils.test';
|
||||||
import { IMarker } from 'common/Types';
|
import { IMarker } from '../common/Types';
|
||||||
|
|
||||||
const INIT_COLS = 80;
|
const INIT_COLS = 80;
|
||||||
const INIT_ROWS = 24;
|
const INIT_ROWS = 24;
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ 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 'browser/CoreBrowserTerminal';
|
import { CoreBrowserTerminal } from './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
|
||||||
const COLS = 80;
|
const COLS = 80;
|
||||||
const ROWS = 25;
|
const ROWS = 25;
|
||||||
|
|
||||||
const escapeSequenceFilesDir = path.join(__dirname, '../../fixtures/escape_sequence_files');
|
const escapeSequenceFilesDir = path.join(__dirname, '../../test/fixtures/escape_sequence_files');
|
||||||
const TESTFILES = fs.readdirSync(escapeSequenceFilesDir)
|
const TESTFILES = fs.readdirSync(escapeSequenceFilesDir)
|
||||||
.filter(f => f.endsWith('.in'))
|
.filter(f => f.endsWith('.in'))
|
||||||
.map(f => path.join(escapeSequenceFilesDir, f));
|
.map(f => path.join(escapeSequenceFilesDir, f));
|
||||||
@@ -125,7 +125,7 @@ function formatError(input: string, output: string, expected: string): string {
|
|||||||
// simple debug output of terminal cells
|
// simple debug output of terminal cells
|
||||||
function terminalToString(term: CoreBrowserTerminal): string {
|
function terminalToString(term: CoreBrowserTerminal): string {
|
||||||
let result = '';
|
let result = '';
|
||||||
let lineText = '';
|
let lineText: string;
|
||||||
for (let line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) {
|
for (let line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) {
|
||||||
lineText = term.buffer.lines.get(line)!.translateToString(true);
|
lineText = term.buffer.lines.get(line)!.translateToString(true);
|
||||||
// rtrim empty cells as xterm does
|
// rtrim empty cells as xterm does
|
||||||
|
|||||||
@@ -4,21 +4,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
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 'browser/services/Services';
|
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IMouseService, IRenderService, ISelectionService, IThemeService } from './services/Services';
|
||||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
|
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from './renderer/shared/Types';
|
||||||
import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from 'browser/Types';
|
import { IColorSet, ITerminal, ILinkifier2, IBrowser, IViewport, ICompositionHelper, CharacterJoinerHandler, IBufferRange, ReadonlyColorSet, IBufferElementProvider } from './Types';
|
||||||
import { IBuffer, IBufferSet } from 'common/buffer/Types';
|
import { IBuffer, IBufferSet } from '../common/buffer/Types';
|
||||||
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions, ColorIndex } from 'common/Types';
|
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions, ColorIndex } from '../common/Types';
|
||||||
import { Buffer } from 'common/buffer/Buffer';
|
import { Buffer } from '../common/buffer/Buffer';
|
||||||
import * as Browser from 'common/Platform';
|
import * as Browser from '../common/Platform';
|
||||||
import { CoreBrowserTerminal } from 'browser/CoreBrowserTerminal';
|
import { CoreBrowserTerminal } from './CoreBrowserTerminal';
|
||||||
import { IUnicodeService, IOptionsService, ICoreService, IMouseStateService } from 'common/services/Services';
|
import { IUnicodeService, IOptionsService, ICoreService, IMouseStateService } from '../common/services/Services';
|
||||||
import { IFunctionIdentifier, IParams } from 'common/parser/Types';
|
import { IFunctionIdentifier, IParams } from '../common/parser/Types';
|
||||||
import { AttributeData } from 'common/buffer/AttributeData';
|
import { AttributeData } from '../common/buffer/AttributeData';
|
||||||
import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types';
|
import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from './selection/Types';
|
||||||
import { css } from 'common/Color';
|
import { css } from '../common/Color';
|
||||||
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
|
import { createRenderDimensions } from './renderer/shared/RendererUtils';
|
||||||
import { Emitter, type IEvent } from 'common/Event';
|
import { Emitter, type IEvent } from '../common/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; }
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second
|
const RENDER_DEBOUNCE_THRESHOLD_MS = 1000; // 1 Second
|
||||||
|
|
||||||
import { IRenderDebouncer } from 'browser/Types';
|
import { IRenderDebouncer } from './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.
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CharData, IColor, ICoreTerminal, ITerminalOptions } from 'common/Types';
|
import { CharData, IColor, ICoreTerminal, ITerminalOptions } from '../common/Types';
|
||||||
import { IBuffer } from 'common/buffer/Types';
|
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/Color';
|
import { channels, css } from '../common/Color';
|
||||||
import type { IEvent } from 'common/Event';
|
import type { IEvent } from '../common/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
@@ -3,16 +3,16 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
|
import { ICoreBrowserService, IRenderService, IThemeService } from './services/Services';
|
||||||
import { ViewportConstants } from 'browser/shared/Constants';
|
import { ViewportConstants } from './shared/Constants';
|
||||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
import { Disposable, toDisposable } from '../common/Lifecycle';
|
||||||
import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from 'common/services/Services';
|
import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from '../common/services/Services';
|
||||||
import { CoreMouseEventType } from 'common/Types';
|
import { CoreMouseEventType } from '../common/Types';
|
||||||
import { scheduleAtNextAnimationFrame } from 'browser/Dom';
|
import { scheduleAtNextAnimationFrame } from './Dom';
|
||||||
import { SmoothScrollableElement } from 'browser/scrollable/scrollableElement';
|
import { SmoothScrollableElement } from './scrollable/scrollableElement';
|
||||||
import type { IScrollableElementChangeOptions } from 'browser/scrollable/scrollableElementOptions';
|
import type { IScrollableElementChangeOptions } from './scrollable/scrollableElementOptions';
|
||||||
import { Emitter, EventUtils } from 'common/Event';
|
import { Emitter, EventUtils } from '../common/Event';
|
||||||
import { Scrollable, ScrollbarVisibility, type IScrollEvent } from 'browser/scrollable/scrollable';
|
import { Scrollable, ScrollbarVisibility, type IScrollEvent } from './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 'browser/services/Services';
|
import { ICoreBrowserService, IRenderService } from '../services/Services';
|
||||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
import { Disposable, toDisposable } from '../../common/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 'browser/decorations/ColorZoneStore';
|
import { ColorZoneStore } from './ColorZoneStore';
|
||||||
|
|
||||||
const optionsRedFull = {
|
const optionsRedFull = {
|
||||||
overviewRulerOptions: {
|
overviewRulerOptions: {
|
||||||
|
|||||||
@@ -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 'browser/decorations/ColorZoneStore';
|
import { ColorZoneStore, IColorZone, IColorZoneStore } from './ColorZoneStore';
|
||||||
import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
|
import { ICoreBrowserService, IRenderService, IThemeService } from '../services/Services';
|
||||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
import { Disposable, toDisposable } from '../../common/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
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { CompositionHelper } from 'browser/input/CompositionHelper';
|
import { CompositionHelper } from './CompositionHelper';
|
||||||
import { MockRenderService } from 'browser/TestUtils.test';
|
import { MockRenderService } from '../TestUtils.test';
|
||||||
import { MockCoreService, MockBufferService, MockOptionsService } from 'common/TestUtils.test';
|
import { MockCoreService, MockBufferService, MockOptionsService } from '../../common/TestUtils.test';
|
||||||
|
|
||||||
describe('CompositionHelper', () => {
|
describe('CompositionHelper', () => {
|
||||||
let compositionHelper: CompositionHelper;
|
let compositionHelper: CompositionHelper;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IRenderService } from 'browser/services/Services';
|
import { IRenderService } from '../services/Services';
|
||||||
import { IBufferService, ICoreService, IOptionsService } from 'common/services/Services';
|
import { IBufferService, ICoreService, IOptionsService } from '../../common/services/Services';
|
||||||
import { C0 } from 'common/data/EscapeSequences';
|
import { C0 } from '../../common/data/EscapeSequences';
|
||||||
|
|
||||||
interface IPosition {
|
interface IPosition {
|
||||||
start: number;
|
start: number;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import jsdom = require('jsdom');
|
import jsdom = require('jsdom');
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { getCoords } from 'browser/input/Mouse';
|
import { getCoords } from './Mouse';
|
||||||
|
|
||||||
const CHAR_WIDTH = 10;
|
const CHAR_WIDTH = 10;
|
||||||
const CHAR_HEIGHT = 20;
|
const CHAR_HEIGHT = 20;
|
||||||
|
|||||||
@@ -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/TestUtils.test';
|
||||||
import { moveToCellSequence } from './MoveToCell';
|
import { moveToCellSequence } from './MoveToCell';
|
||||||
|
|
||||||
describe('MoveToCell', () => {
|
describe('MoveToCell', () => {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { C0 } from 'common/data/EscapeSequences';
|
import { C0 } from '../../common/data/EscapeSequences';
|
||||||
import { IBufferService } from 'common/services/Services';
|
import { IBufferService } from '../../common/services/Services';
|
||||||
|
|
||||||
const enum Direction {
|
const enum Direction {
|
||||||
UP = 'A',
|
UP = 'A',
|
||||||
|
|||||||
@@ -3,17 +3,17 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as Strings from 'browser/LocalizableStrings';
|
import * as Strings from '../LocalizableStrings';
|
||||||
import { CoreBrowserTerminal as TerminalCore } from 'browser/CoreBrowserTerminal';
|
import { CoreBrowserTerminal as TerminalCore } from '../CoreBrowserTerminal';
|
||||||
import { IBufferRange, ITerminal } from 'browser/Types';
|
import { IBufferRange, ITerminal } from '../Types';
|
||||||
import { Disposable } from 'common/Lifecycle';
|
import { Disposable } from '../../common/Lifecycle';
|
||||||
import { ITerminalOptions } from 'common/Types';
|
import { ITerminalOptions } from '../../common/Types';
|
||||||
import { AddonManager } from 'common/public/AddonManager';
|
import { AddonManager } from '../../common/public/AddonManager';
|
||||||
import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi';
|
import { BufferNamespaceApi } from '../../common/public/BufferNamespaceApi';
|
||||||
import { ParserApi } from 'common/public/ParserApi';
|
import { ParserApi } from '../../common/public/ParserApi';
|
||||||
import { UnicodeApi } from 'common/public/UnicodeApi';
|
import { UnicodeApi } from '../../common/public/UnicodeApi';
|
||||||
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, IRenderDimensions, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
|
import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, IRenderDimensions, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
|
||||||
import type { IEvent } from 'common/Event';
|
import type { IEvent } from '../../common/Event';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The set of options that only have an effect when set in the Terminal constructor.
|
* The set of options that only have an effect when set in the Terminal constructor.
|
||||||
|
|||||||
@@ -3,20 +3,20 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { DomRendererRowFactory, RowCss } from 'browser/renderer/dom/DomRendererRowFactory';
|
import { DomRendererRowFactory, RowCss } from './DomRendererRowFactory';
|
||||||
import { WidthCache } from 'browser/renderer/dom/WidthCache';
|
import { WidthCache } from './WidthCache';
|
||||||
import { INVERTED_DEFAULT_COLOR, RendererConstants } from 'browser/renderer/shared/Constants';
|
import { INVERTED_DEFAULT_COLOR, RendererConstants } from '../shared/Constants';
|
||||||
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
|
import { createRenderDimensions } from '../shared/RendererUtils';
|
||||||
import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
|
import { createSelectionRenderModel } from '../shared/SelectionRenderModel';
|
||||||
import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager';
|
import { TextBlinkStateManager } from '../shared/TextBlinkStateManager';
|
||||||
import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
import { IRenderDimensions, IRenderer, IRequestRedrawEvent, ISelectionRenderModel } from '../shared/Types';
|
||||||
import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
import { ICharSizeService, ICoreBrowserService, IThemeService } from '../../services/Services';
|
||||||
import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from 'browser/Types';
|
import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from '../../Types';
|
||||||
import { color } from 'common/Color';
|
import { color } from '../../../common/Color';
|
||||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
import { Disposable, toDisposable } from '../../../common/Lifecycle';
|
||||||
import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from 'common/services/Services';
|
import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from '../../../common/services/Services';
|
||||||
import { Emitter } from 'common/Event';
|
import { Emitter } from '../../../common/Event';
|
||||||
import { addDisposableListener } from 'browser/Dom';
|
import { addDisposableListener } from '../../Dom';
|
||||||
|
|
||||||
|
|
||||||
const enum Constants {
|
const enum Constants {
|
||||||
|
|||||||
@@ -5,15 +5,15 @@
|
|||||||
|
|
||||||
import jsdom = require('jsdom');
|
import jsdom = require('jsdom');
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory';
|
import { DomRendererRowFactory } from './DomRendererRowFactory';
|
||||||
import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags, BgFlags, Attributes, UnderlineStyle } from 'common/buffer/Constants';
|
import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags, BgFlags, Attributes, UnderlineStyle } from '../../../common/buffer/Constants';
|
||||||
import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
import { BufferLine, DEFAULT_ATTR_DATA } from '../../../common/buffer/BufferLine';
|
||||||
import { BufferLineStringCache } from 'common/buffer/BufferLineStringCache';
|
import { BufferLineStringCache } from '../../../common/buffer/BufferLineStringCache';
|
||||||
import { IBufferLine } from 'common/Types';
|
import { IBufferLine } from '../../../common/Types';
|
||||||
import { CellData } from 'common/buffer/CellData';
|
import { CellData } from '../../../common/buffer/CellData';
|
||||||
import { MockCoreService, MockDecorationService, MockOptionsService, createCellData, NULL_CELL_DATA } from 'common/TestUtils.test';
|
import { MockCoreService, MockDecorationService, MockOptionsService, createCellData, NULL_CELL_DATA } from '../../../common/TestUtils.test';
|
||||||
import { MockCharacterJoinerService, MockCoreBrowserService, MockThemeService } from 'browser/TestUtils.test';
|
import { MockCharacterJoinerService, MockCoreBrowserService, MockThemeService } from '../../TestUtils.test';
|
||||||
import { TestWidthCache } from 'browser/renderer/dom/WidthCache.test';
|
import { TestWidthCache } from './WidthCache.test';
|
||||||
|
|
||||||
const TEST_STRING_CACHE = new BufferLineStringCache();
|
const TEST_STRING_CACHE = new BufferLineStringCache();
|
||||||
|
|
||||||
|
|||||||
@@ -3,18 +3,18 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IBufferLine, ICellData, IColor } from 'common/Types';
|
import { IBufferLine, ICellData, IColor } from '../../../common/Types';
|
||||||
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
|
import { INVERTED_DEFAULT_COLOR } from '../shared/Constants';
|
||||||
import { WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/Constants';
|
import { WHITESPACE_CELL_CHAR, Attributes } from '../../../common/buffer/Constants';
|
||||||
import { CellData } from 'common/buffer/CellData';
|
import { CellData } from '../../../common/buffer/CellData';
|
||||||
import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
|
import { ICoreService, IDecorationService, IOptionsService } from '../../../common/services/Services';
|
||||||
import { channels, color } from 'common/Color';
|
import { channels, color } from '../../../common/Color';
|
||||||
import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from '../../services/Services';
|
||||||
import { JoinedCellData } from 'browser/services/CharacterJoinerService';
|
import { JoinedCellData } from '../../services/CharacterJoinerService';
|
||||||
import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils';
|
import { treatGlyphAsBackgroundColor } from '../shared/RendererUtils';
|
||||||
import { AttributeData } from 'common/buffer/AttributeData';
|
import { AttributeData } from '../../../common/buffer/AttributeData';
|
||||||
import { WidthCache } from 'browser/renderer/dom/WidthCache';
|
import { WidthCache } from './WidthCache';
|
||||||
import { IColorContrastCache } from 'browser/Types';
|
import { IColorContrastCache } from '../../Types';
|
||||||
|
|
||||||
|
|
||||||
export const enum RowCss {
|
export const enum RowCss {
|
||||||
@@ -90,14 +90,14 @@ export class DomRendererRowFactory {
|
|||||||
let charElement: HTMLSpanElement | undefined;
|
let charElement: HTMLSpanElement | undefined;
|
||||||
let cellAmount = 0;
|
let cellAmount = 0;
|
||||||
let text = '';
|
let text = '';
|
||||||
let i = 0;
|
let i;
|
||||||
let oldBg = 0;
|
let oldBg = 0;
|
||||||
let oldFg = 0;
|
let oldFg = 0;
|
||||||
let oldExt = 0;
|
let oldExt = 0;
|
||||||
let oldLinkHover: number | boolean = false;
|
let oldLinkHover: number | boolean = false;
|
||||||
let oldSpacing = 0;
|
let oldSpacing = 0;
|
||||||
let oldIsInSelection: boolean = false;
|
let oldIsInSelection: boolean = false;
|
||||||
let spacing = 0;
|
let spacing;
|
||||||
let skipJoinedCheckUntilX = 0;
|
let skipJoinedCheckUntilX = 0;
|
||||||
const classes: string[] = [];
|
const classes: string[] = [];
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import { IWidthCacheFontVariantCanvas, WidthCache, WidthCacheSettings } from 'browser/renderer/dom/WidthCache';
|
import { IWidthCacheFontVariantCanvas, WidthCache, WidthCacheSettings } from './WidthCache';
|
||||||
|
|
||||||
|
|
||||||
class MockWidthCacheFontVariantCanvas implements IWidthCacheFontVariantCanvas {
|
class MockWidthCacheFontVariantCanvas implements IWidthCacheFontVariantCanvas {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
import { throwIfFalsy } from '../shared/RendererUtils';
|
||||||
import { IDisposable } from 'common/Types';
|
import { IDisposable } from '../../../common/Types';
|
||||||
import { FontWeight } from 'common/services/Services';
|
import { FontWeight } from '../../../common/services/Services';
|
||||||
|
|
||||||
|
|
||||||
export const enum WidthCacheSettings {
|
export const enum WidthCacheSettings {
|
||||||
@@ -110,7 +110,7 @@ export class WidthCache implements IDisposable {
|
|||||||
* `variant` denotes the font variant to be used.
|
* `variant` denotes the font variant to be used.
|
||||||
*/
|
*/
|
||||||
public get(c: string, bold: boolean | number, italic: boolean | number): number {
|
public get(c: string, bold: boolean | number, italic: boolean | number): number {
|
||||||
let cp = 0;
|
let cp: number;
|
||||||
if (!bold && !italic && c.length === 1 && (cp = c.charCodeAt(0)) < WidthCacheSettings.FLAT_SIZE) {
|
if (!bold && !italic && c.length === 1 && (cp = c.charCodeAt(0)) < WidthCacheSettings.FLAT_SIZE) {
|
||||||
if (this._flat[cp] !== WidthCacheSettings.FLAT_UNSET) {
|
if (this._flat[cp] !== WidthCacheSettings.FLAT_UNSET) {
|
||||||
return this._flat[cp];
|
return this._flat[cp];
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { computeNextVariantOffset } from 'browser/renderer/shared/RendererUtils';
|
import { computeNextVariantOffset } from './RendererUtils';
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
|
|
||||||
describe('RendererUtils', () => {
|
describe('RendererUtils', () => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IDimensions, IRenderDimensions } from 'browser/renderer/shared/Types';
|
import { IDimensions, IRenderDimensions } from './Types';
|
||||||
|
|
||||||
export function throwIfFalsy<T>(value: T | undefined | null): T {
|
export function throwIfFalsy<T>(value: T | undefined | null): T {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ITerminal } from 'browser/Types';
|
import { ITerminal } from '../../Types';
|
||||||
import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
import { ISelectionRenderModel } from './Types';
|
||||||
import { Terminal } from '@xterm/xterm';
|
import { Terminal } from '@xterm/xterm';
|
||||||
|
|
||||||
class SelectionRenderModel implements ISelectionRenderModel {
|
class SelectionRenderModel implements ISelectionRenderModel {
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager';
|
import { TextBlinkStateManager } from './TextBlinkStateManager';
|
||||||
import { MockOptionsService } from 'common/TestUtils.test';
|
import { MockOptionsService } from '../../../common/TestUtils.test';
|
||||||
import type { ICoreBrowserService } from 'browser/services/Services';
|
import type { ICoreBrowserService } from '../../services/Services';
|
||||||
import { Emitter } from 'common/Event';
|
import { Emitter } from '../../../common/Event';
|
||||||
|
|
||||||
class FakeWindow {
|
class FakeWindow {
|
||||||
public nextId = 1;
|
public nextId = 1;
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ICoreBrowserService } from 'browser/services/Services';
|
import { ICoreBrowserService } from '../../services/Services';
|
||||||
import { Disposable, toDisposable } from 'common/Lifecycle';
|
import { Disposable, toDisposable } from '../../../common/Lifecycle';
|
||||||
import { IOptionsService } from 'common/services/Services';
|
import { IOptionsService } from '../../../common/services/Services';
|
||||||
|
|
||||||
export class TextBlinkStateManager extends Disposable {
|
export class TextBlinkStateManager extends Disposable {
|
||||||
private _intervalDuration: number = 0;
|
private _intervalDuration: number = 0;
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Terminal } from '@xterm/xterm';
|
import { Terminal } from '@xterm/xterm';
|
||||||
import { ITerminal } from 'browser/Types';
|
import { ITerminal } from '../../Types';
|
||||||
import { IDisposable } from 'common/Types';
|
import { IDisposable } from '../../../common/Types';
|
||||||
import type { IEvent } from 'common/Event';
|
import type { IEvent } from '../../../common/Event';
|
||||||
|
|
||||||
export interface IDimensions {
|
export interface IDimensions {
|
||||||
width: number;
|
width: number;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { ScrollbarArrow, IScrollbarArrowOptions } from './scrollbarArrow';
|
|||||||
import { ScrollbarState } from './scrollbarState';
|
import { ScrollbarState } from './scrollbarState';
|
||||||
import { ScrollbarVisibilityController } from './scrollbarVisibilityController';
|
import { ScrollbarVisibilityController } from './scrollbarVisibilityController';
|
||||||
import { Widget } from './widget';
|
import { Widget } from './widget';
|
||||||
import * as platform from 'common/Platform';
|
import * as platform from '../../common/Platform';
|
||||||
import { INewScrollPosition, Scrollable, ScrollbarVisibility } from './scrollable';
|
import { INewScrollPosition, Scrollable, ScrollbarVisibility } from './scrollable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as dom from '../Dom';
|
import * as dom from '../Dom';
|
||||||
import { DisposableStore, IDisposable, toDisposable } from 'common/Lifecycle';
|
import { DisposableStore, IDisposable, toDisposable } from '../../common/Lifecycle';
|
||||||
|
|
||||||
type PointerMoveCallback = (event: PointerEvent) => void;
|
type PointerMoveCallback = (event: PointerEvent) => void;
|
||||||
type OnStopCallback = () => void;
|
type OnStopCallback = () => void;
|
||||||
|
|||||||
@@ -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 * as platform from 'common/Platform';
|
import * as platform from '../../common/Platform';
|
||||||
|
|
||||||
interface IWindowChainElement {
|
interface IWindowChainElement {
|
||||||
readonly window: WeakRef<Window>;
|
readonly window: WeakRef<Window>;
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
* 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 { Emitter, IEvent } from 'common/Event';
|
import { Emitter, IEvent } from '../../common/Event';
|
||||||
import { Disposable, IDisposable } from 'common/Lifecycle';
|
import { Disposable, IDisposable } from '../../common/Lifecycle';
|
||||||
|
|
||||||
export const enum ScrollbarVisibility {
|
export const enum ScrollbarVisibility {
|
||||||
AUTO = 1,
|
AUTO = 1,
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import { HorizontalScrollbar } from './horizontalScrollbar';
|
|||||||
import { IScrollableElementChangeOptions, IScrollableElementCreationOptions, IScrollableElementResolvedOptions } from './scrollableElementOptions';
|
import { IScrollableElementChangeOptions, IScrollableElementCreationOptions, IScrollableElementResolvedOptions } from './scrollableElementOptions';
|
||||||
import { VerticalScrollbar } from './verticalScrollbar';
|
import { VerticalScrollbar } from './verticalScrollbar';
|
||||||
import { Widget } from './widget';
|
import { Widget } from './widget';
|
||||||
import { TimeoutTimer } from 'common/Async';
|
import { TimeoutTimer } from '../../common/Async';
|
||||||
import { Emitter, IEvent } from 'common/Event';
|
import { Emitter, IEvent } from '../../common/Event';
|
||||||
import { IDisposable, dispose } from 'common/Lifecycle';
|
import { IDisposable, dispose } from '../../common/Lifecycle';
|
||||||
import * as platform from 'common/Platform';
|
import * as platform from '../../common/Platform';
|
||||||
import { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, IScrollEvent, Scrollable, ScrollbarVisibility } from './scrollable';
|
import { INewScrollDimensions, INewScrollPosition, IScrollDimensions, IScrollPosition, IScrollEvent, Scrollable, ScrollbarVisibility } from './scrollable';
|
||||||
// import 'vs/css!./media/scrollbars';
|
// import 'vs/css!./media/scrollbars';
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { GlobalPointerMoveMonitor } from './globalPointerMoveMonitor';
|
import { GlobalPointerMoveMonitor } from './globalPointerMoveMonitor';
|
||||||
import { Widget } from './widget';
|
import { Widget } from './widget';
|
||||||
import { TimeoutTimer } from 'common/Async';
|
import { TimeoutTimer } from '../../common/Async';
|
||||||
import * as dom from '../Dom';
|
import * as dom from '../Dom';
|
||||||
|
|
||||||
export interface IScrollbarArrowOptions {
|
export interface IScrollbarArrowOptions {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { FastDomNode } from './fastDomNode';
|
import { FastDomNode } from './fastDomNode';
|
||||||
import { TimeoutTimer } from 'common/Async';
|
import { TimeoutTimer } from '../../common/Async';
|
||||||
import { Disposable } from 'common/Lifecycle';
|
import { Disposable } from '../../common/Lifecycle';
|
||||||
import { ScrollbarVisibility } from './scrollable';
|
import { ScrollbarVisibility } from './scrollable';
|
||||||
|
|
||||||
export class ScrollbarVisibilityController extends Disposable {
|
export class ScrollbarVisibilityController extends Disposable {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as DomUtils from '../Dom';
|
import * as DomUtils from '../Dom';
|
||||||
import { Disposable, IDisposable, toDisposable } from 'common/Lifecycle';
|
import { Disposable, IDisposable, toDisposable } from '../../common/Lifecycle';
|
||||||
|
|
||||||
const mainWindow = (typeof window === 'object' ? window : globalThis) as Window & typeof globalThis;
|
const mainWindow = (typeof window === 'object' ? window : globalThis) as Window & typeof globalThis;
|
||||||
|
|
||||||
@@ -367,7 +367,7 @@ export class Gesture extends Disposable {
|
|||||||
private _dispatchEvent(event: IGestureEvent): void {
|
private _dispatchEvent(event: IGestureEvent): void {
|
||||||
if (event.type === EventType.TAP) {
|
if (event.type === EventType.TAP) {
|
||||||
const currentTime = (new Date()).getTime();
|
const currentTime = (new Date()).getTime();
|
||||||
let setTapCount = 0;
|
let setTapCount;
|
||||||
if (currentTime - this._lastSetTapCountTime > Gesture._clearTapCountTime) {
|
if (currentTime - this._lastSetTapCountTime > Gesture._clearTapCountTime) {
|
||||||
setTapCount = 1;
|
setTapCount = 1;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import * as dom from '../Dom';
|
import * as dom from '../Dom';
|
||||||
import { IMouseEvent, StandardMouseEvent } from './mouseEvent';
|
import { IMouseEvent, StandardMouseEvent } from './mouseEvent';
|
||||||
import { Disposable } from 'common/Lifecycle';
|
import { Disposable } from '../../common/Lifecycle';
|
||||||
|
|
||||||
export abstract class Widget extends Disposable {
|
export abstract class Widget extends Disposable {
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { SelectionModel } from './SelectionModel';
|
import { SelectionModel } from './SelectionModel';
|
||||||
import { MockBufferService } from 'common/TestUtils.test';
|
import { MockBufferService } from '../../common/TestUtils.test';
|
||||||
|
|
||||||
describe('SelectionModel', () => {
|
describe('SelectionModel', () => {
|
||||||
let model: SelectionModel;
|
let model: SelectionModel;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user