Compare commits

...
160 Commits
Author SHA1 Message Date
Daniel Imms a8fbc8d7a3 Merge pull request #3664 from hello-smile6/patch-1
Add libv86 terminal forwarding to README "Programs using xterm.js"
2022-02-28 05:39:36 -08:00
Daniel Imms c5a48043ae Merge pull request #3663 from childrentime/dev_wlw
Make registerMarker's cursor offset optional
2022-02-28 05:33:51 -08:00
儿时 9b02e950f8 Make registerMarker's cursor offset optional 2022-02-27 13:25:07 +08:00
hello-smile6 14366e7c40 Add libv86 terminal forwarding
https://github.com/hello-smile6/libv86-terminal-forwarding
2022-02-26 16:54:05 -08:00
Daniel Imms e1c149521a Merge pull request #3662 from Tyriar/tyriar/faster_tasks
Make vscode tasks faster on reload
2022-02-25 11:34:52 -08:00
Daniel Imms f5ccf75781 Merge branch 'master' into tyriar/faster_tasks 2022-02-25 11:25:42 -08:00
Daniel Imms 771d5f580a Simplify task names 2022-02-25 11:25:38 -08:00
Daniel Imms 336b45a645 Make vscode tasks faster on reload
See microsoft/vscode#142413
2022-02-25 11:24:40 -08:00
Megan Rogge 0f15a78ba2 Merge pull request #3661 from meganrogge/master
set decoration line height
2022-02-24 13:13:32 -06:00
meganrogge 69d6d53631 Merge branch 'master' of https://github.com/meganrogge/xterm.js 2022-02-24 12:24:37 -06:00
meganrogge 2c76f9bee7 remove height as a factor for lineheight 2022-02-24 12:24:28 -06:00
Megan Rogge 0314d95a32 Merge branch 'xtermjs:master' into master 2022-02-24 12:19:31 -06:00
meganrogge d06a2e1ad7 set line height of decoration 2022-02-24 12:19:05 -06:00
Megan Rogge 134e1ebb5f Merge pull request #3659 from meganrogge/master
use proper y value for clearing markers
2022-02-24 12:18:54 -06:00
meganrogge 3c5b73df8e :Merge branch 'master' of https://github.com/meganrogge/xterm.js 2022-02-24 12:07:59 -06:00
meganrogge ef0312473e re-add i-- so that markers on the same line get disposed of 2022-02-24 12:07:54 -06:00
Megan Rogge 7003c24126 Merge branch 'xtermjs:master' into master 2022-02-24 12:02:25 -06:00
meganrogge 34ec80a322 use ybase + y when clearing markers 2022-02-24 12:02:11 -06:00
Megan Rogge ded6750ff5 Merge pull request #3658 from meganrogge/master
fix marker splice by decrementing index
2022-02-23 16:14:59 -06:00
Megan Rogge d2e34277e8 Merge branch 'xtermjs:master' into master 2022-02-23 16:02:28 -06:00
meganrogge e8579bd655 fix marker splice by decrementing index 2022-02-23 16:02:08 -06:00
Daniel Imms a4f5cbf0cc Merge pull request #3656 from Tyriar/decoration_queue
Queue a refresh when registering a decoration
2022-02-23 13:50:29 -08:00
Daniel Imms b6937d05cd Merge branch 'master' into decoration_queue 2022-02-23 13:41:46 -08:00
Daniel Imms 56b824d379 Queue a refresh when registering a decoration
Part of microsoft/vscode#143798
2022-02-23 13:40:56 -08:00
Megan Rogge 72f7e3fac3 Merge pull request #3655 from meganrogge/master
get Decoration dispose to actually work and revert document role accessibility change
2022-02-22 17:51:13 -06:00
meganrogge d90bbdaa8a Merge branch 'master' of https://github.com/meganrogge/xterm.js 2022-02-22 17:40:43 -06:00
meganrogge 8df23582d2 remove super.dispose call 2022-02-22 17:40:37 -06:00
Megan Rogge e5459d7e24 Merge branch 'master' into master 2022-02-22 14:42:40 -06:00
meganrogge b3d9104136 Revert "Add document role to accessibility tree root"
This reverts commit 3a71b3f11e.
2022-02-22 14:33:30 -06:00
Megan Rogge 912284c24c Merge pull request #3654 from Tyriar/clear_marker
Fix clearMarkers when y=0 and avoid extra indexOf iterations
2022-02-22 14:24:40 -06:00
meganrogge dd86776c6c Merge branch 'master' of https://github.com/meganrogge/xterm.js 2022-02-22 14:18:43 -06:00
meganrogge 44bc9a1f58 don't dispose of marker on dispose of decoration 2022-02-22 14:18:39 -06:00
meganrogge 7b9d775a97 Merge branch 'master' of https://github.com/meganrogge/xterm.js 2022-02-22 14:14:09 -06:00
meganrogge 1da492646c re-add dispose of marker 2022-02-22 14:14:05 -06:00
Megan Rogge e7743c4baf Merge branch 'xtermjs:master' into master 2022-02-22 14:12:42 -06:00
meganrogge cd30f54276 get dispose to work 2022-02-22 14:12:29 -06:00
Megan Rogge a923953373 Merge branch 'master' into clear_marker 2022-02-22 14:11:13 -06:00
Megan Rogge c16b45cc1c Merge pull request #3619 from silamon/htmlserialize
HTML serialization
2022-02-22 14:11:00 -06:00
Megan Rogge 365a07968b Merge branch 'master' into htmlserialize 2022-02-22 10:09:49 -06:00
Daniel Imms da5c24a33f Fix clearMarkers when y=0 and avoid extra indexOf 2022-02-22 06:38:01 -08:00
Simon Lamon 92a61e6506 tests for serializeAsHTML 2022-02-19 13:17:26 +00:00
Megan Rogge e08962b8b8 Merge pull request #3649 from meganrogge/master
on buffer clear, update markers array once each has been disposed of
2022-02-18 17:58:50 -06:00
meganrogge f839a0da8b use splice instead of filter 2022-02-18 17:50:12 -06:00
meganrogge 1efdc2e4ce Merge branch 'master' of https://github.com/meganrogge/xterm.js 2022-02-18 16:11:47 -06:00
meganrogge a4fee6d4b5 get rid of the bane of my existence 2022-02-18 16:11:00 -06:00
Megan Rogge ed24e952f8 Merge branch 'xtermjs:master' into master 2022-02-18 15:38:26 -06:00
meganrogge 5b44deaa30 get rid of markers once they've been disposed via clear 2022-02-18 15:38:07 -06:00
Megan Rogge db1fd267a6 Merge pull request #3644 from meganrogge/master 2022-02-15 21:13:36 -06:00
meganrogge 816f62cebc remove unnecessary prop 2022-02-15 15:36:03 -06:00
meganrogge b8e003768d Merge branch 'master' of https://github.com/meganrogge/xterm.js 2022-02-15 11:57:27 -06:00
meganrogge 7134eaf20c remove trailing space 2022-02-15 11:57:20 -06:00
Megan Rogge b5477f71a8 Merge branch 'xtermjs:master' into master 2022-02-15 11:51:57 -06:00
meganrogge f42e78d832 fix https://github.com/microsoft/vscode/issues/143076 2022-02-15 11:47:41 -06:00
meganrogge 9847dcaacc fix #3641 2022-02-15 11:38:04 -06:00
Daniel Imms d943aa3e5b Merge pull request #3638 from xtermjs/dependabot/npm_and_yarn/addons/xterm-addon-ligatures/follow-redirects-1.14.8
Bump follow-redirects from 1.14.7 to 1.14.8 in /addons/xterm-addon-ligatures
2022-02-15 07:03:35 -08:00
dependabot[bot] 8098fd8d01 Bump follow-redirects in /addons/xterm-addon-ligatures
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-13 12:22:04 +00:00
Simon Lamon 4ca26f7e7b text improvements 2022-02-12 09:42:08 +00:00
Megan Rogge 4da2db8e0b Merge pull request #3637 from meganrogge/master
polish decorations
2022-02-11 17:19:34 -06:00
meganrogge 8dfed8f979 be safe and check if container contains element before removing 2022-02-11 17:08:08 -06:00
meganrogge e720643315 get rid of unused 2022-02-11 17:04:11 -06:00
meganrogge 40e6dba43c cleaner 2022-02-11 17:00:24 -06:00
meganrogge 7d357b941f check if container has child before removing it 2022-02-11 16:48:40 -06:00
meganrogge 3f525fb960 fix #3636 2022-02-11 15:40:44 -06:00
meganrogge de3a09e05f fix #3633 2022-02-11 15:09:40 -06:00
meganrogge d8573e38a8 cleanup dispose 2022-02-11 15:01:07 -06:00
meganrogge e44659e8bd remove unused ID - can just use marker's 2022-02-11 14:56:56 -06:00
meganrogge f160436df8 dispose of decoration on dispose of marker 2022-02-11 14:54:01 -06:00
meganrogge 06357b1184 get rid of some unnecessary code 2022-02-11 14:08:46 -06:00
Megan Rogge d64cbc41d5 Merge branch 'master' into htmlserialize 2022-02-10 10:29:43 -06:00
Megan Rogge 71bfbe434b Merge pull request #3630 from meganrogge/merogge/buffer-marker-decoration
add Decorations
2022-02-09 19:30:49 -06:00
meganrogge 9ed95206c1 try to fix tests 2022-02-09 18:48:41 -06:00
meganrogge 14b71b611e fix test 2022-02-09 18:38:47 -06:00
meganrogge 881bc4f0a8 different approach 2022-02-09 17:37:07 -06:00
meganrogge b3b9f3c7cd add poll for 2022-02-09 17:17:36 -06:00
meganrogge 74246497a4 get term to scroll so it refreshes 2022-02-09 15:29:28 -06:00
meganrogge bf5fed083b fix test 2022-02-09 14:55:24 -06:00
meganrogge c7df6e3c2e inject bufferService 2022-02-09 14:50:02 -06:00
meganrogge 6a79017484 on dispose, remove container 2022-02-09 14:33:33 -06:00
meganrogge e5d4e96aad fix test 2022-02-09 14:30:53 -06:00
meganrogge 93bfc18237 fix test 2022-02-09 12:10:50 -06:00
meganrogge 87675649e1 hide if invalid x 2022-02-09 11:55:17 -06:00
meganrogge e2a402d80e add tests 2022-02-09 11:51:25 -06:00
meganrogge 3e204584d9 refresh on dimensions changed 2022-02-09 10:12:25 -06:00
meganrogge 95c9fe11fb save options in constructor 2022-02-09 10:07:20 -06:00
meganrogge 313cb2fe09 add a container 2022-02-09 09:55:30 -06:00
meganrogge 8e75cecba3 change how rendering happens 2022-02-09 09:44:24 -06:00
meganrogge a2721d43fd Merge branch 'merogge/buffer-marker-decoration' of https://github.com/meganrogge/xterm.js into merogge/buffer-marker-decoration 2022-02-09 08:57:14 -06:00
meganrogge db9bdd46b4 fix some things 2022-02-09 08:56:45 -06:00
Megan RoggeandDaniel Imms 591f32d3eb Update src/browser/Terminal.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
2022-02-09 08:51:05 -06:00
meganrogge fb4b19a393 polish 2022-02-09 08:47:10 -06:00
meganrogge ae958c2b12 fix the problem 2022-02-08 21:41:26 -06:00
meganrogge 216849b135 Merge branch 'merogge/buffer-marker-decoration' of https://github.com/meganrogge/xterm.js into merogge/buffer-marker-decoration 2022-02-08 21:31:00 -06:00
meganrogge cfe306fa32 break things 2022-02-08 21:30:11 -06:00
Megan RoggeandDaniel Imms a9b496f06f Update src/browser/services/DecorationService.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
2022-02-08 21:18:31 -06:00
meganrogge f2991a5636 cleanup 2022-02-08 21:10:34 -06:00
Megan RoggeandDaniel Imms 53606820a7 Update src/browser/services/DecorationService.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
2022-02-08 21:02:15 -06:00
Megan RoggeandDaniel Imms ad9818fb1a Update typings/xterm.d.ts
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
2022-02-08 20:29:20 -06:00
meganrogge 88bd33f2d7 bring jumpyness back 2022-02-08 17:27:15 -06:00
meganrogge 5fd7216bf2 add api tests 2022-02-08 15:57:43 -06:00
meganrogge a9722371e1 add decoration test to demo 2022-02-08 15:14:36 -06:00
meganrogge 58c2ba5310 remove empty line 2022-02-08 14:41:33 -06:00
meganrogge d8234affa4 move listener into terminal.ts 2022-02-08 14:35:33 -06:00
meganrogge 571bf9e533 fix bouncing on re render 2022-02-08 14:32:29 -06:00
meganrogge 723e915895 more decorationsService -> singular 2022-02-08 14:25:24 -06:00
meganrogge 3e5ecbf52b more formatting 2022-02-08 14:23:59 -06:00
meganrogge 01b66b756f revert formatting changes 2022-02-08 14:22:37 -06:00
meganrogge 275ae6b2d3 DecorationsService -> DecorationService 2022-02-08 14:20:18 -06:00
meganrogge 094763ea8b Revert changes to webglRenderer 2022-02-08 14:03:49 -06:00
meganrogge 3299315822 remove elt from screen on dispose 2022-02-08 12:26:19 -06:00
Megan Rogge 10d2e66137 Merge branch 'master' into merogge/buffer-marker-decoration 2022-02-08 12:07:13 -06:00
meganrogge 19b9d0eadd cleanup 2022-02-08 12:05:38 -06:00
meganrogge cae9f44f5c remove mention of buffer 2022-02-08 11:45:22 -06:00
meganrogge 8b540c5f3b set initial top value 2022-02-07 16:37:34 -06:00
meganrogge 8bf3cd1102 add to test 2022-02-07 16:06:16 -06:00
meganrogge b226eb2d64 throw for negative x 2022-02-07 14:49:11 -06:00
Megan Rogge e96b709d49 Merge branch 'master' into htmlserialize 2022-02-07 10:44:49 -06:00
meganrogge ac6877aba7 clean up 2022-02-07 10:14:56 -06:00
Megan Rogge a13f11adb4 Merge pull request #3628 from meganrogge/merogge/marker-clear 2022-02-04 21:04:06 -06:00
meganrogge 494aef1c94 remove .only 2022-02-04 19:59:10 -06:00
meganrogge 10adc87a3e add api test 2022-02-04 19:32:34 -06:00
meganrogge 4d7d666c71 add test 2022-02-04 18:00:13 -06:00
meganrogge ac7486856c improve conditional 2022-02-04 17:41:54 -06:00
meganrogge 8999d3948a fix test and improve name 2022-02-04 17:39:37 -06:00
meganrogge f3bd1e24b7 undo formatting change 2022-02-04 16:13:20 -06:00
meganrogge e9913522ac fix buffers not clearing 2022-02-04 16:11:04 -06:00
meganrogge 8f7db4b270 fix buffers not clearing 2022-02-04 16:06:53 -06:00
Megan Rogge 3745496d73 Merge branch 'xtermjs:master' into master 2022-02-04 15:58:01 -06:00
Megan Rogge a64d352d48 Merge branch 'master' into htmlserialize 2022-02-03 23:15:01 -06:00
meganrogge 42d403807c use css class for statics 2022-02-03 13:44:08 -06:00
meganrogge b2f541d4a6 clean up 2022-02-03 13:36:11 -06:00
meganrogge 912c33ca67 get it to wrk 2022-02-03 13:33:30 -06:00
Simon Lamon 4c1722da2c fixup text/plain removal 2022-02-03 18:30:28 +00:00
Simon Lamon 4d70606feb Apply feedback from code review 2022-02-03 18:00:16 +00:00
silamonandDaniel Imms 16257386b1 Apply suggestions from code review
Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
2022-02-03 18:55:07 +01:00
Daniel Imms a63890e627 Merge pull request #3624 from Tyriar/3617_clear_on_buffer_change
Ensure the glyph vertices are cleared when switching buffers
2022-02-03 08:53:51 -08:00
Daniel Imms 904b491521 Merge branch 'master' into 3617_clear_on_buffer_change 2022-02-03 08:43:53 -08:00
Daniel Imms fea1079b1e Force clear of the glyph renderer when switching
Fixes #3617
2022-02-03 08:33:22 -08:00
Daniel Imms b39d245050 Log parsing data string when using buffers 2022-02-03 08:32:24 -08:00
Daniel Imms b557ebe0a2 Merge pull request #3623 from Tyriar/mocha_update
Update mocha
2022-02-03 07:35:54 -08:00
Daniel Imms bb44dbfc51 Merge branch 'master' into mocha_update 2022-02-03 07:21:49 -08:00
Daniel Imms 490bf81b86 Update mocha 2022-02-03 07:21:00 -08:00
meganrogge ad59db9a8a try 2022-02-02 22:33:05 -06:00
meganrogge 49085bbd5b use decorations service instead of decorations render layer 2022-02-02 20:51:28 -06:00
Simon Lamon 93ac9efe92 Use alternate buffer if alternate buffer is active 2022-02-02 19:21:02 +00:00
Simon Lamon bc9e6b49e8 Fix serialize tests 2022-02-02 18:57:41 +00:00
Simon Lamon adedfed2bd Fix serialize tests 2022-02-02 17:44:26 +00:00
Simon Lamon 9473777e43 Fix serialize tests 2022-02-02 17:18:35 +00:00
Megan Rogge a359647683 Merge branch 'master' into htmlserialize 2022-02-02 11:13:59 -06:00
Simon Lamon 09a77998ba improvements for html serialize 2022-02-02 06:27:01 +00:00
meganrogge 5ccb41b9b9 re add height and width 2022-02-01 10:32:09 -06:00
meganrogge 7d7726817a improve docs 2022-01-31 14:24:24 -06:00
meganrogge fc6245122d more cleanup 2022-01-31 13:08:09 -06:00
meganrogge c5a0711f84 clean up 2022-01-31 12:53:07 -06:00
meganrogge 9b4801fb43 more polish 2022-01-30 00:36:31 -06:00
meganrogge d7854bc154 remove unused imports 2022-01-30 00:29:02 -06:00
meganrogge f9ac540031 polish 2022-01-30 00:28:01 -06:00
meganrogge 00ce85166e better button 2022-01-29 22:34:16 -06:00
Simon Lamon 6dc813bdff html serialization 2022-01-29 19:49:57 +01:00
meganrogge ebdd07b8ae get button to work 2022-01-29 00:07:51 -06:00
meganrogge c5810e6d54 Take 1 of api 2022-01-27 13:20:02 -06:00
30 changed files with 949 additions and 89 deletions
+4 -2
View File
@@ -18,6 +18,7 @@
"problemMatcher": []
},
{
"label": "watch",
"type": "npm",
"script": "watch",
"group": "build",
@@ -28,9 +29,10 @@
}
},
{
"label": "start",
"type": "npm",
"script": "start",
"dependsOn": "npm: watch",
"dependsOn": "watch",
"group": "build",
"isBackground": true,
"problemMatcher": [],
@@ -40,7 +42,7 @@
},
{
"label": "Start demo",
"dependsOn": "npm: start",
"dependsOn": "start",
"group": {
"kind": "build",
"isDefault": true
+1
View File
@@ -188,6 +188,7 @@ Xterm.js is used in several world-class applications to provide great terminal e
- [**OpenSumi**](https://github.com/opensumi/core): A framework helps you quickly build Cloud or Desktop IDE products.
- [**KubeSail**](https://kubesail.com): The Self-Hosting Company - uses xterm to allow users to exec into kubernetes pods and build github apps
- [**WiTTY**](https://github.com/syssecfsu/witty): Web-based interactive terminal emulator that allows users to easily record, share, and replay console sessions.
- [**libv86 Terminal Forwarding**](https://github.com/hello-smile6/libv86-terminal-forwarding): Peer-to-peer SSH for the web, using WebRTC via [Bugout](https://github.com/chr15m/bugout) for data transfer and [v86](https://github.com/copy/v86) for web-based virtualization.
- [And much more...](https://github.com/xtermjs/xterm.js/network/dependents?package_id=UGFja2FnZS0xNjYzMjc4OQ%3D%3D)
Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it on our list. Note: Please add any new contributions to the end of the list only.
+3 -3
View File
@@ -67,9 +67,9 @@ fd-slicer@~1.1.0:
pend "~1.2.0"
follow-redirects@^1.14.0:
version "1.14.7"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
version "1.14.8"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
font-finder@^1.0.3:
version "1.0.4"
@@ -0,0 +1,205 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import jsdom = require('jsdom');
import { assert } from 'chai';
import { SerializeAddon } from './SerializeAddon';
import { Terminal } from 'browser/public/Terminal';
import { ColorManager } from 'browser/ColorManager';
import { SelectionModel } from 'browser/selection/SelectionModel';
import { IBufferService } from 'common/services/Services';
function sgr(...seq: string[]): string {
return `\x1b[${seq.join(';')}m`;
}
function writeP(terminal: Terminal, data: string | Uint8Array): Promise<void> {
return new Promise(r => terminal.write(data, r));
}
class TestSelectionService {
private _model: SelectionModel;
private _hasSelection: boolean = false;
constructor(
bufferService: IBufferService
) {
this._model = new SelectionModel(bufferService);
}
public get model(): SelectionModel { return this._model; }
public get hasSelection(): boolean { return this._hasSelection; }
public get selectionStart(): [number, number] | undefined { return this._model.finalSelectionStart; }
public get selectionEnd(): [number, number] | undefined { return this._model.finalSelectionEnd; }
public setSelection(col: number, row: number, length: number): void {
this._model.selectionStart = [col, row];
this._model.selectionStartLength = length;
this._hasSelection = true;
}
}
describe('xterm-addon-serialize html', () => {
let cm: ColorManager;
let dom: jsdom.JSDOM;
let document: Document;
let window: jsdom.DOMWindow;
let serializeAddon: SerializeAddon;
let terminal: Terminal;
let selectionService: any;
before(() => {
serializeAddon = new SerializeAddon();
});
beforeEach(() => {
dom = new jsdom.JSDOM('');
window = dom.window;
document = window.document;
(window as any).HTMLCanvasElement.prototype.getContext = () => ({
createLinearGradient(): any {
return null;
},
fillRect(): void { },
getImageData(): any {
return { data: [0, 0, 0, 0xFF] };
}
});
terminal = new Terminal({ cols: 10, rows: 2 });
terminal.loadAddon(serializeAddon);
selectionService = new TestSelectionService((terminal as any)._core._bufferService);
cm = new ColorManager(document, false);
(terminal as any)._core._colorManager = cm;
(terminal as any)._core._selectionService = selectionService;
});
it('empty terminal with selection turned off', () => {
const output = serializeAddon.serializeAsHTML();
assert.notEqual(output, '');
assert.equal((output.match(new RegExp('<div><span> {10}</span><\/div>', 'g')) || []).length, 2);
});
it('empty terminal with no selection', () => {
const output = serializeAddon.serializeAsHTML({
onlySelection: true
});
assert.equal(output, '');
});
it('basic terminal with selection', async () => {
await writeP(terminal, ' terminal ');
terminal.select(1, 0, 8);
const output = serializeAddon.serializeAsHTML({
onlySelection: true
});
assert.equal((output.match(new RegExp('<div><span>terminal<\/span><\/div>', 'g')) || []).length, 1, output);
});
it('cells with bold styling', async () => {
await writeP(terminal, ' ' + sgr('1') + 'terminal' + sgr('22') + ' ');
const output = serializeAddon.serializeAsHTML();
assert.equal((output.match(new RegExp('<span style=\'font-weight: bold;\'>terminal<\/span>', 'g')) || []).length, 1, output);
});
it('cells with italic styling', async () => {
await writeP(terminal, ' ' + sgr('3') + 'terminal' + sgr('23') + ' ');
const output = serializeAddon.serializeAsHTML();
assert.equal((output.match(new RegExp('<span style=\'font-style: italic;\'>terminal<\/span>', 'g')) || []).length, 1, output);
});
it('cells with inverse styling', async () => {
await writeP(terminal, ' ' + sgr('7') + 'terminal' + sgr('27') + ' ');
const output = serializeAddon.serializeAsHTML();
assert.equal((output.match(new RegExp('<span style=\'color: #000000; background-color: #BFBFBF;\'>terminal<\/span>', 'g')) || []).length, 1, output);
});
it('cells with underline styling', async () => {
await writeP(terminal, ' ' + sgr('4') + 'terminal' + sgr('24') + ' ');
const output = serializeAddon.serializeAsHTML();
assert.equal((output.match(new RegExp('<span style=\'text-decoration: underline;\'>terminal<\/span>', 'g')) || []).length, 1, output);
});
it('cells with invisible styling', async () => {
await writeP(terminal, ' ' + sgr('8') + 'terminal' + sgr('28') + ' ');
const output = serializeAddon.serializeAsHTML();
assert.equal((output.match(new RegExp('<span style=\'visibility: hidden;\'>terminal<\/span>', 'g')) || []).length, 1, output);
});
it('cells with dim styling', async () => {
await writeP(terminal, ' ' + sgr('2') + 'terminal' + sgr('22') + ' ');
const output = serializeAddon.serializeAsHTML();
assert.equal((output.match(new RegExp('<span style=\'opacity: 0.5;\'>terminal<\/span>', 'g')) || []).length, 1, output);
});
it('cells with strikethrough styling', async () => {
await writeP(terminal, ' ' + sgr('9') + 'terminal' + sgr('29') + ' ');
const output = serializeAddon.serializeAsHTML();
assert.equal((output.match(new RegExp('<span style=\'text-decoration: line-through;\'>terminal<\/span>', 'g')) || []).length, 1, output);
});
it('cells with combined styling', async () => {
await writeP(terminal, sgr('1') + ' ' + sgr('9') + 'termi' + sgr('22') + 'nal' + sgr('29') + ' ');
const output = serializeAddon.serializeAsHTML();
assert.equal((output.match(new RegExp('<span style=\'font-weight: bold;\'> <\/span>', 'g')) || []).length, 1, output);
assert.equal((output.match(new RegExp('<span style=\'font-weight: bold; text-decoration: line-through;\'>termi<\/span>', 'g')) || []).length, 1, output);
assert.equal((output.match(new RegExp('<span style=\'text-decoration: line-through;\'>nal<\/span>', 'g')) || []).length, 1, output);
});
it('cells with color styling', async () => {
await writeP(terminal, ' ' + sgr('38;5;46') + 'terminal' + sgr('39') + ' ');
const output = serializeAddon.serializeAsHTML();
assert.equal((output.match(new RegExp('<span style=\'color: #00ff00;\'>terminal<\/span>', 'g')) || []).length, 1, output);
});
it('cells with background styling', async () => {
await writeP(terminal, ' ' + sgr('48;5;46') + 'terminal' + sgr('49') + ' ');
const output = serializeAddon.serializeAsHTML();
assert.equal((output.match(new RegExp('<span style=\'background-color: #00ff00;\'>terminal<\/span>', 'g')) || []).length, 1, output);
});
it('empty terminal with default options', async () => {
const output = serializeAddon.serializeAsHTML();
assert.equal((output.match(new RegExp('color: #000000; background-color: #ffffff; font-family: courier-new, courier, monospace; font-size: 15px;', 'g')) || []).length, 1, output);
});
it('empty terminal with custom options', async () => {
terminal.options.fontFamily = 'verdana';
terminal.options.fontSize = 20;
terminal.options.theme = {
foreground: '#ff00ff',
background: '#00ff00'
};
const output = serializeAddon.serializeAsHTML({
includeGlobalBackground: true
});
assert.equal((output.match(new RegExp('color: #ff00ff; background-color: #00ff00; font-family: verdana; font-size: 20px;', 'g')) || []).length, 1, output);
});
it('empty terminal with background included', async () => {
const output = serializeAddon.serializeAsHTML({
includeGlobalBackground: true
});
assert.equal((output.match(new RegExp('color: #ffffff; background-color: #000000; font-family: courier-new, courier, monospace; font-size: 15px;', 'g')) || []).length, 1, output);
});
});
@@ -5,8 +5,8 @@
* (EXPERIMENTAL) This Addon is still under development
*/
import { Terminal, ITerminalAddon, IBuffer, IBufferCell } from 'xterm';
import { Terminal, ITerminalAddon, IBuffer, IBufferCell, IBufferRange } from 'xterm';
import { IColorSet } from 'browser/Types';
function constrain(value: number, low: number, high: number): number {
return Math.max(low, Math.min(value, high));
@@ -19,18 +19,25 @@ abstract class BaseSerializeHandler {
) {
}
public serialize(startRow: number, endRow: number): string {
public serialize(range: IBufferRange): string {
// we need two of them to flip between old and new cell
const cell1 = this._buffer.getNullCell();
const cell2 = this._buffer.getNullCell();
let oldCell = cell1;
const startRow = range.start.x;
const endRow = range.end.x;
const startColumn = range.start.y;
const endColumn = range.end.y;
this._beforeSerialize(endRow - startRow, startRow, endRow);
for (let row = startRow; row < endRow; row++) {
for (let row = startRow; row <= endRow; row++) {
const line = this._buffer.getLine(row);
if (line) {
for (let col = 0; col < line.length; col++) {
const startLineColumn = row !== range.start.x ? 0 : startColumn;
const endLineColumn = row !== range.end.x ? line.length : endColumn;
for (let col = startLineColumn; col < endLineColumn; col++) {
const c = line.getCell(col, oldCell === cell1 ? cell2 : cell1);
if (!c) {
console.warn(`Can't get cell at row=${row}, col=${col}`);
@@ -40,7 +47,7 @@ abstract class BaseSerializeHandler {
oldCell = c;
}
}
this._rowEnd(row, row === endRow - 1);
this._rowEnd(row, row === endRow);
}
this._afterSerialize();
@@ -403,7 +410,35 @@ export class SerializeAddon implements ITerminalAddon {
const maxRows = buffer.length;
const handler = new StringSerializeHandler(buffer, terminal);
const correctRows = (scrollback === undefined) ? maxRows : constrain(scrollback + terminal.rows, 0, maxRows);
return handler.serialize(maxRows - correctRows, maxRows);
return handler.serialize({
start: { x: maxRows - correctRows, y: 0 },
end: { x: maxRows - 1, y: terminal.cols }
});
}
private _serializeBufferAsHTML(terminal: Terminal, options: Partial<IHTMLSerializeOptions>): string {
const buffer = terminal.buffer.active;
const handler = new HTMLSerializeHandler(buffer, terminal, options);
const onlySelection = options.onlySelection ?? false;
if (!onlySelection) {
const maxRows = buffer.length;
const scrollback = options.scrollback;
const correctRows = (scrollback === undefined) ? maxRows : constrain(scrollback + terminal.rows, 0, maxRows);
return handler.serialize({
start: { x: maxRows - correctRows, y: 0 },
end: { x: maxRows - 1, y: terminal.cols }
});
}
const selection = this._terminal?.getSelectionPosition();
if (selection !== undefined) {
return handler.serialize({
start: { x: selection.startRow, y: selection.startColumn },
end: { x: selection.endRow, y: selection.endColumn }
});
}
return '';
}
private _serializeModes(terminal: Terminal): string {
@@ -460,6 +495,14 @@ export class SerializeAddon implements ITerminalAddon {
return content;
}
public serializeAsHTML(options?: Partial<IHTMLSerializeOptions>): string {
if (!this._terminal) {
throw new Error('Cannot use addon until it has been loaded');
}
return this._serializeBufferAsHTML(this._terminal, options || {});
}
public dispose(): void { }
}
@@ -469,3 +512,150 @@ interface ISerializeOptions {
excludeModes?: boolean;
excludeAltBuffer?: boolean;
}
interface IHTMLSerializeOptions {
scrollback: number;
onlySelection: boolean;
includeGlobalBackground: boolean;
}
export class HTMLSerializeHandler extends BaseSerializeHandler {
private _currentRow: string = '';
private _htmlContent = '';
private _colors: IColorSet;
constructor(
buffer: IBuffer,
private readonly _terminal: Terminal,
private readonly _options: Partial<IHTMLSerializeOptions>
) {
super(buffer);
// https://github.com/xtermjs/xterm.js/issues/3601
this._colors = (_terminal as any)._core._colorManager.colors;
}
private _padStart(target: string, targetLength: number, padString: string): string {
targetLength = targetLength >> 0;
padString = padString ?? ' ';
if (target.length > targetLength) {
return target;
}
targetLength = targetLength - target.length;
if (targetLength > padString.length) {
padString += padString.repeat(targetLength / padString.length);
}
return padString.slice(0, targetLength) + target;
}
protected _beforeSerialize(rows: number, start: number, end: number): void {
this._htmlContent += '<html><body><!--StartFragment--><pre>';
let foreground = '#000000';
let background = '#ffffff';
if (this._options.includeGlobalBackground ?? false) {
foreground = this._terminal.options.theme?.foreground ?? '#ffffff';
background = this._terminal.options.theme?.background ?? '#000000';
}
const globalStyleDefinitions = [];
globalStyleDefinitions.push('color: ' + foreground + ';');
globalStyleDefinitions.push('background-color: ' + background + ';');
globalStyleDefinitions.push('font-family: ' + this._terminal.options.fontFamily + ';');
globalStyleDefinitions.push('font-size: ' + this._terminal.options.fontSize + 'px;');
this._htmlContent += '<div style=\'' + globalStyleDefinitions.join(' ') + '\'>';
}
protected _afterSerialize(): void {
this._htmlContent += '</div>';
this._htmlContent += '</pre><!--EndFragment--></body></html>';
}
protected _rowEnd(row: number, isLastRow: boolean): void {
this._htmlContent += '<div><span>' + this._currentRow + '</span></div>';
this._currentRow = '';
}
private _getHexColor(cell: IBufferCell, isFg: boolean): string | undefined {
const color = isFg ? cell.getFgColor() : cell.getBgColor();
if (isFg ? cell.isFgRGB() : cell.isBgRGB()) {
const rgb = [
(color >> 16) & 255,
(color >> 8) & 255,
(color ) & 255
];
return rgb.map(x => this._padStart(x.toString(16), 2, '0')).join('');
}
if (isFg ? cell.isFgPalette() : cell.isBgPalette()) {
return this._colors.ansi[color].css;
}
return undefined;
}
private _diffStyle(cell: IBufferCell, oldCell: IBufferCell): string[] | undefined {
const content: string[] = [];
const fgChanged = !equalFg(cell, oldCell);
const bgChanged = !equalBg(cell, oldCell);
const flagsChanged = !equalFlags(cell, oldCell);
if (fgChanged || bgChanged || flagsChanged) {
const fgHexColor = this._getHexColor(cell, true);
if (fgHexColor) {
content.push('color: ' + fgHexColor + ';');
}
const bgHexColor = this._getHexColor(cell, false);
if (bgHexColor) {
content.push('background-color: ' + bgHexColor + ';');
}
if (cell.isInverse()) { content.push('color: #000000; background-color: #BFBFBF;'); }
if (cell.isBold()) { content.push('font-weight: bold;'); }
if (cell.isUnderline()) { content.push('text-decoration: underline;'); }
if (cell.isBlink()) { content.push('text-decoration: blink;'); }
if (cell.isInvisible()) { content.push('visibility: hidden;'); }
if (cell.isItalic()) { content.push('font-style: italic;'); }
if (cell.isDim()) { content.push('opacity: 0.5;'); }
if (cell.isStrikethrough()) { content.push('text-decoration: line-through;'); }
return content;
}
return undefined;
}
protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void {
// a width 0 cell don't need to be count because it is just a placeholder after a CJK character;
const isPlaceHolderCell = cell.getWidth() === 0;
if (isPlaceHolderCell) {
return;
}
// this cell don't have content
const isEmptyCell = cell.getChars() === '';
const styleDefinitions = this._diffStyle(cell, oldCell);
// handles style change
if (styleDefinitions) {
this._currentRow += styleDefinitions.length === 0 ?
'</span><span>' :
'</span><span style=\'' + styleDefinitions.join(' ') + '\'>';
}
// handles actual content
if (isEmptyCell) {
this._currentRow += ' ';
} else {
this._currentRow += cell.getChars();
}
}
protected _serializeString(): string {
return this._htmlContent;
}
}
@@ -14,6 +14,9 @@
"paths": {
"common/*": [
"../../../src/common/*"
],
"browser/*": [
"../../../src/browser/*"
]
},
"strict": true,
@@ -28,6 +31,9 @@
"references": [
{
"path": "../../../src/common"
},
{
"path": "../../../src/browser"
}
]
}
@@ -24,14 +24,23 @@ declare module 'xterm-addon-serialize' {
* the state. The cursor will also be positioned to the correct cell. When restoring a terminal
* it is best to do before `Terminal.open` is called to avoid wasting CPU cycles rendering
* incomplete frames.
*
*
* It's recommended that you write the serialized data into a terminal of the same size in which
* it originated from and then resize it after if needed.
*
*
* @param options Custom options to allow control over what gets serialized.
*/
public serialize(options?: ISerializeOptions): string;
/**
* Serializes terminal content as HTML, which can be written to the clipboard using the
* `text/html` mimetype. For applications that support it, the pasted text should then retain
* its colors/styles.
*
* @param options Custom options to allow control over what gets serialized.
*/
public serializeAsHTML(options?: Partial<IHTMLSerializeOptions>): string;
/**
* Disposes the addon.
*/
@@ -56,4 +65,24 @@ declare module 'xterm-addon-serialize' {
*/
excludeAltBuffer?: boolean;
}
export interface IHTMLSerializeOptions {
/**
* The number of rows in the scrollback buffer to serialize, starting from the bottom of the
* scrollback buffer. When not specified, all available rows in the scrollback buffer will be
* serialized. This setting is ignored if {@link IHTMLSerializeOptions.onlySelection} is true.
*/
scrollback: number;
/**
* Whether to only serialize the selection. If false, the whole active buffer is serialized in HTML.
* False by default.
*/
onlySelection: boolean;
/**
* Whether to include the global background of the terminal. False by default.
*/
includeGlobalBackground: boolean;
}
}
+24 -19
View File
@@ -299,32 +299,37 @@ export class GlyphRenderer {
return this._colors.ansi[idx];
}
public onResize(): void {
public clear(force?: boolean): void {
const terminal = this._terminal;
const gl = this._gl;
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Update vertices
const newCount = terminal.cols * terminal.rows * INDICES_PER_CELL;
if (this._vertices.count !== newCount) {
this._vertices.count = newCount;
this._vertices.attributes = new Float32Array(newCount);
for (let i = 0; i < this._vertices.attributesBuffers.length; i++) {
this._vertices.attributesBuffers[i] = new Float32Array(newCount);
}
let i = 0;
for (let y = 0; y < terminal.rows; y++) {
for (let x = 0; x < terminal.cols; x++) {
this._vertices.attributes[i + 8] = x / terminal.cols;
this._vertices.attributes[i + 9] = y / terminal.rows;
i += INDICES_PER_CELL;
}
// Don't clear if not forced and the array length is correct
if (!force && this._vertices.count === newCount) {
return;
}
// Clear vertices
this._vertices.count = newCount;
this._vertices.attributes = new Float32Array(newCount);
for (let i = 0; i < this._vertices.attributesBuffers.length; i++) {
this._vertices.attributesBuffers[i] = new Float32Array(newCount);
}
let i = 0;
for (let y = 0; y < terminal.rows; y++) {
for (let x = 0; x < terminal.cols; x++) {
this._vertices.attributes[i + 8] = x / terminal.cols;
this._vertices.attributes[i + 9] = y / terminal.rows;
i += INDICES_PER_CELL;
}
}
}
public onResize(): void {
const gl = this._gl;
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
this.clear();
}
public setColors(): void {
}
@@ -247,6 +247,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
}
public clear(): void {
this._model.clear();
this._glyphRenderer.clear(true);
for (const l of this._renderLayers) {
l.reset(this._terminal);
}
+5
View File
@@ -173,3 +173,8 @@
.xterm-strikethrough {
text-decoration: line-through;
}
.xterm-screen .xterm-decoration-container .xterm-decoration {
z-index: 6;
position: absolute;
}
+25
View File
@@ -147,8 +147,10 @@ if (document.location.pathname === '/test') {
createTerminal();
document.getElementById('dispose').addEventListener('click', disposeRecreateButtonHandler);
document.getElementById('serialize').addEventListener('click', serializeButtonHandler);
document.getElementById('htmlserialize').addEventListener('click', htmlSerializeButtonHandler);
document.getElementById('custom-glyph').addEventListener('click', writeCustomGlyphHandler);
document.getElementById('load-test').addEventListener('click', loadTest);
document.getElementById('add-decoration').addEventListener('click', addDecoration);
}
function createTerminal(): void {
@@ -447,6 +449,21 @@ function serializeButtonHandler(): void {
}
}
function htmlSerializeButtonHandler(): void {
const output = addons.serialize.instance.serializeAsHTML();
document.getElementById('htmlserialize-output').innerText = output;
// Deprecated, but the most supported for now.
function listener(e: any) {
e.clipboardData.setData("text/html", output);
e.preventDefault();
}
document.addEventListener("copy", listener);
document.execCommand("copy");
document.removeEventListener("copy", listener);
document.getElementById("htmlserialize-output-result").innerText = "Copied to clipboard";
}
function writeCustomGlyphHandler() {
term.write('\n\r');
@@ -525,3 +542,11 @@ function loadTest() {
term._core._onData.fire('\x03');
});
}
function addDecoration() {
const marker = term.addMarker(1);
const decoration = term.registerDecoration({ marker });
decoration.onRender(() => {
decoration.element.style.backgroundColor = 'red';
});
}
+5
View File
@@ -49,6 +49,10 @@
<button id="serialize">Serialize the content of terminal</button>
<label><input type="checkbox" id="write-to-terminal">Write back to terminal</label>
<div><pre id="serialize-output"></pre></div>
<button id="htmlserialize">Serialize the content of terminal in HTML</button>
<span id="htmlserialize-output-result"></span>
<div><pre id="htmlserialize-output"></pre></div>
</div>
</div>
<div id="style" class="tabContent">
@@ -64,6 +68,7 @@
<button id="dispose" title="This is used to testing memory leaks">Dispose terminal</button>
<button id="custom-glyph" title="Write custom box drawing and block element characters to the terminal">Test custom glyphs</button>
<button id="load-test" title="Write several MB of data to simulate a lot of data coming from the process">Load test</button>
<button id="add-decoration" title="Add a decoration to the terminal">Decoration</button>
</div>
</div>
</div>
+1 -1
View File
@@ -72,7 +72,7 @@
"express-ws": "^5.0.2",
"glob": "^7.2.0",
"jsdom": "^18.0.1",
"mocha": "^9.1.3",
"mocha": "^9.2.0",
"mustache": "^4.2.0",
"node-pty": "^0.10.1",
"nyc": "^15.1.0",
-1
View File
@@ -53,7 +53,6 @@ export class AccessibilityManager extends Disposable {
) {
super();
this._accessibilityTreeRoot = document.createElement('div');
this._accessibilityTreeRoot.setAttribute('role', 'document');
this._accessibilityTreeRoot.classList.add('xterm-accessibility');
this._accessibilityTreeRoot.tabIndex = 0;
+11 -2
View File
@@ -37,7 +37,7 @@ import * as Strings from 'browser/LocalizableStrings';
import { SoundService } from 'browser/services/SoundService';
import { MouseZoneManager } from 'browser/MouseZoneManager';
import { AccessibilityManager } from './AccessibilityManager';
import { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider } from 'xterm';
import { ITheme, IMarker, IDisposable, ISelectionPosition, ILinkProvider, IDecorationOptions, IDecoration } from 'xterm';
import { DomRenderer } from 'browser/renderer/dom/DomRenderer';
import { KeyboardResultType, CoreMouseEventType, CoreMouseButton, CoreMouseAction, ITerminalOptions, ScrollSource, IColorEvent, ColorIndex, ColorRequestType } from 'common/Types';
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
@@ -45,7 +45,7 @@ import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { ColorManager } from 'browser/ColorManager';
import { RenderService } from 'browser/services/RenderService';
import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService, ICharacterJoinerService } from 'browser/services/Services';
import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService, ICoreBrowserService, ICharacterJoinerService, IDecorationService } from 'browser/services/Services';
import { CharSizeService } from 'browser/services/CharSizeService';
import { IBuffer } from 'common/buffer/Types';
import { MouseService } from 'browser/services/MouseService';
@@ -55,6 +55,7 @@ import { CoreTerminal } from 'common/CoreTerminal';
import { color, rgba } from 'browser/Color';
import { CharacterJoinerService } from 'browser/services/CharacterJoinerService';
import { toRgbString } from 'common/input/XParseColor';
import { DecorationService } from 'browser/services/DecorationService';
// Let it work inside Node.js for automated testing purposes.
const document: Document = (typeof window !== 'undefined') ? window.document : null as any;
@@ -108,6 +109,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
public linkifier: ILinkifier;
public linkifier2: ILinkifier2;
public viewport: IViewport | undefined;
public decorationService: IDecorationService;
private _compositionHelper: ICompositionHelper | undefined;
private _mouseZoneManager: IMouseZoneManager | undefined;
private _accessibilityManager: AccessibilityManager | undefined;
@@ -157,6 +159,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.linkifier = this._instantiationService.createInstance(Linkifier);
this.linkifier2 = this.register(this._instantiationService.createInstance(Linkifier2));
this.decorationService = this.register(this._instantiationService.createInstance(DecorationService));
// Setup InputHandler listeners
this.register(this._inputHandler.onRequestBell(() => this.bell()));
@@ -574,6 +577,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
this.linkifier2.attachToDom(this.screenElement, this._mouseService, this._renderService);
this.decorationService.attachToDom(this.screenElement, this._renderService, this._bufferService);
// This event listener must be registered aftre MouseZoneManager is created
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.onMouseDown(e)));
@@ -998,6 +1002,10 @@ export class Terminal extends CoreTerminal implements ITerminal {
return this.buffer.addMarker(this.buffer.ybase + this.buffer.y + cursorYOffset);
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
return this.decorationService!.registerDecoration(decorationOptions);
}
/**
* Gets whether the terminal has an active selection.
*/
@@ -1293,6 +1301,7 @@ export class Terminal extends CoreTerminal implements ITerminal {
// Don't clear if it's already clear
return;
}
this.buffer.clearMarkers();
this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)!);
this.buffer.lines.length = 1;
this.buffer.ydisp = 0;
+13 -1
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { IDisposable, IMarker, ISelectionPosition, ILinkProvider } from 'xterm';
import { IDisposable, IMarker, ISelectionPosition, ILinkProvider, IDecorationOptions, IDecoration } from 'xterm';
import { IEvent, EventEmitter } from 'common/EventEmitter';
import { ICharacterJoinerService, ICharSizeService, IMouseService, IRenderService, ISelectionService } from 'browser/services/Services';
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/Types';
@@ -102,6 +102,9 @@ export class MockTerminal implements ITerminal {
public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
throw new Error('Method not implemented.');
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
throw new Error('Method not implemented.');
}
public hasSelection(): boolean {
throw new Error('Method not implemented.');
}
@@ -254,6 +257,9 @@ export class MockBuffer implements IBuffer {
public getWhitespaceCell(attr?: IAttributeData): ICellData {
throw new Error('Method not implemented.');
}
public clearMarkers(): void {
throw new Error('Method not implemented.');
}
}
export class MockRenderer implements IRenderer {
@@ -280,6 +286,9 @@ export class MockRenderer implements IRenderer {
public setColors(colors: IColorSet): void {
throw new Error('Method not implemented.');
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration {
throw new Error('Method not implemented.');
}
public onResize(cols: number, rows: number): void { }
public onCharSizeChanged(): void { }
public onBlur(): void { }
@@ -419,6 +428,9 @@ export class MockRenderService implements IRenderService {
public dispose(): void {
throw new Error('Method not implemented.');
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration {
throw new Error('Method not implemented.');
}
}
export class MockCharacterJoinerService implements ICharacterJoinerService {
+3 -2
View File
@@ -3,11 +3,11 @@
* @license MIT
*/
import { IDisposable, IMarker, ISelectionPosition } from 'xterm';
import { IDecorationOptions, IDecoration, IDisposable, IMarker, ISelectionPosition } from 'xterm';
import { IEvent } from 'common/EventEmitter';
import { ICoreTerminal, CharData, ITerminalOptions } from 'common/Types';
import { IMouseService, IRenderService } from './services/Services';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { IBuffer } from 'common/buffer/Types';
import { IFunctionIdentifier, IParams } from 'common/parser/Types';
export interface ITerminal extends IPublicTerminal, ICoreTerminal {
@@ -61,6 +61,7 @@ export interface IPublicTerminal extends IDisposable {
registerCharacterJoiner(handler: (text: string) => [number, number][]): number;
deregisterCharacterJoiner(joinerId: number): void;
addMarker(cursorYOffset: number): IMarker | undefined;
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
hasSelection(): boolean;
getSelection(): string;
getSelectionPosition(): ISelectionPosition | undefined;
+15 -2
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { Terminal as ITerminalApi, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBufferNamespace as IBufferNamespaceApi, IParser, ILinkProvider, IUnicodeHandling, FontWeight, IModes } from 'xterm';
import { Terminal as ITerminalApi, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBufferNamespace as IBufferNamespaceApi, IParser, ILinkProvider, IUnicodeHandling, FontWeight, IModes, IDecorationOptions, IDecoration } from 'xterm';
import { ITerminal } from 'browser/Types';
import { Terminal as TerminalCore } from 'browser/Terminal';
import * as Strings from 'browser/LocalizableStrings';
@@ -166,11 +166,16 @@ export class Terminal implements ITerminalApi {
this._checkProposedApi();
this._core.deregisterCharacterJoiner(joinerId);
}
public registerMarker(cursorYOffset: number): IMarker | undefined {
public registerMarker(cursorYOffset: number = 0): IMarker | undefined {
this._checkProposedApi();
this._verifyIntegers(cursorYOffset);
return this._core.addMarker(cursorYOffset);
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
this._checkProposedApi();
this._verifyPositiveIntegers(decorationOptions.x ?? 0, decorationOptions.width ?? 0, decorationOptions.height ?? 0);
return this._core.registerDecoration(decorationOptions);
}
public addMarker(cursorYOffset: number): IMarker | undefined {
return this.registerMarker(cursorYOffset);
}
@@ -281,4 +286,12 @@ export class Terminal implements ITerminalApi {
}
}
}
private _verifyPositiveIntegers(...values: number[]): void {
for (const value of values) {
if (value && (value === Infinity || isNaN(value) || value % 1 !== 0 || value < 0)) {
throw new Error('This API only accepts positive integers');
}
}
}
}
+3 -2
View File
@@ -10,10 +10,11 @@ import { IRenderLayer, IRenderer, IRenderDimensions, IRequestRedrawEvent } from
import { LinkRenderLayer } from 'browser/renderer/LinkRenderLayer';
import { Disposable } from 'common/Lifecycle';
import { IColorSet, ILinkifier, ILinkifier2 } from 'browser/Types';
import { ICharSizeService, ICoreBrowserService } from 'browser/services/Services';
import { IBufferService, IOptionsService, ICoreService, IInstantiationService } from 'common/services/Services';
import { ICharSizeService } from 'browser/services/Services';
import { IBufferService, IOptionsService, IInstantiationService } from 'common/services/Services';
import { removeTerminalFromCache } from 'browser/renderer/atlas/CharAtlasCache';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { IDecorationOptions, IDecoration } from 'xterm';
let nextRendererId = 1;
+164
View File
@@ -0,0 +1,164 @@
/**
* Copyright (c) 2022 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IDecorationService, IRenderService } from 'browser/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { Disposable } from 'common/Lifecycle';
import { IBufferService, IInstantiationService } from 'common/services/Services';
import { IDecorationOptions, IDecoration, IMarker } from 'xterm';
export class DecorationService extends Disposable implements IDecorationService {
private readonly _decorations: Decoration[] = [];
private _container: HTMLElement | undefined;
private _screenElement: HTMLElement | undefined;
private _renderService: IRenderService | undefined;
private _animationFrame: number | undefined;
constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService) { super(); }
public attachToDom(screenElement: HTMLElement, renderService: IRenderService): void {
this._renderService = renderService;
this._screenElement = screenElement;
this._container = document.createElement('div');
this._container.classList.add('xterm-decoration-container');
screenElement.appendChild(this._container);
this.register(this._renderService.onRenderedBufferChange(() => this.refresh()));
this.register(this._renderService.onDimensionsChange(() => this.refresh(true)));
}
public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
if (decorationOptions.marker.isDisposed || !this._container) {
return undefined;
}
const decoration = this._instantiationService.createInstance(Decoration, decorationOptions, this._container);
this._decorations.push(decoration);
decoration.onDispose(() => this._decorations.splice(this._decorations.indexOf(decoration), 1));
this._queueRefresh();
return decoration;
}
private _queueRefresh(): void {
if (this._animationFrame !== undefined) {
return;
}
this._animationFrame = window.requestAnimationFrame(() => {
this.refresh();
this._animationFrame = undefined;
});
}
public refresh(shouldRecreate?: boolean): void {
if (!this._renderService) {
return;
}
for (const decoration of this._decorations) {
decoration.render(this._renderService, shouldRecreate);
}
}
public dispose(): void {
for (const decoration of this._decorations) {
decoration.dispose();
}
if (this._screenElement && this._container && this._screenElement.contains(this._container)) {
this._screenElement.removeChild(this._container);
}
}
}
export class Decoration extends Disposable implements IDecoration {
private readonly _marker: IMarker;
private _element: HTMLElement | undefined;
public isDisposed: boolean = false;
public get element(): HTMLElement | undefined { return this._element; }
public get marker(): IMarker { return this._marker; }
private _onDispose = new EventEmitter<void>();
public get onDispose(): IEvent<void> { return this._onDispose.event; }
private _onRender = new EventEmitter<HTMLElement>();
public get onRender(): IEvent<HTMLElement> { return this._onRender.event; }
public x: number;
public anchor: 'left' | 'right';
public width: number;
public height: number;
constructor(
options: IDecorationOptions,
private readonly _container: HTMLElement,
@IBufferService private readonly _bufferService: IBufferService
) {
super();
this.x = options.x ?? 0;
this._marker = options.marker;
this._marker.onDispose(() => this.dispose());
this.anchor = options.anchor || 'left';
this.width = options.width || 1;
this.height = options.height || 1;
}
public render(renderService: IRenderService, shouldRecreate?: boolean): void {
if (!this._element || shouldRecreate) {
this._createElement(renderService, shouldRecreate);
}
if (this._container && this._element && !this._container.contains(this._element)) {
this._container.append(this._element);
}
this._refreshStyle(renderService);
if (this._element) {
this._onRender.fire(this._element);
}
}
private _createElement(renderService: IRenderService, shouldRecreate?: boolean): void {
if (shouldRecreate && this._element && this._container.contains(this._element)) {
this._container.removeChild(this._element);
}
this._element = document.createElement('div');
this._element.classList.add('xterm-decoration');
this._element.style.width = `${this.width * renderService.dimensions.actualCellWidth}px`;
this._element.style.height = `${this.height * renderService.dimensions.actualCellHeight}px`;
this._element.style.top = `${(this.marker.line - this._bufferService.buffers.active.ydisp) * renderService.dimensions.actualCellHeight}px`;
this._element.style.lineHeight = `${renderService.dimensions.actualCellHeight}px`;
if (this.x && this.x > this._bufferService.cols) {
// exceeded the container width, so hide
this._element.style.display = 'none';
}
if (this.anchor === 'right') {
this._element.style.right = this.x ? `${this.x * renderService.dimensions.actualCellWidth}px` : '';
} else {
this._element.style.left = this.x ? `${this.x * renderService.dimensions.actualCellWidth}px` : '';
}
}
private _refreshStyle(renderService: IRenderService): void {
if (!this._element) {
return;
}
const line = this.marker.line - this._bufferService.buffers.active.ydisp;
if (line < 0 || line > this._bufferService.rows) {
// outside of viewport
this._element.style.display = 'none';
} else {
this._element.style.top = `${line * renderService.dimensions.actualCellHeight}px`;
this._element.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
}
}
public override dispose(): void {
if (this.isDisposed) {
return;
}
if (this._element && this._container.contains(this._element)) {
this._container.removeChild(this._element);
}
this.isDisposed = true;
this._onDispose.fire();
}
}
+2 -1
View File
@@ -65,7 +65,8 @@ export class RenderService extends Disposable implements IRenderService {
this._screenDprMonitor.setListener(() => this.onDevicePixelRatioChange());
this.register(this._screenDprMonitor);
this.register(bufferService.onResize(e => this._fullRefresh()));
this.register(bufferService.onResize(() => this._fullRefresh()));
this.register(bufferService.buffers.onBufferActivate(() => this._renderer?.clear()));
this.register(optionsService.onOptionChange(() => this._renderer.onOptionsChanged()));
this.register(this._charSizeService.onCharSizeChange(() => this.onCharSizeChanged()));
+10
View File
@@ -9,6 +9,8 @@ import { IColorSet } from 'browser/Types';
import { ISelectionRedrawRequestEvent as ISelectionRequestRedrawEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types';
import { createDecorator } from 'common/services/ServiceRegistry';
import { IDisposable } from 'common/Types';
import { IDecorationOptions, IDecoration } from 'xterm';
import { IBufferService } from 'common/services/Services';
export const ICharSizeService = createDecorator<ICharSizeService>('CharSizeService');
export interface ICharSizeService {
@@ -113,3 +115,11 @@ export interface ICharacterJoinerService {
deregister(joinerId: number): boolean;
getJoinedCharacters(row: number): [number, number][];
}
export const IDecorationService = createDecorator<IDecorationService>('DecorationService');
export interface IDecorationService extends IDisposable {
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
refresh(): void;
attachToDom(screenElement: HTMLElement, renderService: IRenderService, bufferService: IBufferService): void;
}
+2 -1
View File
@@ -546,7 +546,7 @@ export class InputHandler extends Disposable implements IInputHandler {
// Log debug data, the log level gate is to prevent extra work in this hot path
if (this._logService.logLevel <= LogLevelEnum.DEBUG) {
this._logService.debug(`parsing data${typeof data === 'string' ? ` "${data}"` : ''}`, typeof data === 'string'
this._logService.debug(`parsing data${typeof data === 'string' ? ` "${data}"` : ` "${Array.prototype.map.call(data, e => String.fromCharCode(e)).join('')}"`}`, typeof data === 'string'
? data.split('').map(e => e.charCodeAt(0))
: data
);
@@ -1231,6 +1231,7 @@ export class InputHandler extends Disposable implements IInputHandler {
private _resetBufferLine(y: number): void {
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
line.fill(this._activeBuffer.getNullCell(this._eraseAttrData()));
this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase + y);
line.isWrapped = false;
}
+22 -1
View File
@@ -43,6 +43,7 @@ export class Buffer implements IBuffer {
private _whitespaceCell: ICellData = CellData.fromCharData([0, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE]);
private _cols: number;
private _rows: number;
private _isClearing: boolean = false;
constructor(
private _hasScrollback: boolean,
@@ -584,6 +585,24 @@ export class Buffer implements IBuffer {
return x >= this._cols ? this._cols - 1 : x < 0 ? 0 : x;
}
public clearMarkers(y?: number): void {
this._isClearing = true;
if (y !== undefined) {
for (let i = 0; i < this.markers.length; i++) {
if (this.markers[i].line === y) {
this.markers[i].dispose();
this.markers.splice(i--, 1);
}
}
} else {
for (const marker of this.markers) {
marker.dispose();
}
this.markers = [];
}
this._isClearing = false;
}
public addMarker(y: number): Marker {
const marker = new Marker(y);
this.markers.push(marker);
@@ -615,7 +634,9 @@ export class Buffer implements IBuffer {
}
private _removeMarker(marker: Marker): void {
this.markers.splice(this.markers.indexOf(marker), 1);
if (!this._isClearing) {
this.markers.splice(this.markers.indexOf(marker), 1);
}
}
public iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator {
+1
View File
@@ -45,6 +45,7 @@ export interface IBuffer {
getNullCell(attr?: IAttributeData): ICellData;
getWhitespaceCell(attr?: IAttributeData): ICellData;
addMarker(y: number): IMarker;
clearMarkers(y?: number): void;
}
export interface IBufferSet extends IDisposable {
+1 -1
View File
@@ -136,7 +136,7 @@ export class Terminal implements ITerminalApi {
this._verifyIntegers(columns, rows);
this._core.resize(columns, rows);
}
public registerMarker(cursorYOffset: number): IMarker | undefined {
public registerMarker(cursorYOffset: number = 0): IMarker | undefined {
this._checkProposedApi();
this._verifyIntegers(cursorYOffset);
return this._core.addMarker(cursorYOffset);
+69
View File
@@ -559,6 +559,29 @@ describe('API Integration Tests', function(): void {
assert.equal(await page.evaluate(`window.term.buffer.active.getLine(0).getCell(2).getChars()`), '');
assert.equal(await page.evaluate(`window.term.buffer.active.getLine(0).getCell(2).getWidth()`), 0);
});
it('clearMarkers', async () => {
await openTerminal(page, { cols: 5 });
await page.evaluate(`
window.disposeStack = [];
`);
await writeSync(page, '\\n\\n\\n\\n');
await writeSync(page, '\\n\\n\\n\\n');
await writeSync(page, '\\n\\n\\n\\n');
await writeSync(page, '\\n\\n\\n\\n');
await page.evaluate(`window.term.addMarker(1)`);
await page.evaluate(`window.term.addMarker(2)`);
await page.evaluate(`window.term.scrollLines(10)`);
await page.evaluate(`window.term.addMarker(3)`);
await page.evaluate(`window.term.addMarker(4)`);
await page.evaluate(`
for (let i = 0; i < window.term.markers.length; ++i) {
const marker = window.term.markers[i];
marker.onDispose(() => window.disposeStack.push(marker));
}`);
await page.evaluate(`window.term.clear()`);
assert.equal(await page.evaluate(`window.disposeStack.length`), 4);
});
});
it('active, normal, alternate', async () => {
@@ -708,6 +731,52 @@ describe('API Integration Tests', function(): void {
await pollFor(page, `window.term._core._renderService.dimensions.actualCellWidth > 0`, true);
});
describe('registerDecoration', () => {
it('should register decorations and render them', async () => {
await openTerminal(page);
await writeSync(page, '\\n\\n\\n\\n');
await writeSync(page, '\\n\\n\\n\\n');
await writeSync(page, '\\n\\n\\n\\n');
await page.evaluate(`window.marker1 = window.term.addMarker(1)`);
await page.evaluate(`window.marker2 = window.term.addMarker(2)`);
await page.evaluate(`window.term.registerDecoration({ marker: window.marker1 })`);
await page.evaluate(`window.term.registerDecoration({ marker: window.marker2 })`);
await page.evaluate(`window.term.resize(10, 5)`);
assert.equal(await page.evaluate(`document.querySelectorAll('.xterm-screen .xterm-decoration').length`), 2);
});
it('on resize should dispose of the old decoration and create a new one', async () => {
await openTerminal(page);
await writeSync(page, '\\n\\n\\n\\n');
await writeSync(page, '\\n\\n\\n\\n');
await page.evaluate(`window.marker = window.term.addMarker(1)`);
await page.evaluate(`window.decoration = window.term.registerDecoration({ marker: window.marker })`);
await page.evaluate(`window.term.resize(10, 5)`);
assert.equal(await page.evaluate(`document.querySelectorAll('.xterm-screen .xterm-decoration').length`), 1);
});
it('should return undefined when the marker has already been disposed of', async () => {
await openTerminal(page);
await writeSync(page, '\\n\\n\\n\\n');
await writeSync(page, '\\n\\n\\n\\n');
await page.evaluate(`window.marker = window.term.addMarker(1)`);
await page.evaluate(`window.marker.dispose()`);
assert.equal(await page.evaluate(`window.decoration = window.term.registerDecoration({ marker: window.marker });`), undefined);
});
it('should throw when a negative x offset is provided', async () => {
await openTerminal(page);
await writeSync(page, '\\n\\n\\n\\n');
await writeSync(page, '\\n\\n\\n\\n');
await page.evaluate(`window.marker = window.term.addMarker(1)`);
await page.evaluate(`
try {
window.decoration = window.term.registerDecoration({ marker: window.marker, x: -2 });
} catch (e) {
window.throwMessage = e.message;
}
`);
await pollFor(page, 'window.throwMessage', 'This API only accepts positive integers');
});
});
describe('registerLinkProvider', () => {
it('should fire provideLinks when hovering cells', async () => {
await openTerminal(page, { rendererType: 'dom' });
+1 -1
View File
@@ -643,7 +643,7 @@ declare module 'xterm-headless' {
* @param cursorYOffset The y position offset of the marker from the cursor.
* @returns The new marker or undefined.
*/
registerMarker(cursorYOffset: number): IMarker | undefined;
registerMarker(cursorYOffset?: number): IMarker | undefined;
/**
* @deprecated use `registerMarker` instead.
+86 -10
View File
@@ -380,29 +380,96 @@ declare module 'xterm' {
* is trimmed and lines are added or removed. This is a single line that may
* be part of a larger wrapped line.
*/
export interface IMarker extends IDisposable {
export interface IMarker extends IDisposableWithEvent {
/**
* A unique identifier for this marker.
*/
readonly id: number;
/**
* Whether this marker is disposed.
*/
readonly isDisposed: boolean;
/**
* The actual line index in the buffer at this point in time. This is set to
* -1 if the marker has been disposed.
*/
readonly line: number;
}
/**
* Represents a disposable with an
* @param onDispose event listener and
* @param isDisposed property.
*/
export interface IDisposableWithEvent extends IDisposable {
/**
* Event listener to get notified when the marker gets disposed. Automatic disposal
* might happen for a marker, that got invalidated by scrolling out or removal of
* a line from the buffer.
* Event listener to get notified when this gets disposed.
*/
onDispose: IEvent<void>;
/**
* Whether this is disposed.
*/
readonly isDisposed: boolean;
}
/**
* Represents a decoration in the terminal that is associated with a particular marker and DOM element.
*/
export interface IDecoration extends IDisposableWithEvent {
/*
* The marker for the decoration in the terminal.
*/
readonly marker: IMarker;
/**
* An event fired when the decoration
* is rendered, returns the dom element
* associated with the decoration.
*/
readonly onRender: IEvent<HTMLElement>;
/**
* The HTMLElement that gets created after the
* first _onRender call, or undefined if accessed before
* that.
*/
readonly element: HTMLElement | undefined;
}
/**
* Options provided when registering a decoration
* containing a @param marker, @param anchor,
* @param x offset from the anchor, @param width in cells
* and @param height in cells.
*/
export interface IDecorationOptions {
/**
* The line in the terminal where
* the decoration will be displayed
*/
marker: IMarker;
/*
* Where the decoration will be anchored -
* defaults to the left edge
*/
anchor?: 'right' | 'left';
/**
* The x position offset relative to the anchor
*/
x?: number;
/**
* The width of the decoration in cells, which defaults to
* cell width
*/
width?: number;
/**
* The height of the decoration in cells, which defaults to
* cell height
*/
height?: number;
}
/**
@@ -863,13 +930,22 @@ declare module 'xterm' {
* @param cursorYOffset The y position offset of the marker from the cursor.
* @returns The new marker or undefined.
*/
registerMarker(cursorYOffset: number): IMarker | undefined;
registerMarker(cursorYOffset?: number): IMarker | undefined;
/**
* @deprecated use `registerMarker` instead.
*/
addMarker(cursorYOffset: number): IMarker | undefined;
/**
* (EXPERIMENTAL) Adds a decoration to the terminal using
* @param decorationOptions, which takes a marker and an optional anchor,
* width, height, and x offset from the anchor. Returns the decoration or
* undefined if the alt buffer is active or the marker has already been disposed of.
* @throws when options include a negative x offset.
*/
registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined;
/**
* Gets whether the terminal has an active selection.
*/
+37 -30
View File
@@ -1153,10 +1153,10 @@ chokidar@3.5.1:
optionalDependencies:
fsevents "~2.3.1"
chokidar@3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
chokidar@3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
@@ -1398,7 +1398,14 @@ debug@4.3.1:
dependencies:
ms "2.1.2"
debug@4.3.2, debug@^4.3.1, debug@^4.3.2:
debug@4.3.3:
version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
dependencies:
ms "2.1.2"
debug@^4.3.1, debug@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
@@ -2140,10 +2147,10 @@ glob@7.1.6, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@7.1.7, glob@^7.1.1:
version "7.1.7"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
glob@7.2.0, glob@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
@@ -2152,10 +2159,10 @@ glob@7.1.7, glob@^7.1.1:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
glob@^7.1.1:
version "7.1.7"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
@@ -2983,32 +2990,32 @@ mocha@^8.3.2:
yargs-parser "20.2.4"
yargs-unparser "2.0.0"
mocha@^9.1.3:
version "9.1.3"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.3.tgz#8a623be6b323810493d8c8f6f7667440fa469fdb"
integrity sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==
mocha@^9.2.0:
version "9.2.0"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.0.tgz#2bfba73d46e392901f877ab9a47b7c9c5d0275cc"
integrity sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q==
dependencies:
"@ungap/promise-all-settled" "1.1.2"
ansi-colors "4.1.1"
browser-stdout "1.3.1"
chokidar "3.5.2"
debug "4.3.2"
chokidar "3.5.3"
debug "4.3.3"
diff "5.0.0"
escape-string-regexp "4.0.0"
find-up "5.0.0"
glob "7.1.7"
glob "7.2.0"
growl "1.10.5"
he "1.2.0"
js-yaml "4.1.0"
log-symbols "4.1.0"
minimatch "3.0.4"
ms "2.1.3"
nanoid "3.1.25"
nanoid "3.2.0"
serialize-javascript "6.0.0"
strip-json-comments "3.1.1"
supports-color "8.1.1"
which "2.0.2"
workerpool "6.1.5"
workerpool "6.2.0"
yargs "16.2.0"
yargs-parser "20.2.4"
yargs-unparser "2.0.0"
@@ -3048,10 +3055,10 @@ nanoid@3.1.20:
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
nanoid@3.1.25:
version "3.1.25"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==
nanoid@3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c"
integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==
natural-compare@^1.4.0:
version "1.4.0"
@@ -4421,10 +4428,10 @@ workerpool@6.1.0:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b"
integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==
workerpool@6.1.5:
version "6.1.5"
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581"
integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==
workerpool@6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b"
integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==
wrap-ansi@^6.2.0:
version "6.2.0"