@wendongfly/zihi 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/lib/xterm/README.md +27 -14
  3. package/dist/lib/xterm/css/xterm.css +81 -5
  4. package/dist/lib/xterm/lib/xterm.js +1 -1
  5. package/dist/lib/xterm/lib/xterm.js.map +1 -1
  6. package/dist/lib/xterm/lib/xterm.mjs +53 -0
  7. package/dist/lib/xterm/lib/xterm.mjs.map +7 -0
  8. package/dist/lib/xterm/package.json +49 -38
  9. package/dist/lib/xterm/src/browser/AccessibilityManager.ts +185 -50
  10. package/dist/lib/xterm/src/browser/CoreBrowserTerminal.ts +1339 -0
  11. package/dist/lib/xterm/src/browser/Linkifier.ts +403 -0
  12. package/dist/lib/xterm/src/browser/LocalizableStrings.ts +15 -4
  13. package/dist/lib/xterm/src/browser/OscLinkProvider.ts +2 -1
  14. package/dist/lib/xterm/src/browser/RenderDebouncer.ts +6 -5
  15. package/dist/lib/xterm/src/browser/TimeBasedDebouncer.ts +2 -2
  16. package/dist/lib/xterm/src/browser/Types.ts +226 -0
  17. package/dist/lib/xterm/src/browser/Viewport.ts +148 -357
  18. package/dist/lib/xterm/src/browser/decorations/BufferDecorationRenderer.ts +17 -12
  19. package/dist/lib/xterm/src/browser/decorations/OverviewRulerRenderer.ts +47 -52
  20. package/dist/lib/xterm/src/browser/input/CompositionHelper.ts +5 -3
  21. package/dist/lib/xterm/src/browser/input/MoveToCell.ts +3 -1
  22. package/dist/lib/xterm/src/browser/public/Terminal.ts +39 -24
  23. package/dist/lib/xterm/src/browser/renderer/dom/DomRenderer.ts +76 -40
  24. package/dist/lib/xterm/src/browser/renderer/dom/DomRendererRowFactory.ts +47 -23
  25. package/dist/lib/xterm/src/browser/renderer/dom/WidthCache.ts +19 -9
  26. package/dist/lib/xterm/src/browser/renderer/shared/Constants.ts +0 -8
  27. package/dist/lib/xterm/src/browser/renderer/shared/RendererUtils.ts +38 -1
  28. package/dist/lib/xterm/src/browser/renderer/shared/SelectionRenderModel.ts +6 -4
  29. package/dist/lib/xterm/src/browser/renderer/shared/Types.ts +84 -0
  30. package/dist/lib/xterm/src/browser/selection/Types.ts +15 -0
  31. package/dist/lib/xterm/src/browser/services/CharSizeService.ts +57 -32
  32. package/dist/lib/xterm/src/browser/services/CoreBrowserService.ts +108 -4
  33. package/dist/lib/xterm/src/browser/services/LinkProviderService.ts +28 -0
  34. package/dist/lib/xterm/src/browser/services/RenderService.ts +132 -40
  35. package/dist/lib/xterm/src/browser/services/SelectionService.ts +19 -9
  36. package/dist/lib/xterm/src/browser/services/Services.ts +36 -16
  37. package/dist/lib/xterm/src/browser/services/ThemeService.ts +19 -58
  38. package/dist/lib/xterm/src/browser/shared/Constants.ts +8 -0
  39. package/dist/lib/xterm/src/common/CircularList.ts +5 -5
  40. package/dist/lib/xterm/src/common/Color.ts +34 -14
  41. package/dist/lib/xterm/src/common/CoreTerminal.ts +40 -41
  42. package/dist/lib/xterm/src/common/InputHandler.ts +177 -125
  43. package/dist/lib/xterm/src/common/Platform.ts +2 -1
  44. package/dist/lib/xterm/src/common/SortedList.ts +86 -10
  45. package/dist/lib/xterm/src/common/TaskQueue.ts +7 -7
  46. package/dist/lib/xterm/src/common/Types.ts +552 -0
  47. package/dist/lib/xterm/src/common/buffer/AttributeData.ts +15 -0
  48. package/dist/lib/xterm/src/common/buffer/Buffer.ts +15 -7
  49. package/dist/lib/xterm/src/common/buffer/BufferLine.ts +53 -22
  50. package/dist/lib/xterm/src/common/buffer/BufferRange.ts +1 -1
  51. package/dist/lib/xterm/src/common/buffer/BufferReflow.ts +9 -6
  52. package/dist/lib/xterm/src/common/buffer/BufferSet.ts +5 -5
  53. package/dist/lib/xterm/src/common/buffer/Constants.ts +10 -2
  54. package/dist/lib/xterm/src/common/buffer/Marker.ts +4 -4
  55. package/dist/lib/xterm/src/common/buffer/Types.ts +52 -0
  56. package/dist/lib/xterm/src/common/input/Keyboard.ts +2 -27
  57. package/dist/lib/xterm/src/common/input/UnicodeV6.ts +18 -5
  58. package/dist/lib/xterm/src/common/input/WriteBuffer.ts +9 -8
  59. package/dist/lib/xterm/src/common/parser/EscapeSequenceParser.ts +13 -13
  60. package/dist/lib/xterm/src/common/parser/Types.ts +275 -0
  61. package/dist/lib/xterm/src/common/public/AddonManager.ts +1 -1
  62. package/dist/lib/xterm/src/common/public/BufferApiView.ts +1 -1
  63. package/dist/lib/xterm/src/common/public/BufferLineApiView.ts +1 -1
  64. package/dist/lib/xterm/src/common/public/BufferNamespaceApi.ts +4 -4
  65. package/dist/lib/xterm/src/common/public/ParserApi.ts +1 -1
  66. package/dist/lib/xterm/src/common/public/UnicodeApi.ts +1 -1
  67. package/dist/lib/xterm/src/common/services/BufferService.ts +14 -11
  68. package/dist/lib/xterm/src/common/services/CoreMouseService.ts +53 -6
  69. package/dist/lib/xterm/src/common/services/CoreService.ts +13 -8
  70. package/dist/lib/xterm/src/common/services/DecorationService.ts +11 -11
  71. package/dist/lib/xterm/src/common/services/InstantiationService.ts +1 -1
  72. package/dist/lib/xterm/src/common/services/LogService.ts +2 -2
  73. package/dist/lib/xterm/src/common/services/OptionsService.ts +16 -5
  74. package/dist/lib/xterm/src/common/services/ServiceRegistry.ts +1 -1
  75. package/dist/lib/xterm/src/common/services/Services.ts +73 -19
  76. package/dist/lib/xterm/src/common/services/UnicodeService.ts +30 -5
  77. package/dist/lib/xterm/src/vs/base/browser/browser.ts +141 -0
  78. package/dist/lib/xterm/src/vs/base/browser/canIUse.ts +49 -0
  79. package/dist/lib/xterm/src/vs/base/browser/dom.ts +2369 -0
  80. package/dist/lib/xterm/src/vs/base/browser/fastDomNode.ts +316 -0
  81. package/dist/lib/xterm/src/vs/base/browser/globalPointerMoveMonitor.ts +112 -0
  82. package/dist/lib/xterm/src/vs/base/browser/iframe.ts +135 -0
  83. package/dist/lib/xterm/src/vs/base/browser/keyboardEvent.ts +213 -0
  84. package/dist/lib/xterm/src/vs/base/browser/mouseEvent.ts +229 -0
  85. package/dist/lib/xterm/src/vs/base/browser/touch.ts +372 -0
  86. package/dist/lib/xterm/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +303 -0
  87. package/dist/lib/xterm/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +114 -0
  88. package/dist/lib/xterm/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +720 -0
  89. package/dist/lib/xterm/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +165 -0
  90. package/dist/lib/xterm/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +114 -0
  91. package/dist/lib/xterm/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +243 -0
  92. package/dist/lib/xterm/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +118 -0
  93. package/dist/lib/xterm/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +116 -0
  94. package/dist/lib/xterm/src/vs/base/browser/ui/widget.ts +57 -0
  95. package/dist/lib/xterm/src/vs/base/browser/window.ts +14 -0
  96. package/dist/lib/xterm/src/vs/base/common/arrays.ts +887 -0
  97. package/dist/lib/xterm/src/vs/base/common/arraysFind.ts +202 -0
  98. package/dist/lib/xterm/src/vs/base/common/assert.ts +71 -0
  99. package/dist/lib/xterm/src/vs/base/common/async.ts +1992 -0
  100. package/dist/lib/xterm/src/vs/base/common/cancellation.ts +148 -0
  101. package/dist/lib/xterm/src/vs/base/common/charCode.ts +450 -0
  102. package/dist/lib/xterm/src/vs/base/common/collections.ts +140 -0
  103. package/dist/lib/xterm/src/vs/base/common/decorators.ts +130 -0
  104. package/dist/lib/xterm/src/vs/base/common/equals.ts +146 -0
  105. package/dist/lib/xterm/src/vs/base/common/errors.ts +303 -0
  106. package/dist/lib/xterm/src/vs/base/common/event.ts +1778 -0
  107. package/dist/lib/xterm/src/vs/base/common/functional.ts +32 -0
  108. package/dist/lib/xterm/src/vs/base/common/hash.ts +316 -0
  109. package/dist/lib/xterm/src/vs/base/common/iterator.ts +159 -0
  110. package/dist/lib/xterm/src/vs/base/common/keyCodes.ts +526 -0
  111. package/dist/lib/xterm/src/vs/base/common/keybindings.ts +284 -0
  112. package/dist/lib/xterm/src/vs/base/common/lazy.ts +47 -0
  113. package/dist/lib/xterm/src/vs/base/common/lifecycle.ts +801 -0
  114. package/dist/lib/xterm/src/vs/base/common/linkedList.ts +142 -0
  115. package/dist/lib/xterm/src/vs/base/common/map.ts +202 -0
  116. package/dist/lib/xterm/src/vs/base/common/numbers.ts +98 -0
  117. package/dist/lib/xterm/src/vs/base/common/observable.ts +76 -0
  118. package/dist/lib/xterm/src/vs/base/common/observableInternal/api.ts +31 -0
  119. package/dist/lib/xterm/src/vs/base/common/observableInternal/autorun.ts +281 -0
  120. package/dist/lib/xterm/src/vs/base/common/observableInternal/base.ts +489 -0
  121. package/dist/lib/xterm/src/vs/base/common/observableInternal/debugName.ts +145 -0
  122. package/dist/lib/xterm/src/vs/base/common/observableInternal/derived.ts +428 -0
  123. package/dist/lib/xterm/src/vs/base/common/observableInternal/lazyObservableValue.ts +146 -0
  124. package/dist/lib/xterm/src/vs/base/common/observableInternal/logging.ts +328 -0
  125. package/dist/lib/xterm/src/vs/base/common/observableInternal/promise.ts +209 -0
  126. package/dist/lib/xterm/src/vs/base/common/observableInternal/utils.ts +610 -0
  127. package/dist/lib/xterm/src/vs/base/common/platform.ts +281 -0
  128. package/dist/lib/xterm/src/vs/base/common/scrollable.ts +522 -0
  129. package/dist/lib/xterm/src/vs/base/common/sequence.ts +34 -0
  130. package/dist/lib/xterm/src/vs/base/common/stopwatch.ts +43 -0
  131. package/dist/lib/xterm/src/vs/base/common/strings.ts +557 -0
  132. package/dist/lib/xterm/src/vs/base/common/symbols.ts +9 -0
  133. package/dist/lib/xterm/src/vs/base/common/uint.ts +59 -0
  134. package/dist/lib/xterm/src/vs/patches/nls.ts +90 -0
  135. package/dist/lib/xterm/src/vs/typings/base-common.d.ts +20 -0
  136. package/dist/lib/xterm/src/vs/typings/require.d.ts +42 -0
  137. package/dist/lib/xterm/src/vs/typings/vscode-globals-nls.d.ts +36 -0
  138. package/dist/lib/xterm/src/vs/typings/vscode-globals-product.d.ts +33 -0
  139. package/dist/lib/xterm/typings/xterm.d.ts +156 -43
  140. package/dist/lib/xterm-fit/README.md +5 -5
  141. package/dist/lib/xterm-fit/lib/addon-fit.js +2 -0
  142. package/dist/lib/xterm-fit/lib/addon-fit.js.map +1 -0
  143. package/dist/lib/xterm-fit/lib/addon-fit.mjs +18 -0
  144. package/dist/lib/xterm-fit/lib/addon-fit.mjs.map +7 -0
  145. package/dist/lib/xterm-fit/package.json +9 -9
  146. package/dist/lib/xterm-fit/src/FitAddon.ts +7 -4
  147. package/dist/lib/xterm-fit/typings/addon-fit.d.ts +55 -0
  148. package/dist/lib/xterm-links/README.md +5 -5
  149. package/dist/lib/xterm-links/lib/addon-web-links.js +2 -0
  150. package/dist/lib/xterm-links/lib/addon-web-links.js.map +1 -0
  151. package/dist/lib/xterm-links/lib/addon-web-links.mjs +18 -0
  152. package/dist/lib/xterm-links/lib/addon-web-links.mjs.map +7 -0
  153. package/dist/lib/xterm-links/package.json +9 -9
  154. package/dist/lib/xterm-links/src/WebLinkProvider.ts +16 -15
  155. package/dist/lib/xterm-links/src/WebLinksAddon.ts +4 -3
  156. package/dist/lib/xterm-links/typings/addon-web-links.d.ts +57 -0
  157. package/package.json +5 -6
@@ -1,12 +1,16 @@
1
1
  {
2
- "name": "xterm",
2
+ "name": "@xterm/xterm",
3
3
  "description": "Full xterm terminal, in your browser",
4
- "version": "5.3.0",
4
+ "version": "6.0.0",
5
5
  "main": "lib/xterm.js",
6
+ "module": "lib/xterm.mjs",
6
7
  "style": "css/xterm.css",
7
8
  "types": "typings/xterm.d.ts",
8
9
  "repository": "https://github.com/xtermjs/xterm.js",
9
10
  "license": "MIT",
11
+ "workspaces": [
12
+ "addons/*"
13
+ ],
10
14
  "keywords": [
11
15
  "cli",
12
16
  "command-line",
@@ -23,52 +27,58 @@
23
27
  "xterm"
24
28
  ],
25
29
  "scripts": {
26
- "prepackage": "npm run build",
27
- "package": "webpack",
28
- "package-headless": "webpack --config ./webpack.config.headless.js",
29
- "postpackage-headless": "node ./bin/package_headless.js",
30
+ "setup": "npm run build",
30
31
  "start": "node demo/start",
31
- "build-demo": "webpack --config ./demo/webpack.config.js",
32
- "start-debug": "node --inspect-brk demo/start",
33
- "lint": "eslint -c .eslintrc.json --max-warnings 0 --ext .ts src/ addons/",
34
- "lint-api": "eslint --no-eslintrc -c .eslintrc.json.typings --max-warnings 0 --no-ignore --ext .d.ts typings/",
32
+ "build": "npm run tsc",
33
+ "watch": "npm run tsc-watch",
34
+ "tsc": "tsc -b ./tsconfig.all.json",
35
+ "tsc-watch": "tsc -b -w ./tsconfig.all.json --preserveWatchOutput",
36
+ "esbuild": "node bin/esbuild_all.mjs",
37
+ "esbuild-watch": "node bin/esbuild_all.mjs --watch",
38
+ "esbuild-package": "node bin/esbuild_all.mjs --prod",
39
+ "esbuild-package-watch": "node bin/esbuild_all.mjs --prod --watch",
40
+ "esbuild-package-headless-only": "node bin/esbuild.mjs --prod --headless",
41
+ "esbuild-demo": "node bin/esbuild.mjs --demo-client",
42
+ "esbuild-demo-watch": "node bin/esbuild.mjs --demo-client --watch",
35
43
  "test": "npm run test-unit",
36
44
  "posttest": "npm run lint",
37
- "test-api": "npm run test-api-chromium",
38
- "test-api-chromium": "node ./bin/test_api.js --browser=chromium --timeout=20000",
39
- "test-api-firefox": "node ./bin/test_api.js --browser=firefox --timeout=20000",
40
- "test-api-webkit": "node ./bin/test_api.js --browser=webkit --timeout=20000",
41
- "test-playwright": "playwright test -c ./out-test/playwright/playwright.config.js --workers 4",
42
- "test-playwright-chromium": "playwright test -c ./out-test/playwright/playwright.config.js --workers 4 --project='Chrome Stable'",
43
- "test-playwright-firefox": "playwright test -c ./out-test/playwright/playwright.config.js --workers 4 --project='Firefox Stable'",
44
- "test-playwright-webkit": "playwright test -c ./out-test/playwright/playwright.config.js --workers 4 --project='WebKit'",
45
- "test-playwright-debug": "playwright test -c ./out-test/playwright/playwright.config.js --headed --workers 1 --timeout 30000",
46
- "test-unit": "node ./bin/test.js",
47
- "test-unit-coverage": "node ./bin/test.js --coverage",
45
+ "lint": "eslint -c .eslintrc.json --max-warnings 0 --ext .ts src/ addons/",
46
+ "lint-fix": "eslint -c .eslintrc.json --fix --ext .ts src/ addons/",
47
+ "lint-api": "eslint --no-eslintrc -c .eslintrc.json.typings --max-warnings 0 --no-ignore --ext .d.ts typings/",
48
+ "test-unit": "node ./bin/test_unit.js",
49
+ "test-unit-coverage": "node ./bin/test_unit.js --coverage",
48
50
  "test-unit-dev": "cross-env NODE_PATH='./out' mocha",
49
- "build": "tsc -b ./tsconfig.all.json",
50
- "install-addons": "node ./bin/install-addons.js",
51
- "presetup": "npm run install-addons",
52
- "setup": "npm run build",
53
- "prepublishOnly": "npm run package",
54
- "watch": "tsc -b -w ./tsconfig.all.json --preserveWatchOutput",
51
+ "test-integration": "node ./bin/test_integration.js --workers=75%",
52
+ "test-integration-chromium": "node ./bin/test_integration.js --workers=75% \"--project=ChromeStable\"",
53
+ "test-integration-firefox": "node ./bin/test_integration.js --workers=75% \"--project=FirefoxStable\"",
54
+ "test-integration-webkit": "node ./bin/test_integration.js --workers=75% \"--project=WebKit\"",
55
+ "test-integration-debug": "node ./bin/test_integration.js --workers=1 --headed --timeout=30000",
55
56
  "benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json",
56
- "benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/test/benchmark/*benchmark.js",
57
- "benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/test/benchmark/*benchmark.js",
57
+ "benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-test/benchmark/*benchmark.js",
58
+ "benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-test/benchmark/*benchmark.js",
58
59
  "clean": "rm -rf lib out addons/*/lib addons/*/out",
59
- "vtfeatures": "node bin/extract_vtfeatures.js src/**/*.ts src/*.ts"
60
+ "vtfeatures": "node bin/extract_vtfeatures.js src/**/*.ts src/*.ts",
61
+ "prepackage": "npm run build",
62
+ "package": "webpack",
63
+ "postpackage": "npm run esbuild-package",
64
+ "prepackage-headless": "npm run esbuild-package-headless-only",
65
+ "package-headless": "webpack --config ./webpack.config.headless.js",
66
+ "postpackage-headless": "node ./bin/package_headless.js",
67
+ "prepublishOnly": "npm run package"
60
68
  },
61
69
  "devDependencies": {
70
+ "@lunapaint/png-codec": "^0.2.0",
62
71
  "@playwright/test": "^1.37.1",
72
+ "@stylistic/eslint-plugin": "^2.3.0",
63
73
  "@types/chai": "^4.2.22",
64
74
  "@types/debug": "^4.1.7",
65
75
  "@types/deep-equal": "^1.0.1",
66
76
  "@types/express": "4",
67
77
  "@types/express-ws": "^3.0.1",
68
- "@types/glob": "^7.2.0",
69
78
  "@types/jsdom": "^16.2.13",
70
79
  "@types/mocha": "^9.0.0",
71
80
  "@types/node": "^18.16.0",
81
+ "@types/trusted-types": "^1.0.6",
72
82
  "@types/utf8": "^3.0.0",
73
83
  "@types/webpack": "^5.28.0",
74
84
  "@types/ws": "^8.2.0",
@@ -77,24 +87,25 @@
77
87
  "chai": "^4.3.4",
78
88
  "cross-env": "^7.0.3",
79
89
  "deep-equal": "^2.0.5",
80
- "eslint": "^8.45.0",
81
- "eslint-plugin-jsdoc": "^39.3.6",
82
- "express": "^4.17.1",
90
+ "esbuild": "~0.25.2",
91
+ "eslint": "^8.56.0",
92
+ "eslint-plugin-jsdoc": "^46.9.1",
93
+ "express": "^4.19.2",
83
94
  "express-ws": "^5.0.2",
84
- "glob": "^7.2.0",
85
95
  "jsdom": "^18.0.1",
86
96
  "mocha": "^10.1.0",
87
97
  "mustache": "^4.2.0",
88
- "node-pty": "^0.10.1",
98
+ "node-pty": "1.1.0-beta19",
89
99
  "nyc": "^15.1.0",
90
100
  "source-map-loader": "^3.0.0",
91
101
  "source-map-support": "^0.5.20",
92
102
  "ts-loader": "^9.3.1",
93
- "typescript": "^5.1.6",
103
+ "typescript": "5.5",
94
104
  "utf8": "^3.0.0",
95
105
  "webpack": "^5.61.0",
96
106
  "webpack-cli": "^4.9.1",
97
107
  "ws": "^8.2.3",
98
108
  "xterm-benchmark": "^0.3.1"
99
- }
109
+ },
110
+ "commit": "f447274f430fd22513f6adbf9862d19524471c04"
100
111
  }
@@ -5,13 +5,12 @@
5
5
 
6
6
  import * as Strings from 'browser/LocalizableStrings';
7
7
  import { ITerminal, IRenderDebouncer } from 'browser/Types';
8
- import { isMac } from 'common/Platform';
9
8
  import { TimeBasedDebouncer } from 'browser/TimeBasedDebouncer';
10
- import { Disposable, toDisposable } from 'common/Lifecycle';
11
- import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
12
- import { IRenderService } from 'browser/services/Services';
13
- import { addDisposableDomListener } from 'browser/Lifecycle';
9
+ import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
10
+ import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
14
11
  import { IBuffer } from 'common/buffer/Types';
12
+ import { IInstantiationService } from 'common/services/Services';
13
+ import { addDisposableListener } from 'vs/base/browser/dom';
15
14
 
16
15
  const MAX_ROWS_TO_READ = 20;
17
16
 
@@ -20,18 +19,22 @@ const enum BoundaryPosition {
20
19
  BOTTOM
21
20
  }
22
21
 
22
+ // Turn this on to unhide the accessibility tree and display it under
23
+ // (instead of overlapping with) the terminal.
24
+ const DEBUG = false;
25
+
23
26
  export class AccessibilityManager extends Disposable {
27
+ private _debugRootContainer: HTMLElement | undefined;
24
28
  private _accessibilityContainer: HTMLElement;
25
29
 
26
30
  private _rowContainer: HTMLElement;
27
31
  private _rowElements: HTMLElement[];
32
+ private _rowColumns: WeakMap<HTMLElement, number[]> = new WeakMap();
28
33
 
29
34
  private _liveRegion: HTMLElement;
30
35
  private _liveRegionLineCount: number = 0;
31
36
  private _liveRegionDebouncer: IRenderDebouncer;
32
37
 
33
- private _screenDprMonitor: ScreenDprMonitor;
34
-
35
38
  private _topBoundaryFocusListener: (e: FocusEvent) => void;
36
39
  private _bottomBoundaryFocusListener: (e: FocusEvent) => void;
37
40
 
@@ -50,13 +53,16 @@ export class AccessibilityManager extends Disposable {
50
53
 
51
54
  constructor(
52
55
  private readonly _terminal: ITerminal,
56
+ @IInstantiationService instantiationService: IInstantiationService,
57
+ @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
53
58
  @IRenderService private readonly _renderService: IRenderService
54
59
  ) {
55
60
  super();
56
- this._accessibilityContainer = document.createElement('div');
61
+ const doc = this._coreBrowserService.mainDocument;
62
+ this._accessibilityContainer = doc.createElement('div');
57
63
  this._accessibilityContainer.classList.add('xterm-accessibility');
58
64
 
59
- this._rowContainer = document.createElement('div');
65
+ this._rowContainer = doc.createElement('div');
60
66
  this._rowContainer.setAttribute('role', 'list');
61
67
  this._rowContainer.classList.add('xterm-accessibility-tree');
62
68
  this._rowElements = [];
@@ -70,41 +76,56 @@ export class AccessibilityManager extends Disposable {
70
76
  this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener);
71
77
  this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);
72
78
 
73
- this._refreshRowsDimensions();
74
79
  this._accessibilityContainer.appendChild(this._rowContainer);
75
80
 
76
- this._liveRegion = document.createElement('div');
81
+ this._liveRegion = doc.createElement('div');
77
82
  this._liveRegion.classList.add('live-region');
78
83
  this._liveRegion.setAttribute('aria-live', 'assertive');
79
84
  this._accessibilityContainer.appendChild(this._liveRegion);
80
- this._liveRegionDebouncer = this.register(new TimeBasedDebouncer(this._renderRows.bind(this)));
85
+ this._liveRegionDebouncer = this._register(new TimeBasedDebouncer(this._renderRows.bind(this)));
81
86
 
82
87
  if (!this._terminal.element) {
83
88
  throw new Error('Cannot enable accessibility before Terminal.open');
84
89
  }
85
- this._terminal.element.insertAdjacentElement('afterbegin', this._accessibilityContainer);
86
90
 
87
- this.register(this._terminal.onResize(e => this._handleResize(e.rows)));
88
- this.register(this._terminal.onRender(e => this._refreshRows(e.start, e.end)));
89
- this.register(this._terminal.onScroll(() => this._refreshRows()));
91
+ if (DEBUG) {
92
+ this._accessibilityContainer.classList.add('debug');
93
+ this._rowContainer.classList.add('debug');
94
+
95
+ // Use a `<div class="xterm">` container so that the css will still apply.
96
+ this._debugRootContainer = doc.createElement('div');
97
+ this._debugRootContainer.classList.add('xterm');
98
+
99
+ this._debugRootContainer.appendChild(doc.createTextNode('------start a11y------'));
100
+ this._debugRootContainer.appendChild(this._accessibilityContainer);
101
+ this._debugRootContainer.appendChild(doc.createTextNode('------end a11y------'));
102
+
103
+ this._terminal.element.insertAdjacentElement('afterend', this._debugRootContainer);
104
+ } else {
105
+ this._terminal.element.insertAdjacentElement('afterbegin', this._accessibilityContainer);
106
+ }
107
+
108
+ this._register(this._terminal.onResize(e => this._handleResize(e.rows)));
109
+ this._register(this._terminal.onRender(e => this._refreshRows(e.start, e.end)));
110
+ this._register(this._terminal.onScroll(() => this._refreshRows()));
90
111
  // Line feed is an issue as the prompt won't be read out after a command is run
91
- this.register(this._terminal.onA11yChar(char => this._handleChar(char)));
92
- this.register(this._terminal.onLineFeed(() => this._handleChar('\n')));
93
- this.register(this._terminal.onA11yTab(spaceCount => this._handleTab(spaceCount)));
94
- this.register(this._terminal.onKey(e => this._handleKey(e.key)));
95
- this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
96
- this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
97
-
98
- this._screenDprMonitor = new ScreenDprMonitor(window);
99
- this.register(this._screenDprMonitor);
100
- this._screenDprMonitor.setListener(() => this._refreshRowsDimensions());
101
- // This shouldn't be needed on modern browsers but is present in case the
102
- // media query that drives the ScreenDprMonitor isn't supported
103
- this.register(addDisposableDomListener(window, 'resize', () => this._refreshRowsDimensions()));
112
+ this._register(this._terminal.onA11yChar(char => this._handleChar(char)));
113
+ this._register(this._terminal.onLineFeed(() => this._handleChar('\n')));
114
+ this._register(this._terminal.onA11yTab(spaceCount => this._handleTab(spaceCount)));
115
+ this._register(this._terminal.onKey(e => this._handleKey(e.key)));
116
+ this._register(this._terminal.onBlur(() => this._clearLiveRegion()));
117
+ this._register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
118
+ this._register(addDisposableListener(doc, 'selectionchange', () => this._handleSelectionChange()));
119
+ this._register(this._coreBrowserService.onDprChange(() => this._refreshRowsDimensions()));
104
120
 
121
+ this._refreshRowsDimensions();
105
122
  this._refreshRows();
106
- this.register(toDisposable(() => {
107
- this._accessibilityContainer.remove();
123
+ this._register(toDisposable(() => {
124
+ if (DEBUG) {
125
+ this._debugRootContainer!.remove();
126
+ } else {
127
+ this._accessibilityContainer.remove();
128
+ }
108
129
  this._rowElements.length = 0;
109
130
  }));
110
131
  }
@@ -130,16 +151,7 @@ export class AccessibilityManager extends Disposable {
130
151
  if (char === '\n') {
131
152
  this._liveRegionLineCount++;
132
153
  if (this._liveRegionLineCount === MAX_ROWS_TO_READ + 1) {
133
- this._liveRegion.textContent += Strings.tooMuchOutput;
134
- }
135
- }
136
-
137
- // Only detach/attach on mac as otherwise messages can go unaccounced
138
- if (isMac) {
139
- if (this._liveRegion.textContent && this._liveRegion.textContent.length > 0 && !this._liveRegion.parentNode) {
140
- setTimeout(() => {
141
- this._accessibilityContainer.appendChild(this._liveRegion);
142
- }, 0);
154
+ this._liveRegion.textContent += Strings.tooMuchOutput.get();
143
155
  }
144
156
  }
145
157
  }
@@ -148,11 +160,6 @@ export class AccessibilityManager extends Disposable {
148
160
  private _clearLiveRegion(): void {
149
161
  this._liveRegion.textContent = '';
150
162
  this._liveRegionLineCount = 0;
151
-
152
- // Only detach/attach on mac as otherwise messages can go unaccounced
153
- if (isMac) {
154
- this._liveRegion.remove();
155
- }
156
163
  }
157
164
 
158
165
  private _handleKey(keyChar: string): void {
@@ -171,17 +178,22 @@ export class AccessibilityManager extends Disposable {
171
178
  const buffer: IBuffer = this._terminal.buffer;
172
179
  const setSize = buffer.lines.length.toString();
173
180
  for (let i = start; i <= end; i++) {
174
- const lineData = buffer.translateBufferLineToString(buffer.ydisp + i, true);
181
+ const line = buffer.lines.get(buffer.ydisp + i);
182
+ const columns: number[] = [];
183
+ const lineData = line?.translateToString(true, undefined, undefined, columns) || '';
175
184
  const posInSet = (buffer.ydisp + i + 1).toString();
176
185
  const element = this._rowElements[i];
177
186
  if (element) {
178
187
  if (lineData.length === 0) {
179
- element.innerText = '\u00a0';
188
+ element.textContent = '\u00a0';
189
+ this._rowColumns.set(element, [0, 1]);
180
190
  } else {
181
191
  element.textContent = lineData;
192
+ this._rowColumns.set(element, columns);
182
193
  }
183
194
  element.setAttribute('aria-posinset', posInSet);
184
195
  element.setAttribute('aria-setsize', setSize);
196
+ this._alignRowWidth(element);
185
197
  }
186
198
  }
187
199
  this._announceCharacters();
@@ -255,6 +267,103 @@ export class AccessibilityManager extends Disposable {
255
267
  e.stopImmediatePropagation();
256
268
  }
257
269
 
270
+ private _handleSelectionChange(): void {
271
+ if (this._rowElements.length === 0) {
272
+ return;
273
+ }
274
+
275
+ const selection = this._coreBrowserService.mainDocument.getSelection();
276
+ if (!selection) {
277
+ return;
278
+ }
279
+
280
+ if (selection.isCollapsed) {
281
+ // Only do something when the anchorNode is inside the row container. This
282
+ // behavior mirrors what we do with mouse --- if the mouse clicks
283
+ // somewhere outside of the terminal, we don't clear the selection.
284
+ if (this._rowContainer.contains(selection.anchorNode)) {
285
+ this._terminal.clearSelection();
286
+ }
287
+ return;
288
+ }
289
+
290
+ if (!selection.anchorNode || !selection.focusNode) {
291
+ console.error('anchorNode and/or focusNode are null');
292
+ return;
293
+ }
294
+
295
+ // Sort the two selection points in document order.
296
+ let begin = { node: selection.anchorNode, offset: selection.anchorOffset };
297
+ let end = { node: selection.focusNode, offset: selection.focusOffset };
298
+ if ((begin.node.compareDocumentPosition(end.node) & Node.DOCUMENT_POSITION_PRECEDING) || (begin.node === end.node && begin.offset > end.offset) ) {
299
+ [begin, end] = [end, begin];
300
+ }
301
+
302
+ // Clamp begin/end to the inside of the row container.
303
+ if (begin.node.compareDocumentPosition(this._rowElements[0]) & (Node.DOCUMENT_POSITION_CONTAINED_BY | Node.DOCUMENT_POSITION_FOLLOWING)) {
304
+ begin = { node: this._rowElements[0].childNodes[0], offset: 0 };
305
+ }
306
+ if (!this._rowContainer.contains(begin.node)) {
307
+ // This happens when `begin` is below the last row.
308
+ return;
309
+ }
310
+ const lastRowElement = this._rowElements.slice(-1)[0];
311
+ if (end.node.compareDocumentPosition(lastRowElement) & (Node.DOCUMENT_POSITION_CONTAINED_BY | Node.DOCUMENT_POSITION_PRECEDING)) {
312
+ end = {
313
+ node: lastRowElement,
314
+ offset: lastRowElement.textContent?.length ?? 0
315
+ };
316
+ }
317
+ if (!this._rowContainer.contains(end.node)) {
318
+ // This happens when `end` is above the first row.
319
+ return;
320
+ }
321
+
322
+ const toRowColumn = ({ node, offset }: typeof begin): {row: number, column: number} | null => {
323
+ // `node` is either the row element or the Text node inside it.
324
+ const rowElement: any = node instanceof Text ? node.parentNode : node;
325
+ let row = parseInt(rowElement?.getAttribute('aria-posinset'), 10) - 1;
326
+ if (isNaN(row)) {
327
+ console.warn('row is invalid. Race condition?');
328
+ return null;
329
+ }
330
+
331
+ const columns = this._rowColumns.get(rowElement);
332
+ if (!columns) {
333
+ console.warn('columns is null. Race condition?');
334
+ return null;
335
+ }
336
+
337
+ let column = offset < columns.length ? columns[offset] : columns.slice(-1)[0] + 1;
338
+ if (column >= this._terminal.cols) {
339
+ ++row;
340
+ column = 0;
341
+ }
342
+ return {
343
+ row,
344
+ column
345
+ };
346
+ };
347
+
348
+ const beginRowColumn = toRowColumn(begin);
349
+ const endRowColumn = toRowColumn(end);
350
+
351
+ if (!beginRowColumn || !endRowColumn) {
352
+ return;
353
+ }
354
+
355
+ if (beginRowColumn.row > endRowColumn.row || (beginRowColumn.row === endRowColumn.row && beginRowColumn.column >= endRowColumn.column)) {
356
+ // This should not happen unless we have some bugs.
357
+ throw new Error('invalid range');
358
+ }
359
+
360
+ this._terminal.select(
361
+ beginRowColumn.column,
362
+ beginRowColumn.row,
363
+ (endRowColumn.row - beginRowColumn.row) * this._terminal.cols - beginRowColumn.column + endRowColumn.column
364
+ );
365
+ }
366
+
258
367
  private _handleResize(rows: number): void {
259
368
  // Remove bottom boundary listener
260
369
  this._rowElements[this._rowElements.length - 1].removeEventListener('focus', this._bottomBoundaryFocusListener);
@@ -276,25 +385,51 @@ export class AccessibilityManager extends Disposable {
276
385
  }
277
386
 
278
387
  private _createAccessibilityTreeNode(): HTMLElement {
279
- const element = document.createElement('div');
388
+ const element = this._coreBrowserService.mainDocument.createElement('div');
280
389
  element.setAttribute('role', 'listitem');
281
390
  element.tabIndex = -1;
282
391
  this._refreshRowDimensions(element);
283
392
  return element;
284
393
  }
394
+
285
395
  private _refreshRowsDimensions(): void {
286
396
  if (!this._renderService.dimensions.css.cell.height) {
287
397
  return;
288
398
  }
289
- this._accessibilityContainer.style.width = `${this._renderService.dimensions.css.canvas.width}px`;
399
+ Object.assign(this._accessibilityContainer.style, {
400
+ width: `${this._renderService.dimensions.css.canvas.width}px`,
401
+ fontSize: `${this._terminal.options.fontSize}px`
402
+ });
290
403
  if (this._rowElements.length !== this._terminal.rows) {
291
404
  this._handleResize(this._terminal.rows);
292
405
  }
293
406
  for (let i = 0; i < this._terminal.rows; i++) {
294
407
  this._refreshRowDimensions(this._rowElements[i]);
408
+ this._alignRowWidth(this._rowElements[i]);
295
409
  }
296
410
  }
411
+
297
412
  private _refreshRowDimensions(element: HTMLElement): void {
298
413
  element.style.height = `${this._renderService.dimensions.css.cell.height}px`;
299
414
  }
415
+
416
+ /**
417
+ * Scale the width of a row so that each of the character is (mostly) aligned
418
+ * with the actual rendering. This will allow the screen reader to draw
419
+ * selection outline at the correct position.
420
+ *
421
+ * On top of using the "monospace" font and correct font size, the scaling
422
+ * here is necessary to handle characters that are not covered by the font
423
+ * (e.g. CJK).
424
+ */
425
+ private _alignRowWidth(element: HTMLElement): void {
426
+ element.style.transform = '';
427
+ const width = element.getBoundingClientRect().width;
428
+ const lastColumn = this._rowColumns.get(element)?.slice(-1)?.[0];
429
+ if (!lastColumn) {
430
+ return;
431
+ }
432
+ const targetWidth = lastColumn * this._renderService.dimensions.css.cell.width;
433
+ element.style.transform = `scaleX(${targetWidth / width})`;
434
+ }
300
435
  }