Compare commits

..
1295 Commits
Author SHA1 Message Date
Daniel Imms 2e02c37e52 Merge pull request #4756 from Tyriar/tyriar/5_3__2
Disable image addon publishing
2023-09-07 10:58:11 -07:00
Daniel Imms 71ee3c7a33 Disable image addon publishing 2023-09-07 10:57:21 -07:00
Daniel Imms 009add2b4a Merge pull request #4755 from Tyriar/tyriar/5_3
v5.3.0 and bump all addons
2023-09-07 10:54:11 -07:00
Daniel Imms fb4b662756 v5.3.0 and bump all addons 2023-09-07 10:46:35 -07:00
Daniel Imms 7b52b8ee45 Merge pull request #4754 from tisilent/DomRenderer-Focus-Cursor
When DomRenderer focus,render the cursor line.
2023-09-07 08:53:26 -07:00
tisilent c03cb2b016 When DomRenderer focus,render the cursor line. 2023-09-07 23:44:36 +08:00
Daniel Imms 29dd30a98d Merge pull request #4735 from Tyriar/playwright
Start @playwright/test migration
2023-09-03 07:19:20 -07:00
Daniel Imms 68c572345b Remove debugger statement 2023-09-02 09:19:17 -07:00
Daniel Imms 5e628d7fc6 Use arrow functions 2023-09-02 09:11:15 -07:00
Daniel Imms 93aae670ba Readme explaining test/api 2023-09-02 08:56:59 -07:00
Daniel Imms 7c7ac86681 Run inner pwsh to unzip on Winodws 2023-09-02 08:53:20 -07:00
Daniel Imms 076ab0f49b Combine unzip and ls -R 2023-09-02 08:44:32 -07:00
Daniel Imms 5fc1c04bd6 Use bash on all OS' 2023-09-02 08:41:59 -07:00
Daniel Imms 525d498e9f Fix unzip on Windows 2023-09-02 08:40:57 -07:00
Daniel Imms 2b5ebb2b7b Use unified unzip on unit tests 2023-09-02 08:33:01 -07:00
Daniel Imms 2269e92c88 Fix syntax 2023-09-02 08:30:51 -07:00
Daniel Imms 7c434a0566 Merge unzip steps 2023-09-02 08:30:14 -07:00
Daniel Imms 31bbeca2d8 Add webkit 2023-09-02 08:26:46 -07:00
Daniel Imms 158f7a17a6 Use list reporter 2023-09-02 08:26:42 -07:00
Daniel Imms 8d0fde2d7f Remove only 2023-09-02 08:22:56 -07:00
Daniel Imms 4cd4597082 Browser-specific playwright job 2023-09-02 08:19:59 -07:00
Daniel Imms 1624854bd7 Migrate Terminal api tests to playwright 2023-09-02 08:18:02 -07:00
Daniel Imms c099cb2a3d Add browser-specific npm scripts 2023-09-02 08:17:15 -07:00
Daniel Imms 1c57b98c9a Migrate InputHandler api tests to playwright 2023-09-02 07:19:32 -07:00
Daniel Imms a29ab32876 Migrate MouseTracking api tests to playwright 2023-09-02 07:10:47 -07:00
Daniel Imms 91456fff04 More proxy impl 2023-09-02 06:06:51 -07:00
Daniel Imms 13af55f9fc Add onWriteParsed event to proxy 2023-09-02 06:00:33 -07:00
Daniel Imms 66454d378b Add todos 2023-09-01 08:50:39 -07:00
Daniel Imms 92d09f2ea1 Clean up, fix errors, add todo 2023-09-01 08:50:03 -07:00
Daniel Imms fac5e5f02d Improve types of proxy object to extend base interfaces 2023-09-01 08:46:14 -07:00
Daniel Imms 4ed97cae3a Merge branch 'master' into playwright 2023-09-01 07:46:35 -07:00
Daniel Imms 7bd9f8ee38 Revert image addon options type name 2023-08-28 02:52:03 -07:00
Daniel Imms 2dd4dfda80 Merge pull request #4729 from tisilent/circle-line-custom-powerline-glyph
Update circle line custom powerline glyph.
2023-08-28 02:28:41 -07:00
Daniel Imms f7ac0d878b Center new semi-circle line glyphs 2023-08-28 02:20:31 -07:00
Daniel Imms fe2723b585 Merge branch 'master' into circle-line-custom-powerline-glyph 2023-08-28 02:13:56 -07:00
tisilent 7fecec6599 Offset outward 2023-08-28 16:54:27 +08:00
Daniel Imms 5b25c65521 Migrate Parser tests to playwright/test 2023-08-27 09:11:47 -07:00
Daniel Imms cb977ab6b6 Remove CharWidth.test.ts from api (in playwright) 2023-08-27 09:03:55 -07:00
Daniel Imms 2b21f27ac2 Fix demo build script 2023-08-27 09:00:48 -07:00
Daniel Imms ec984d25db Install --with-deps 2023-08-27 08:56:40 -07:00
Daniel Imms 7000447915 Build demo step 2023-08-27 08:54:04 -07:00
Daniel Imms 486d61736b Start demo 2023-08-27 08:50:24 -07:00
Daniel Imms a4683ab874 dist -> /dist 2023-08-27 08:44:24 -07:00
Daniel Imms b26c53d7e2 Make client dist a build artifact 2023-08-27 08:28:23 -07:00
Daniel Imms 86b8744838 Install playwright deps 2023-08-27 08:22:54 -07:00
Daniel Imms 6f04e00ea1 Add test-playwright-parallel job 2023-08-27 08:16:53 -07:00
Daniel Imms 9eadaf7079 Feature detect WebAssembly instead 2023-08-27 08:10:58 -07:00
Daniel Imms fbebabe2e6 Add @playwright/test support 2023-08-27 08:08:52 -07:00
Daniel Imms 48184cc94f Merge pull request #4734 from xtermjs/coverage
Bring back unit test coverage report
2023-08-27 07:49:05 -07:00
Daniel Imms 923c28cb77 Use node 18 in coverage 2023-08-27 07:43:11 -07:00
Daniel Imms 03c1906f13 Have coverage depend on build 2023-08-27 07:33:46 -07:00
Daniel Imms 2b7b4d6fea Upgrade playwright 2023-08-27 07:31:20 -07:00
Daniel Imms abae7b6210 Merge pull request #4733 from Tyriar/mutable
Adopt MutableDisposable for most usages of pattern `<var>?.dispose(); <var> = undefined`
2023-08-27 07:29:02 -07:00
Daniel Imms 12a862c277 Bring back unit test coverage report 2023-08-27 07:28:04 -07:00
Daniel Imms 38c6af9b46 Fix _renderer access in api test 2023-08-27 07:22:08 -07:00
Daniel Imms 2eaa9b3b69 Fix remaining issues with MutableDisposable usage 2023-08-27 07:15:51 -07:00
Daniel Imms 37b0e4018c More adoption of MutableDisposable 2023-08-27 07:03:27 -07:00
Daniel Imms 57db72d287 More adoption of MutableDisposable 2023-08-27 06:55:16 -07:00
Daniel Imms ec02bab97c Use MutableDisposable in CursorRenderLayer 2023-08-27 06:46:49 -07:00
Daniel Imms 94ed3e800a Don't dispose of texture atlas in canvas addon
This would cause problems if the atlas is shared between multiple
terminals.
2023-08-27 06:44:48 -07:00
Daniel Imms 1e1ccba238 Use MutableDisposable in BaseRenderLayer 2023-08-27 06:44:11 -07:00
Daniel Imms a0d0c923f3 Merge pull request #4732 from Tyriar/texture_utilization
Improve texture atlas utilization and fix glyph corruption when merging
2023-08-26 15:46:52 -07:00
Daniel Imms aba16687b0 Fix high pressure texture atlas corruption
The corruption was caused by confusion with page indexes when
merging pages. The fix is to simplify it by deleting all pages
and then just adding the new page.

Fixes #4534
Fixes #4351
2023-08-26 10:23:09 -07:00
Daniel Imms 797a71695f Improve texture atlas utilization
- Changed the threshold at which pages start to merge from MAX/2 to
  MAX.
- If a glyph is about to create a new page that will be merged,
  check if the existing row fits when ignoring the row pixel
  threshold. This will improve texture utilization by using the
  available space before the page is merged and becomes static.
2023-08-26 09:37:08 -07:00
Daniel Imms 55c5a8560c Merge pull request #4727 from jerch/fix_4720
remove inwasm burden
2023-08-25 07:50:45 -07:00
tisilent 9e768a4c7c Update circle line custom powerline glyph. 2023-08-25 18:37:48 +08:00
jerch d367eb41d2 Merge branch 'master' into fix_4720 2023-08-24 17:32:08 +02:00
Jörg Breitbart 7c164efdcc fix CI scripts 2023-08-24 17:30:52 +02:00
Jörg Breitbart bd76cdbd20 resort wasm to xterm-wasm-parts 2023-08-24 17:24:25 +02:00
Daniel Imms 40e51aba6b Merge pull request #4726 from Tyriar/no_confusing
Warn on eslint rule @typescript-eslint/no-confusing-void-expression
2023-08-24 06:41:18 -07:00
Daniel Imms f9dc26d413 Merge branch 'master' into no_confusing 2023-08-24 06:33:43 -07:00
Daniel Imms 2fe082beea Warn on eslint rule @typescript-eslint/no-confusing-void-expression 2023-08-24 06:33:11 -07:00
Daniel Imms 2835976b81 Merge pull request #4725 from Tyriar/no_useless_ctor
Warn on eslint rule @typescript-eslint/no-useless-constructor
2023-08-24 06:30:59 -07:00
Daniel Imms 1968e8ced4 Merge branch 'master' into no_useless_ctor 2023-08-24 06:24:54 -07:00
Daniel Imms 1965daff50 Warn on eslint rule @typescript-eslint/no-useless-constructor 2023-08-24 06:24:25 -07:00
Daniel Imms 0e3db591c0 Merge pull request #4724 from Tyriar/webpack_eslint
@ts-check webpack files, fix vscode language of eslint file
2023-08-23 16:50:23 -07:00
Daniel Imms 224902af9f Merge branch 'master' into webpack_eslint 2023-08-23 16:37:37 -07:00
Daniel Imms 131f578367 Merge pull request #4723 from Tyriar/demo_tscheck
Use ts-check in server and start
2023-08-23 16:37:29 -07:00
Daniel Imms 6b794285c6 Remove esbuild
Accidentally added
2023-08-23 16:32:22 -07:00
Daniel Imms e78f85bf65 ts-check webpack configs 2023-08-23 16:29:45 -07:00
Daniel Imms 9be0b6d918 Force eslintrc to use jsonc language 2023-08-23 16:29:38 -07:00
Daniel Imms 3aa31111de Merge branch 'master' into demo_tscheck 2023-08-23 16:26:07 -07:00
Daniel Imms 81baad6acd Prefer const 2023-08-23 16:26:00 -07:00
Daniel Imms 25ef47a78f Use express-ws types 2023-08-23 16:25:15 -07:00
Daniel Imms 7d7d593fee Merge pull request #4722 from Tyriar/client_warnings
Fix demo client.ts warnings
2023-08-23 16:22:28 -07:00
Daniel Imms 766ffde4ca Merge pull request #4721 from Tyriar/complex_options
Fix demo warnings by adding complex options
2023-08-23 16:22:20 -07:00
Daniel Imms 4534b56c1f Use ts-check in server and start 2023-08-23 16:20:55 -07:00
Daniel Imms ee0da5e4c1 Fix demo client.ts warnings 2023-08-23 16:03:25 -07:00
Daniel Imms e5d60c0895 Fix demo warnings by adding complex options 2023-08-23 16:00:16 -07:00
Daniel Imms 2268ae3967 Merge pull request #4718 from Tyriar/no_unused
Add no unused variables lint warning
2023-08-23 04:57:55 -07:00
Daniel Imms 921da1a2ea Ignore no-unused-vars in .api, organize all remaining imports 2023-08-23 04:47:53 -07:00
Daniel Imms ebd4092bee Ignore no-unused-vars in args 2023-08-23 04:42:44 -07:00
Daniel Imms b6fd7a2b93 Ignore no-unused-vars in test files 2023-08-23 04:42:21 -07:00
Daniel Imms 210ba8109c Add no unused variables lint warning
This shows a warning while compiling, so demo bundling will still happen but
it will be rejected when a PR is made.
2023-08-23 04:34:55 -07:00
Daniel Imms 317ccdac66 Merge pull request #4716 from xtermjs/Tyriar-patch-1
Fix typo in ci.yml
2023-08-22 07:33:02 -07:00
Daniel Imms 43a3251bee Fix typo in ci.yml 2023-08-22 07:32:51 -07:00
Daniel Imms 94ab56542f Merge pull request #4715 from Tyriar/waiton
Parallelize required tests and build jobs
2023-08-22 07:31:25 -07:00
Daniel Imms 92f1881dfe Polish job names
This matches the npm script names closer
2023-08-22 07:21:27 -07:00
Daniel Imms ec4c018f73 Remove windows from fast 2023-08-22 07:15:02 -07:00
Daniel Imms 2c25dd1970 Fix invalid names 2023-08-22 07:14:11 -07:00
Daniel Imms 06fbe69b09 Split out integration tests too 2023-08-22 07:05:55 -07:00
Daniel Imms 4bd154e7dc Split fast and slow unit tests to free agents 2023-08-22 07:04:12 -07:00
Daniel Imms 516756fad8 Fix install step 2023-08-22 07:01:27 -07:00
Daniel Imms 2b3cfb39a1 Merge branch 'master' into waiton 2023-08-22 06:58:01 -07:00
Daniel Imms e9cbb3e547 Add job name 2023-08-22 06:57:20 -07:00
Daniel Imms 1744801867 Try parallelize tests and build 2023-08-22 06:52:57 -07:00
Daniel Imms 64cb9a3c62 Merge pull request #4714 from Tyriar/4713
Don't smooth scroll when it's a no-op
2023-08-22 06:49:52 -07:00
Daniel Imms 0e947788e0 Don't smooth scroll when it's a no-op
Fixes #4713
2023-08-22 06:39:59 -07:00
Daniel Imms 60b59db94f Merge pull request #4709 from Tyriar/tyriar/logger
Fix error that can occur when loading canvas addon
2023-08-22 06:27:06 -07:00
Daniel Imms 6d7032a906 Merge pull request #4710 from Tyriar/4704
Move addon install to presetup
2023-08-22 06:23:10 -07:00
Daniel Imms b452fc9e85 Merge pull request #4712 from Tyriar/timeout
Put reasonable GH action timeouts in place
2023-08-22 06:22:55 -07:00
Daniel Imms dac4dec0ad Put reasonable GH action timeouts in place
Fixes #4711
2023-08-22 06:13:52 -07:00
Daniel Imms eae79e93f3 Install addons in CI 2023-08-22 06:11:54 -07:00
Daniel Imms ee33765932 Move addon install to presetup
Fixes #4704
2023-08-22 06:04:33 -07:00
Daniel Imms 8af83fef85 Fix error that can occur when loading canvas addon 2023-08-22 06:00:44 -07:00
Daniel Imms 6f0a5c9f0c Merge pull request #4695 from PerBothner/demo
demo: auto-resize and other tweaks
2023-08-20 09:31:06 -07:00
Daniel Imms 1529e0bf04 Remove unused variable 2023-08-20 07:33:15 -07:00
Daniel Imms 6021e6cf81 Merge branch 'master' into demo 2023-08-20 07:29:31 -07:00
Daniel Imms 427d33c7b7 Merge pull request #4700 from Tyriar/ci_concise
Make GH actions strategy more concise
2023-08-19 15:41:37 -07:00
Daniel Imms 95f332cb13 Merge pull request #4699 from Tyriar/slow_tests
Improve slow tests
2023-08-19 15:34:08 -07:00
Daniel Imms db4567599b Make GH actions strategy more concise 2023-08-19 15:28:44 -07:00
Daniel Imms 0667c7aedd Merge pull request #4698 from Tyriar/codeql
Revert codeql file name change
2023-08-19 15:22:46 -07:00
Daniel Imms 1c5596de66 Split up Base64Decoder decoding tests
Fixes #4696
2023-08-19 15:21:42 -07:00
Daniel Imms 905707284d Up parser unti test timeout 10s -> 30s 2023-08-19 15:18:44 -07:00
Daniel Imms 67f46a54b8 Split up StringToUtf32 decoder tests
Part of #4696
2023-08-19 15:17:11 -07:00
Daniel Imms b8db3853f1 Merge pull request #4697 from xtermjs/Tyriar-patch-1
Remove Azure Pipelines badge
2023-08-19 15:07:15 -07:00
Daniel Imms a9015c0ee6 Revert codeql file name change 2023-08-19 15:05:32 -07:00
Daniel Imms 60db1e7f82 Remove Azure Pipelines badge 2023-08-19 14:57:25 -07:00
Daniel Imms 6dae811dae Migrate to GitHub Actions
Fixes #3297
2023-08-19 14:47:35 -07:00
Per Bothner a20599efb4 Merge branch 'master' into demo 2023-08-19 09:15:30 -07:00
jerch 27b8d73f66 Merge pull request #4694 from jerch/fix_bce_demo
fix spaces in bce demo
2023-08-19 17:43:36 +02:00
Per Bothner 025e8505d5 implement autoResize for terminal in demo
This is optional (but the default): You can still explicitly set COLSxROWS,
but "auto" uses a ResizeObserver sets the size based on window size.
2023-08-19 08:28:43 -07:00
Jörg Breitbart 16f5900252 fix spaces in demo 2023-08-19 17:28:27 +02:00
Per Bothner e2d699cb32 Styling tweaks for demo
* Make terminal take 2/3 of the width, and the options etc get 1/3
* Remove options height limit, which could lead to annoying nested
  verical scroll regions.  (An inner region for the options inside a
  scrollable viewport.)
2023-08-19 08:23:31 -07:00
Daniel Imms 156f75628a Merge pull request #4692 from Tyriar/2117
Stub out all CSI api tests
2023-08-19 06:25:57 -07:00
Daniel Imms 8fad65d1d5 Stub out all CSI tests
Part of #2117
2023-08-19 06:11:29 -07:00
Daniel Imms 0867804504 Prefer arrow function 2023-08-19 05:33:49 -07:00
Daniel Imms f0900fdc5f Tweak format of CSI test names to make it easier to see terminating char 2023-08-19 05:32:41 -07:00
Daniel Imms 0be744b567 Merge pull request #4691 from Tyriar/4690
Improve lifecycle of WebLinksAddon.api, test all country tlds
2023-08-19 05:22:14 -07:00
Daniel Imms 243b0bb626 Add all country tlds to tests 2023-08-19 05:07:41 -07:00
Daniel Imms 0b32b42134 Improve lifecycle of WebLinksAddon.api
Part of #4690
2023-08-19 05:00:45 -07:00
Daniel Imms ef1cd4318b Merge pull request #4688 from Tyriar/cursor_style
Simplify cursor style types in webgl
2023-08-19 04:51:15 -07:00
Per Bothner 6e39d03a52 Some html/css cleanps in demo
* id="grid" should be id="class" (since "grid" is not unique in the document).
* Remove space after 'id=' (multiple places).
* Group 3 texture-atlas elements in new <div class="texture-atlas-container">.
2023-08-18 16:30:36 -07:00
Daniel Imms 7b7dbd36d6 Merge pull request #4689 from Tyriar/4685
Track CursorBlinkStateManager in MutableDisposable
2023-08-18 14:38:05 -07:00
Daniel Imms d03b5c9481 Track CursorBlinkStateManager in MutableDisposable
Fixes #4685
2023-08-18 14:26:56 -07:00
Daniel Imms 93e060aeb2 Add MutableDisposable tests 2023-08-18 14:24:50 -07:00
Daniel Imms a84351ad0c Add MutableDisposable
Part of #4685
2023-08-18 14:15:56 -07:00
Daniel Imms 25a78c77f2 Simplify cursor style types in webgl
Fixes #4686
2023-08-18 14:11:17 -07:00
Daniel Imms a35fa611ff Merge pull request #4687 from Tyriar/tracing
Add trace log level, @traceCall decorator and use in webgl
2023-08-18 11:14:52 -07:00
Daniel Imms 0b54c9a5d6 Add trace log level, traceCall decorator and use in webgl
- New 'trace' log level in the API, you should expect very spammy logs when
  trace is on and filter them down. As opposed to 'debug' which should be more
  readable.
- traceCall decorator logs the args and return value of the decorated method.
  Wecan use this on key functions in various parts of the code to assist
  debugging issues that are hard to reproduce such as #4665.
- Decorated a few webgl render methods for #4665.
- There's an early exit in traceCall when not on the trace log level so the
  runtime impact when not in trace is negligable.

Part of #4665
2023-08-18 11:04:23 -07:00
Daniel Imms 3b3b23a2fd Merge pull request #4683 from Tyriar/tyriar/minor_cleanup
Remove unneeded comments, headless options
2023-08-17 09:25:31 -07:00
Daniel Imms 7b874473d3 Remove unneeded comments, headless options 2023-08-17 09:10:38 -07:00
Daniel Imms 9d7b9293eb Merge pull request #4681 from PerBothner/dom-selection
Fix bug in selection handling in dom renderer
2023-08-17 09:01:12 -07:00
Per Bothner 957e3e0f08 Fix bug in selection handling in dom renderer 2023-08-16 15:34:35 -07:00
Daniel Imms 2f0a7eee8d Merge pull request #4679 from Tyriar/4663
Reduce repetition with internal terminal types and inherit docs from API
2023-08-15 08:49:40 -07:00
Daniel Imms 1a3e14a24c Merge pull request #4680 from Tyriar/crash
Revert Stylesheet change
2023-08-15 08:49:05 -07:00
Daniel Imms a6b67fb8a6 Remove unused headless types file 2023-08-15 08:39:20 -07:00
Daniel Imms 843bdc4d55 Revert "Merge pull request #4611 from SimonSiefke/constructed-stylesheets"
This reverts commit 5889344dd2, reversing
changes made to 679c149164.
2023-08-15 08:33:42 -07:00
Daniel Imms 2aee19f0e1 Revert "Workaround crash that can happen in Electron/Chromium"
This reverts commit 2ca0ccbe0c.
2023-08-15 08:31:34 -07:00
Daniel Imms 9f3be5e28d Simplify terminal interfaces
Fixes #4663
2023-08-15 07:49:31 -07:00
Daniel Imms f69f803f0f Implement public APIs internally to inherit docs
Part of #4663
2023-08-15 07:46:51 -07:00
Daniel Imms 2eca26b210 Merge pull request #4678 from Tyriar/vscode189753
Workaround crash that can happen in Electron/Chromium
2023-08-15 07:01:28 -07:00
Daniel Imms 2ca0ccbe0c Workaround crash that can happen in Electron/Chromium
Part of microsoft/vscode#189753
2023-08-15 06:46:35 -07:00
Daniel Imms c908eeed5c Merge pull request #4667 from Dennnnny/fix-transparent-foreground-color
Fix transparent foreground color
2023-08-14 12:11:04 -07:00
Daniel Imms caa1223e5c Merge pull request #4677 from Tyriar/fix_headless
Fix headless types regression
2023-08-14 07:18:50 -07:00
Daniel Imms 62cf3d39e1 Fix headless types regression
From the API lint PR
2023-08-14 07:15:20 -07:00
Daniel Imms 2437b68d5d Merge pull request #4676 from Tyriar/4097_2
Remove redundant condition
2023-08-14 06:01:07 -07:00
Daniel Imms 09fc798e80 Remove redundant condition
Part of #4097
2023-08-14 05:47:25 -07:00
Daniel Imms 7ae6febedc Merge pull request #4673 from Tyriar/4097
DOM: Render selection color instead of cell bg
2023-08-14 05:34:14 -07:00
Daniel Imms 39ae0f45fd Fix selectionForeground dom tests 2023-08-14 04:43:55 -07:00
dennnnny 855e911398 Fix transparent foreground color 2023-08-14 09:49:18 +08:00
Daniel Imms 73a77cda3d DOM: Render selection color instead of cell bg
Summary:

- Selection cell reuse has been added to the DOM renderer
- Selection bg override is now correctly set correctly
- Selection elements 'pretend' to be a decoration in order to render bg

Fixes #4097
2023-08-13 13:56:51 -07:00
Daniel Imms 6c9346011d Merge pull request #4672 from Tyriar/4262
Enforce half minimum contrast ratio for dim text
2023-08-13 13:14:45 -07:00
Daniel Imms 28c4732abc Fix ThemeService.ctor test 2023-08-13 13:04:40 -07:00
Daniel Imms 1cef30cca0 Enforce half minimum contrast ratio for dim text
Provided MCR is not too high, this ensures dim text differs from normal
text and has a reasonable difference in contrast.

Fixes #4262
2023-08-13 11:27:57 -07:00
Daniel Imms af787e1c85 Merge pull request #4671 from Tyriar/4652
Fix SortedList.values iteration and general dec lifecycle fixes
2023-08-13 10:53:20 -07:00
Daniel Imms 70f6fa2bc9 Fix SortedList.values iteration and general dec lifecycle fixes
Fixes #4652
2023-08-13 10:42:45 -07:00
dennnnny bef87403db Fix force transparent foreground colors to be opaque 2023-08-13 17:52:49 +08:00
Daniel Imms 5890029f71 Merge pull request #4669 from Tyriar/api_lint
Lint API .d.ts files
2023-08-12 10:32:53 -07:00
Daniel Imms 1f4fe0a8c0 Lint API .d.ts files
Similar to #4668, this brings linting to the API files with a cut down
set of rules. One of the bigger ones is comment length is restricted to 80,
this was done as opposed to 100 for regular code to reduce the chance of
wrapping or API going off screen regardless of resolution or window/browser
size
2023-08-12 09:33:35 -07:00
Daniel Imms 519f6a3d4a Merge pull request #4668 from Tyriar/comment_lint
Restrict comments to 100 characters
2023-08-12 09:18:14 -07:00
Daniel Imms 530f758905 Restrict comments to 100 characters
This is a rule I try enforce in PR but it's a bit of a hassle, this will
give a lint warning when it happens resulting in:

- Yellow underline in supported editors when eslint is installed
- Compilation will succeed since it's a warning not an error
- CI will fail as it does not allow lint warnings

Special case comments are ignored which includes @vt comments, table
comments and commented out code.
2023-08-12 09:04:41 -07:00
dennnnny fb7349bc87 Fix transparent foreground color 2023-08-12 08:39:39 +08:00
Daniel Imms dc390ab03a Merge pull request #4657 from tisilent/add-cursorInactiveStyle-option
Add cursorInactiveStyle option
2023-08-11 13:16:51 -07:00
Daniel Imms 6f6cc6a686 Remove string mapping, improve types, standardize on 'outline' 2023-08-11 13:09:31 -07:00
Daniel Imms 136e2f2afe Improve API wording 2023-08-11 13:07:04 -07:00
Daniel Imms 73bb72dbcf Merge pull request #4666 from Tyriar/nvm
Add nvmrc using node 16
2023-08-11 10:03:41 -07:00
Daniel Imms 4b2f774f53 Merge pull request #4664 from tisilent/fix-canvas-underline-cutoff
Fix canvas underline is cut off.
2023-08-11 10:01:28 -07:00
Daniel Imms 0a4151fbfa Add nvmrc using node 16
node-pty doesn't seem to work 18 yet and VS Code has moved over to be based
on 18 so that's my main (don't ask me why it's working there but not in this
repo)
2023-08-11 09:52:15 -07:00
Daniel Imms 07d187151e Update src/browser/renderer/shared/TextureAtlas.ts 2023-08-11 09:50:22 -07:00
Daniel Imms 47f606f0df Merge pull request #4662 from Tyriar/inwasm
Add functional yarn wasm
2023-08-11 05:09:25 -07:00
Daniel Imms 18125dad84 Add functional yarn wasm
This is what I tried after seeing the error so it will improve the #4646
experience until a real fix is in place

Part of #4646
2023-08-11 04:58:07 -07:00
tisilent f317b96262 Fix canvas underline is cut off. 2023-08-11 17:58:29 +08:00
tisilent c3d8c2a919 Update test and fix 2023-08-11 15:47:50 +08:00
tisilent 5c1e6dce99 Add block to cursorInactiveStyle,
change line to bar
2023-08-11 11:36:40 +08:00
tisilent 0321bcb715 Remove special classes.
Add xterm-cursor-outline class.
2023-08-11 09:40:06 +08:00
Daniel Imms bb5d61fb14 Merge pull request #4659 from SvanT/dispose-blink-state-manager
Dispose blink state manager
2023-08-10 10:27:24 -07:00
Svante Boberg 3b3012353f Use disposable registration 2023-08-10 19:04:20 +02:00
Svante Boberg adad53d526 Dispose blink state manager 2023-08-10 18:47:04 +02:00
Daniel Imms 1b4460e37d Merge pull request #4655 from Tyriar/tyriar/4645
Fix API facade memory leaks
2023-08-10 07:29:14 -07:00
tisilent 482dad3036 Add cursorInactiveStyle option 2023-08-10 16:11:04 +08:00
Daniel Imms 7276027861 Merge pull request #4651 from jerch/fix_4642
fix decorations on DOM renderer
2023-08-09 16:08:23 -07:00
Daniel Imms 62055522fb Fix API facade memory leaks
We need to register/dispose any event created in the facades, otherwise
embedders need to dispose of the listeners manually.

Fixes #4645
2023-08-09 15:55:31 -07:00
jerch 0366aa2217 Merge branch 'master' into fix_4642 2023-08-10 00:24:03 +02:00
Jörg Breitbart 2b79b7b29f faster SortedList._search 2023-08-10 00:12:00 +02:00
Daniel Imms 02243173c2 Merge pull request #4648 from tisilent/fix-double-underline
fix double underline, upwards
2023-08-09 06:45:03 -07:00
Jörg Breitbart a84a95055d exclude decorated cells from merging 2023-08-09 14:20:08 +02:00
tisilent b8bf35be17 Add restrictToCellHeight,Restore abnormal format 2023-08-09 11:53:30 +08:00
tisilent 3ba579f680 fix double underline, upwards 2023-08-08 18:36:44 +08:00
Daniel Imms 4f41070b45 Merge pull request #4638 from Tyriar/4603
Fix reset and clear leaving viewport in a bad state
2023-08-02 11:55:14 -07:00
Daniel Imms d2031955ec Remove error from MockViewport.reset 2023-08-02 11:42:40 -07:00
Daniel Imms 91c08ed48e Fix clear leaving viewport in a bad state
Fixes #4603
2023-08-02 11:30:43 -07:00
Daniel Imms acf53f69e0 Ensure viewport is correctly reset on term.reset
Part of #4603
2023-08-02 11:18:00 -07:00
Daniel Imms bb237f109a Merge pull request #4637 from meganrogge/merogge/fix-acc-tree
fix a11y tree
2023-08-02 08:30:06 -07:00
meganrogge 44d4ec83fc revert change 2023-08-02 08:17:18 -07:00
meganrogge 33124a31bd fix #4571 2023-08-02 08:14:16 -07:00
Daniel Imms 1e90aab2a6 Merge pull request #4636 from Tyriar/4588
Add ignoreBracketedPasteMode option
2023-08-01 14:40:39 -07:00
Daniel Imms 44ced27bee Add ignoreBracketedPasteMode option
Fixes #4588
2023-08-01 13:58:52 -07:00
Daniel Imms c3def09c38 Merge pull request #4631 from jerch/missing_image_changes
image addon fixes
2023-08-01 12:38:59 -07:00
Daniel Imms afff339d28 Merge pull request #4605 from jerch/faster_dom
Faster DOM renderer
2023-08-01 12:32:56 -07:00
Jörg Breitbart b7a597f925 use offsetWidth for measuring 2023-08-01 19:53:41 +02:00
Jörg Breitbart ae84ee39f3 Merge branch 'master' into faster_dom 2023-08-01 19:21:30 +02:00
jerch 73e60586c9 Merge pull request #4584 from dlech/fix-underline-style
use border-bottom instead of box-shadow for underline cursor in DOM renderer
2023-08-01 19:15:41 +02:00
Jörg Breitbart b56bc77dcc make font variant positions explicit with const enum 2023-08-01 18:35:48 +02:00
Jörg Breitbart 32a852c0bf remove private binding of document in width cache ctor 2023-08-01 18:30:06 +02:00
Jörg Breitbart 40e622dfc8 change comment format 2023-08-01 18:29:00 +02:00
Jörg Breitbart 994861a6fa change repeat variable to const enum 2023-08-01 18:25:29 +02:00
Jörg Breitbart b770431102 Merge branch 'master' into faster_dom 2023-08-01 18:18:31 +02:00
Jörg Breitbart 2fd108c864 Merge branch 'missing_image_changes' of github.com:jerch/xterm.js into missing_image_changes 2023-08-01 18:12:50 +02:00
Jörg Breitbart 914f7be644 add image addon to publish 2023-08-01 18:11:03 +02:00
jerch 1fdb8c006d Merge branch 'master' into missing_image_changes 2023-08-01 17:42:34 +02:00
Daniel Imms b46404b139 Merge pull request #4568 from xzfc/webgl-cursor
Render the cursor in the WebGL canvas
2023-08-01 08:41:16 -07:00
Daniel Imms 1dfd14fbc0 Fix canvas count test 2023-08-01 08:28:59 -07:00
Jörg Breitbart a1ddc629dc make tests less data intensive 2023-08-01 17:26:02 +02:00
Daniel Imms 9cfc2c881c Merge pull request #4626 from Tyriar/tyriar/light_cursor
Fix demo light theme cursor colors
2023-08-01 08:08:29 -07:00
Jörg Breitbart b7f9db69ca update readme 2023-08-01 16:51:56 +02:00
Daniel Imms 60eb4f3385 Create ICursorRenderModel 2023-08-01 07:47:27 -07:00
Daniel Imms 41e97ec042 Always render cursor even if model didn't change
This ensures underline/bar cursors get rendered
2023-08-01 07:43:27 -07:00
Jörg Breitbart fe838726b0 transfer changes from 0.4.3 2023-08-01 16:43:14 +02:00
Daniel Imms 1cfce3d0b6 Fix demo light theme cursor colors 2023-08-01 06:37:00 -07:00
Daniel Imms f64cb1566d Correctly apply cursor colors in new renderer 2023-08-01 06:31:58 -07:00
Daniel Imms 9b204c012a Remove bad logs 2023-08-01 06:19:54 -07:00
Daniel Imms 6b33ac1e85 Share cursor blink state manager between canvas and webgl
This also focused the manager's creation on renderer start up, to ensure blink
works immediately.
2023-08-01 06:13:21 -07:00
Daniel Imms 18e578bd09 Merge branch 'master' into webgl-cursor 2023-08-01 05:53:45 -07:00
Daniel Imms e8a925b6af Merge pull request #4583 from jerch/fix_mouse_tracking
fix mouse tracking behavior
2023-08-01 05:50:20 -07:00
Daniel Imms db6e0cd2ef Merge pull request #4482 from tisilent/#4121
Add smoothScroll to scrollLines
2023-08-01 05:48:26 -07:00
Daniel Imms f6738ec354 Merge pull request #4577 from jerch/image_addon
move image addon to base repo
2023-08-01 05:40:48 -07:00
Daniel Imms 1067ec19a8 Implement smooth scroll for pages/top/bottom, add viewport request event 2023-08-01 05:31:30 -07:00
Daniel Imms e7a7842808 Use viewport source in tests 2023-08-01 05:11:19 -07:00
Daniel Imms ce3bb35c8c Merge pull request #4366 from arekouzounian/master
Fix for #2488. Use element offsetHeight rather than getBoundingRect
2023-08-01 05:05:42 -07:00
Daniel Imms 2d7b531bba Merge branch 'master' into #4121 2023-08-01 04:58:36 -07:00
Daniel Imms 2d81490ac4 Merge pull request #4387 from LabhanshAgrawal/ligatures-electron
Enable ligatures addon on electron
2023-08-01 04:57:16 -07:00
Daniel Imms 5939d72d8f Merge branch 'master' into master 2023-08-01 04:54:05 -07:00
Daniel Imms d1bd4973ad Merge pull request #4348 from silamon/ansiheadlessretry
SerializeAsHTML(): Fallback to default ansi colors when running headless terminal
2023-08-01 04:52:43 -07:00
Jörg Breitbart f407f0c5a6 unit tests 2023-07-31 21:24:39 +02:00
Jörg Breitbart 40eccfa61a Merge branch 'faster_dom' of github.com:jerch/xterm.js into faster_dom 2023-07-31 20:07:56 +02:00
Jörg Breitbart c79aa542c9 cache optimizations 2023-07-31 20:07:40 +02:00
jerch 366ffedf08 Merge branch 'master' into faster_dom 2023-07-31 18:01:16 +02:00
Jörg Breitbart c796514de8 apply weight settings in width cache 2023-07-31 16:49:58 +02:00
Jörg Breitbart e75ec5b51f revert changes to selection service 2023-07-31 16:09:44 +02:00
Jörg Breitbart 702c3578a3 re-add explicit span stylesheet 2023-07-30 19:03:54 +02:00
Jörg Breitbart 0eecf8187d Merge branch 'faster_dom' of github.com:jerch/xterm.js into faster_dom 2023-07-30 18:46:49 +02:00
Jörg Breitbart e9058b7498 implement default letter spacing 2023-07-30 18:46:33 +02:00
Daniel Imms 25b266e809 Merge pull request #4618 from Tyriar/4614_color_index
Add IntRange type for ColorIndex
2023-07-30 07:04:14 -07:00
Daniel Imms 2e638598e4 Simplify condition 2023-07-30 06:55:45 -07:00
jerch 8562635231 Merge branch 'master' into faster_dom 2023-07-30 13:37:45 +02:00
Jörg Breitbart b358754b46 respect dpr/charWidth changes, more accurate base measuring 2023-07-30 13:27:28 +02:00
Jörg Breitbart 64d04f0712 optimization: remove document fragment 2023-07-29 23:58:15 +02:00
Daniel Imms ebdafe54aa Merge pull request #4619 from Tyriar/tyriar/4611_2
Avoid using global document, add copyright
2023-07-29 14:50:50 -07:00
Daniel Imms 829ecac8e3 Merge branch 'master' into image_addon 2023-07-29 14:43:46 -07:00
Daniel Imms d22e3e093a Fix compile 2023-07-29 14:42:05 -07:00
Daniel Imms a9662e8b7b Avoid using global document, add copyright
Fixes #4611
2023-07-29 14:38:56 -07:00
Jörg Breitbart 5f98db9b76 re-enable failing test 2023-07-29 18:26:31 +02:00
Jörg Breitbart 664a110fd1 Merge branch 'master' into faster_dom 2023-07-29 18:07:47 +02:00
Jörg Breitbart 3b5044b55b readd doc notes on link underline handling 2023-07-29 17:54:14 +02:00
Jörg Breitbart e848151b50 change to width cache 2023-07-29 17:25:49 +02:00
Jörg Breitbart 91197bd1aa disable font kerning 2023-07-29 15:37:01 +02:00
Jörg Breitbart d1fdaa17d6 apply font variant, some cleanup 2023-07-29 14:16:47 +02:00
Daniel Imms 5660c404f0 Differentiate special color indexes and others 2023-07-28 16:32:05 -07:00
Daniel Imms 79c1fff853 Add IntRange type for ColorIndex
Fixes #4614
2023-07-28 13:51:46 -07:00
Daniel Imms 962c070a75 Merge branch 'master' into webgl-cursor 2023-07-28 13:42:47 -07:00
Jörg Breitbart 5bdd5022b2 some cleanup, use faster str join for classes 2023-07-28 19:33:38 +02:00
Jörg Breitbart 370bad1380 better caching:
- own cache class with flat and holey cache
- use letter spacing
- on-demand measuring
2023-07-28 18:36:01 +02:00
Daniel Imms 17b8247c3d Merge pull request #4617 from xtermjs/Tyriar-patch-1
Mention discussions in CONTRIBUTING.md
2023-07-28 07:44:13 -07:00
Daniel Imms c0d1dec847 Mention discussions in CONTRIBUTING.md 2023-07-28 07:33:35 -07:00
Daniel Imms 5889344dd2 Merge pull request #4611 from SimonSiefke/constructed-stylesheets
feature: use constructed style sheets for dom render so that it works with csp style-src 'self'
2023-07-27 12:45:31 -07:00
Simon Siefke 5f80fe00d5 fix: use replacesync instead of replace 2023-07-27 18:33:30 +02:00
Simon Siefke b225b9a710 fix eslint 2023-07-27 17:39:15 +02:00
Daniel Imms 72c51b19a5 Merge branch 'master' into constructed-stylesheets 2023-07-27 08:28:24 -07:00
Daniel Imms 679c149164 Merge pull request #4612 from SimonSiefke/update-typescript-version
update typescript version to 5.1.6
2023-07-27 08:28:09 -07:00
Simon Siefke 92a18defce remove one more public 2023-07-27 17:16:00 +02:00
Simon Siefke 859c9ec68d update typescript-eslint 2023-07-27 17:11:04 +02:00
Simon Siefke b4aeb1b3e7 fix eslint warnings 2023-07-27 13:29:02 +02:00
Simon Siefke 8f1313519f update typescript version to 5.1.6 2023-07-27 13:19:38 +02:00
Simon Siefke 1c0a25d01b feature: use constructed style sheets for dom render so that it works with csp style-src 'self' 2023-07-27 13:08:22 +02:00
Jörg Breitbart a5df5e7333 first unit tests 2023-07-27 11:25:57 +02:00
Jörg Breitbart 4bd3e1d0c2 remove magic numbers 2023-07-25 13:19:08 +02:00
Jörg Breitbart ad145541af exclude bold&italic variants from merge, limit glyph calc to latin + latin supplement 2023-07-25 12:47:35 +02:00
Jörg Breitbart 37153018b9 fix BCE 2023-07-24 16:06:19 +02:00
Jörg Breitbart 90f1318596 skip failing test for now 2023-07-24 00:01:51 +02:00
Jörg Breitbart 4a5ae78d35 try to fix search test error 2023-07-23 23:52:17 +02:00
Jörg Breitbart 097879b839 faster metrics calc 2023-07-23 23:36:25 +02:00
Jörg Breitbart 2d9ad32d93 fix weblinks api test 2023-07-23 22:25:58 +02:00
Jörg Breitbart 4d257f469b fix linter 2023-07-23 21:12:38 +02:00
Jörg Breitbart 521dc915dc fix weblink tests 2023-07-23 12:36:15 +02:00
Jörg Breitbart 5ea669f091 batch metrics calc 2023-07-23 00:08:40 +02:00
Jörg Breitbart aa6e8d91c2 cleanup 2023-07-22 22:50:05 +02:00
Jörg Breitbart 089dc282c3 fix hover underline 2023-07-22 22:18:12 +02:00
Jörg Breitbart 329b465a63 fix SP for underline/overline 2023-07-22 16:53:13 +02:00
Jörg Breitbart 6785dc19ab exclude combined from merge, fix linter 2023-07-22 16:32:01 +02:00
Jörg Breitbart c515cc2823 fix ext underline handling 2023-07-22 16:20:47 +02:00
Jörg Breitbart 848f89e8a7 fix char under cursor 2023-07-22 15:24:50 +02:00
Jörg Breitbart bd7f76190c fix superfluous selection.refresh calls 2023-07-22 15:12:37 +02:00
Jörg Breitbart 514afcd445 fix bad runtime of merger 2023-07-22 13:29:26 +02:00
Jörg Breitbart 3e13abf974 fix selection handling 2023-07-22 13:01:53 +02:00
Jörg Breitbart 4cfe32f626 make linter happy 2023-07-22 11:08:51 +02:00
Jörg Breitbart f36954764d faster DOM updates 2023-07-22 11:00:12 +02:00
Daniel Imms 1c98037eed Merge pull request #4599 from xtermjs/dependabot/npm_and_yarn/word-wrap-1.2.4
Bump word-wrap from 1.2.3 to 1.2.4
2023-07-18 14:55:34 -07:00
dependabot[bot] 2d732e3a99 Bump word-wrap from 1.2.3 to 1.2.4
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-18 21:27:19 +00:00
Daniel Imms 53d105714a Merge pull request #4596 from lewis-sanchez/lewissanchez/bug/rasterized-glyph-undefined
Add getRasterizedGlyph null and undefined check
2023-07-15 17:56:31 -07:00
Lewis Sanchez 78aad113a6 Minor clean up 2023-07-14 15:07:38 -07:00
Lewis Sanchez d2c28c819f Add getRasterizedGlyph null and undefined check 2023-07-14 11:31:59 -07:00
Daniel Imms 3d75333e95 Merge pull request #4595 from matifali/patch-1
Add Coder to the list
2023-07-14 07:48:40 -07:00
Muhammad Atif Ali 7e5bdb5321 Add Coder Self-Hosted Remote Development Environments to list 2023-07-14 14:08:34 +03:00
Daniel Imms be74969d70 Merge pull request #4589 from xtermjs/dependabot/npm_and_yarn/semver-5.7.2
Bump semver from 5.7.1 to 5.7.2
2023-07-11 04:35:17 -07:00
dependabot[bot] 196f863441 Bump semver from 5.7.1 to 5.7.2
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-11 10:14:24 +00:00
Daniel Imms 823258e311 Merge pull request #4587 from xtermjs/dependabot/npm_and_yarn/tough-cookie-4.1.3
Bump tough-cookie from 4.0.0 to 4.1.3
2023-07-09 08:55:23 -07:00
dependabot[bot] e100a4310b Bump tough-cookie from 4.0.0 to 4.1.3
Bumps [tough-cookie](https://github.com/salesforce/tough-cookie) from 4.0.0 to 4.1.3.
- [Release notes](https://github.com/salesforce/tough-cookie/releases)
- [Changelog](https://github.com/salesforce/tough-cookie/blob/master/CHANGELOG.md)
- [Commits](https://github.com/salesforce/tough-cookie/compare/v4.0.0...v4.1.3)

---
updated-dependencies:
- dependency-name: tough-cookie
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-09 10:15:18 +00:00
Daniel Imms 681c912533 Merge pull request #4585 from tomgroenwoldt/add-helix-editor-playground-project
Add helix editor playground project to real world uses
2023-07-06 07:25:42 -07:00
Tom Groenwoldt 6339c09c01 Add helix editor playground project to real world uses 2023-07-06 11:36:23 +02:00
David Lechner 96634298e4 use border-bottom instead of box-shadow for underline cursor
In rare cases, box-shadow can cause aliasing artifacts on the top and
sides of the cursor box when only the bottom should be filled in to
create an underline.

This replaces the box-shadow style with border-bottom which should be
pixel perfect in all cases.

Fixes: https://github.com/xtermjs/xterm.js/issues/4580
2023-07-05 16:48:42 +00:00
Jörg Breitbart 65568f502e fix #4582 2023-07-05 13:56:41 +02:00
tisilent 1fb9f0185a format 2023-06-30 11:45:50 +08:00
tisilent 74c5256d6d using _smoothScroll 2023-06-30 11:44:06 +08:00
Jörg Breitbart 3991b5f251 make linter happy 2023-06-27 17:11:45 +02:00
Jörg Breitbart 9afb924e84 different loop for failing test 2023-06-27 17:06:43 +02:00
Jörg Breitbart 7a82e7370f fix timeout issue again 2023-06-27 16:51:11 +02:00
Jörg Breitbart 709e9fd01a remove left over .only 2023-06-27 16:20:33 +02:00
Jörg Breitbart e16f87f973 use pollFor in failing test 2023-06-27 16:20:11 +02:00
Jörg Breitbart d4ddac704c more waits for remote browser under heavy CI load 2023-06-27 15:57:07 +02:00
Jörg Breitbart bc92d61616 remove left over .only 2023-06-27 15:41:54 +02:00
Jörg Breitbart 4884d81704 fix another heavy CI load timeout issue 2023-06-27 15:35:52 +02:00
Jörg Breitbart d99bdb7710 fix timeout issue in CI 2023-06-27 15:15:51 +02:00
Jörg Breitbart eaa265b4fa add image options and tests to demo 2023-06-27 15:04:55 +02:00
Jörg Breitbart 8fa3cae618 integrate addon into demo 2023-06-27 14:04:27 +02:00
Jörg Breitbart 0027b27ea3 add wasm builds 2023-06-27 13:49:49 +02:00
Jörg Breitbart 2bcc653293 remove only from api test 2023-06-27 13:43:02 +02:00
Jörg Breitbart ebfff5efee initial move to base repo 2023-06-27 13:35:59 +02:00
Daniel Imms ac0207bf2e Merge pull request #4570 from naufalafif/cloudtutor-io-patch-1
add cloudtutor.io to real world uses
2023-06-19 07:52:33 -07:00
naufal afif 8068880aab add cloudtutor.io to real world uses 2023-06-19 10:26:12 +08:00
Albert Safin 064fe0cfd9 Render the cursor in the WebGL canvas 2023-06-18 06:34:14 +00:00
Daniel Imms 096fe17135 Merge pull request #4565 from Tyriar/tyriar/readonly_log
Improve safety of log service
2023-06-16 10:42:47 -07:00
Daniel Imms 4d501b38b8 Improve safety of log service 2023-06-16 10:30:54 -07:00
Daniel Imms f79e9a1951 Merge pull request #4564 from Tyriar/bind_loggers
Bind logger calls
2023-06-16 09:30:04 -07:00
Daniel Imms 64a62fb0c1 Bind logger calls 2023-06-16 09:19:23 -07:00
Daniel Imms 2c45989fea Merge pull request #4563 from Tyriar/logger
Add logger delegate option
2023-06-16 08:34:13 -07:00
Daniel Imms f779e4ddc0 Add logger delegate option
This allows embedders to route the logs whereever they want, when this
is set the xterm.js prefix is excluded.
2023-06-16 08:23:22 -07:00
Daniel Imms 7be0016e45 Merge branch 'master' into #4121 2023-06-10 14:02:21 -07:00
Daniel Imms ac923e55d2 Merge pull request #4553 from Tyriar/v5_2_1
v5.2.1
2023-06-10 14:01:33 -07:00
Daniel Imms e08e5607ed v5.2.1 2023-06-10 13:59:51 -07:00
Daniel Imms 2ac2eeadfc Merge pull request #4551 from Tyriar/npes
Fix NPEs on buffer line and windowsPty option
2023-06-09 08:59:24 -07:00
Daniel Imms ffadc0b31d Fix NPEs on buffer line and windowsPty option 2023-06-09 08:47:17 -07:00
Daniel Imms cc1089e943 Merge pull request #4549 from Tyriar/r5_2_0
v5.2.0
2023-06-08 14:43:25 -07:00
Daniel Imms 47c21df83d v5.2.0 2023-06-08 14:32:54 -07:00
Daniel Imms 380e6805e8 Merge pull request #4539 from Tyriar/tyriar/windowsPty
Add more granular windowsPty option
2023-06-07 11:41:13 -07:00
xie jialong 努力鸭 f616ebb083 Merge branch 'master' into #4121 2023-06-07 19:23:01 +08:00
tisilent ce2d115087 Move logic to Viewport 2023-06-06 20:14:24 +08:00
Daniel Imms 9f424833d6 Merge pull request #4546 from jerch/fix_4545
DOM renderer link underline
2023-06-05 05:20:54 -07:00
Jörg Breitbart 90ccc815cb more verbose comments to get the idea of the code 2023-06-05 12:23:56 +02:00
Jörg Breitbart 8de7bd5850 rename lneX to maxY 2023-06-04 21:11:55 +02:00
Jörg Breitbart 9fed8e2a3a defensive underline event coords handling in DOM renderer 2023-06-04 21:06:26 +02:00
Daniel Imms 118915c1f7 Merge pull request #4542 from silamon/node18
Upgrade to Node18
2023-05-31 10:50:12 -07:00
Simon Lamon fca593d93b Fix unit testing (headless) 2023-05-31 17:37:41 +00:00
Simon Lamon 3376ee2f85 Node 18 2023-05-31 17:14:49 +00:00
Daniel Imms f6fbf63673 Add windowsPty unit test 2023-05-29 09:50:52 -07:00
Daniel Imms 3e5c6a156b Correct Windows wrapping heuristics condition 2023-05-29 09:49:33 -07:00
Daniel Imms a91665cb18 Listen for windowsPty changes 2023-05-29 09:43:49 -07:00
Daniel Imms 22140b5e94 Add more granular windowsPty option
See microsoft/vscode#134448
2023-05-29 09:40:00 -07:00
Daniel Imms 22a4dc4b6b Merge pull request #4478 from jerch/fix_4469
fix DECRQM reports
2023-05-24 06:47:52 -07:00
Megan Rogge 0ff06db417 bring back accessibility tree (#4536) 2023-05-22 21:17:25 +00:00
Daniel Imms 83a77b6024 Merge pull request #4533 from Tyriar/4480
Workaround drawing off invalid bitmap in canvas
2023-05-21 09:13:18 -07:00
Daniel Imms 8b422442b7 Close generator before deleting it 2023-05-21 09:05:21 -07:00
Daniel Imms 02ce3eff87 Workaround drawing off invalid bitmap in canvas
Fixes #4480
2023-05-21 08:51:31 -07:00
Daniel Imms 518b87f01d Merge pull request #4532 from Tyriar/4529
Finish overline support
2023-05-21 08:25:20 -07:00
Daniel Imms 6188c07bf7 Serialize overline
Fixes #4529
2023-05-21 08:17:06 -07:00
Daniel Imms 325d133a75 Add overline to vt comments
Part of #4529
2023-05-21 08:16:57 -07:00
Daniel Imms b662824b81 Add isOverline to public API
Part of #4529
2023-05-21 08:16:38 -07:00
Daniel Imms ada5bfe048 Merge pull request #4530 from jerch/fix_atlas_merge_webgl
fix blackout lines in webgl
2023-05-21 07:52:38 -07:00
Jörg Breitbart f270127ce5 fix webgl part of #4480 2023-05-21 16:32:57 +02:00
Daniel Imms 7460782494 Merge pull request #4527 from jerch/fix_4485
fix buffer corruption for utf8 transport in demo
2023-05-21 07:03:21 -07:00
Jörg Breitbart 048010c219 fix buffer corruption for utf8 transport 2023-05-21 12:28:15 +02:00
Daniel Imms cdec1e5b03 Merge pull request #4526 from Tyriar/4499_overline
Add support for SGR 53 overline and SGR 55 not overline
2023-05-20 14:21:22 -07:00
Daniel Imms eb56356133 Don't dodge overline, simplify drawing 2023-05-20 10:04:14 -07:00
Daniel Imms 2bfd95fe54 Add support for SGR 53, 55 overline
Specced in ECMA-48
2023-05-20 09:56:56 -07:00
Daniel Imms e9b0593298 Merge pull request #4525 from Tyriar/4161_dim
Don't apply dim to background color
2023-05-20 09:43:16 -07:00
Daniel Imms d5c47ec531 it -> itWebgl 2023-05-20 09:35:37 -07:00
Daniel Imms 5a145fcffe Add webgl api tests for dim 2023-05-20 08:41:11 -07:00
Daniel Imms 539fed4daa Don't apply dim to background color
Fixes #4161
2023-05-20 08:13:50 -07:00
Daniel Imms a0493a604c Merge pull request #4521 from jeanp413/jp/fix-forced-reflow
Avoid triggering a reflow while rendering search decorations
2023-05-17 16:04:16 -07:00
Jean Pierre 07e8f86bd1 Avoid triggering a reflow 2023-05-17 17:03:25 -05:00
Daniel Imms 1dbc1bf639 Merge pull request #4516 from jeanp413/jp/fix-decoration-render-layer
Ensure decorations in the top layer render on top of decorations in the bottom layer
2023-05-17 07:08:47 -07:00
Daniel Imms 6e8c59f0db Merge pull request #4504 from jeanp413/jp/fix-4444
Fixes improve search addon behavior when there are > 1000 results
2023-05-17 07:02:29 -07:00
Daniel Imms ec227c757b Prefix class with xterm-decoration- 2023-05-17 06:57:13 -07:00
Jean Pierre 4fbeb58203 Update search demo 2023-05-15 22:18:36 -05:00
Jean Pierre 60f549028f Ensure decorations in the top layer render on top of decorations in the bottom layer 2023-05-12 16:19:03 -05:00
Jean Pierre cc5136aeb3 Address feedback 2023-05-12 16:08:11 -05:00
Daniel Imms a1e9105ce7 Merge branch 'master' into jp/fix-4444 2023-05-12 06:24:34 -07:00
Daniel Imms 7a7f4f0add Merge pull request #4512 from xtermjs/Tyriar-patch-1
Add FAQ to new issue report
2023-05-10 06:40:53 -07:00
Daniel Imms 96ea9fe188 Add FAQ to new issue report
Hopefully this will reduce issue count a little
2023-05-10 06:31:54 -07:00
Jean Pierre b4e2293dbc Fix incremental 2023-05-09 16:14:28 -05:00
Daniel Imms 7194223696 Merge pull request #4508 from Tyriar/4507_zalgo
Disallow glyphs to exceed atlas texture size
2023-05-09 07:44:34 -07:00
Daniel Imms 8c9e44c219 Disallow glyphs to exceed atlas texture size
Fixes #4507
2023-05-09 07:35:41 -07:00
Daniel Imms 075f33031d Merge pull request #4475 from kelvinhammond/patch-1
Re-render browser DOM style onOptionsChanged, fixes xtermjs#4474
2023-05-09 07:12:58 -07:00
Jean Pierre e4b670231d Add tests 2023-05-08 09:08:35 -05:00
Jean Pierre 1c19f1d282 Remove incremental? 2023-05-07 15:19:23 -05:00
Jean Pierre 508589aa80 Improve search addon behavior when there are > 1000 results 2023-05-07 15:19:23 -05:00
Daniel Imms 854e2736f6 Move inject into _handleOptionsChanged 2023-05-07 06:27:56 -07:00
Kelvin 58f650e6f0 Merge branch 'master' into patch-1 2023-05-04 15:32:29 -04:00
Daniel Imms 913cb25863 Merge pull request #4491 from jerch/fix_4477
fix urls with screenreader
2023-05-03 08:19:23 -07:00
Daniel Imms 16f82cb3b5 Merge pull request #4500 from Tyriar/4487
Clear whole cursor layer on Firefox
2023-05-02 15:54:23 -07:00
Daniel Imms 0a101a01f5 Clear whole cursor layer on Firefox
Fixes #4487
2023-05-02 15:44:36 -07:00
Jörg Breitbart 9638063359 fix #4477 2023-04-24 12:30:41 +02:00
tisilent b7d73f63cd Add smoothScroll to scrollLines 2023-04-19 15:48:12 +08:00
Jörg Breitbart ee26780e03 fix test cases 2023-04-16 21:17:12 +02:00
Jörg Breitbart b35415ed16 flip SET 2 & 12 perma flags, add DECSET 67 (DECBKM) as perma reset 2023-04-16 21:08:42 +02:00
Kelvin 2a9ec8fd1d Re-render browser DOM style onOptionsChanged, fixes xtermjs#4474
Fixes https://github.com/xtermjs/xterm.js/issues/4474
2023-04-11 13:43:32 -04:00
Daniel Imms 2fdb46919c Merge pull request #4465 from Tyriar/4410
Ensure Decoration.isDisposed can be true
2023-03-31 12:21:52 -07:00
Daniel Imms c8e633ecb5 Merge pull request #4464 from Tyriar/4409
Correct registerMarker docs
2023-03-31 12:12:19 -07:00
Daniel Imms c546f7cc8a Ensure Decoration.isDisposed can be true
Fixes #4410
2023-03-31 12:11:50 -07:00
Daniel Imms 6997f1a940 Correct registerMarker docs
Fixes #4409
2023-03-31 11:55:30 -07:00
Daniel Imms 14c6df3187 Merge pull request #4458 from Tyriar/tyriar/178014
Clear line wrapped state on explicit line feed
2023-03-28 11:46:45 -07:00
Daniel Imms 18be346df9 Clear line wrapped state on explicit line feed
Fixes #4457
See microsoft/vscode#178014
2023-03-28 11:35:21 -07:00
Daniel Imms 37297e09ed Merge pull request #4456 from xtermjs/Tyriar-patch-1
Add DOM term to real world use cases
2023-03-28 09:04:25 -07:00
Daniel Imms b2f0cc8dc1 Add DOM term to real world use cases
Fixes #4455
2023-03-28 08:51:24 -07:00
Daniel Imms 6cbee3349e Merge pull request #4451 from Starwort/master
Fix #4450
2023-03-27 06:36:53 -07:00
Starwort f0edc1b613 Fix #4450 2023-03-27 09:56:17 +01:00
Daniel Imms cd9867e222 Merge pull request #4443 from tisilent/#4441
Uniform cursor unfocused rendering. #4441
2023-03-23 07:58:55 -07:00
tisilent e1d852c86a Adjust DomRenderer cursor style 2023-03-22 21:09:28 +08:00
tisilent 455c744ab1 Uniform cursor unfocused rendering. #4441 2023-03-22 20:54:58 +08:00
Daniel Imms 21f48ba803 Merge pull request #4438 from xtermjs/jerch-patch-1
fix BCE render bug in canvas renderer
2023-03-15 07:51:45 -07:00
jerch 49fdacba6a fix BCE render bug in canvas renderer 2023-03-15 15:41:44 +01:00
Daniel Imms a359c7ca10 Merge pull request #4437 from xtermjs/dependabot/npm_and_yarn/webpack-5.76.0
Bump webpack from 5.61.0 to 5.76.0
2023-03-15 06:31:53 -07:00
dependabot[bot] 242eade757 Bump webpack from 5.61.0 to 5.76.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.61.0 to 5.76.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.61.0...v5.76.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-14 15:58:20 +00:00
Megan Rogge 4037b943b9 remove AccessibleBuffer (#4427)
* remove AccessibleBuffer

* rm more

* Update src/browser/Terminal.ts

* rm css
2023-03-02 14:51:32 -06:00
Labhansh Agrawal a8cec5f02f Enable ligatures addon on electron 2023-02-23 23:49:40 +05:30
Megan Rogge 9154831afa allow html elements (#4413) 2023-02-15 14:34:00 -06:00
Megan Rogge d6599bc048 accessibilityBuffer -> accessibleBuffer (#4406) 2023-02-08 21:48:20 +00:00
Megan Rogge 8a1faed5cb ensure line is defined via clamp (#4403) 2023-02-07 21:03:06 +00:00
Megan Rogge 72a157df5e add registerBufferElementProvider (#4402) 2023-02-07 09:56:23 -08:00
Megan Rogge 6daadc4c55 pull AccessibleBuffer out of AccessibilityManager (#4400) 2023-02-03 18:59:30 +00:00
Daniel Imms c05d0f650c Merge pull request #4398 from Tyriar/tyriar/a11y_remove
Remove accessibility tree code
2023-02-02 11:47:17 -08:00
Daniel Imms d5b5f1a9a4 Clean up 2023-02-02 06:24:18 -08:00
Daniel Imms dfcd674f8c Fix const -> let in LocalizableStrings
This needs to be settable outside, so const gives the wrong impression
2023-02-02 06:18:09 -08:00
Daniel Imms a7e0a9c534 Remove old accessibility tree code 2023-02-02 06:17:32 -08:00
Daniel Imms 5839accbf1 Use is prefix for boolean prop/getter 2023-02-02 06:12:04 -08:00
Megan Rogge 01c9a98e8e update accessibilityBufferActive on focusOut (#4395) 2023-02-01 20:57:52 +00:00
Daniel Imms bcfed71e1d Merge pull request #4393 from Tyriar/createinstance
Improve createInstance types
2023-02-01 12:38:30 -08:00
Daniel Imms 046d4b8314 Improve createInstance types 2023-02-01 08:42:51 -08:00
Megan Rogge a9da61e605 accessibilityBuffer: textarea -> div & prevent mouse click from exiting (#4389) 2023-01-31 18:25:25 -06:00
Daniel Imms 24c7be210c Merge pull request #4390 from Tyriar/webgl_exception
Handle glyph/rect renderer safer
2023-01-31 11:25:41 -08:00
Daniel Imms 9c815250b6 Handle glyph/rect renderer safer
See microsoft/vscode#172782
2023-01-31 11:14:33 -08:00
Megan Rogge 377f88796f append accessibility tree as child, not as adjacent elt to terminal (#4383) 2023-01-24 19:40:46 +00:00
Megan Rogge ed6c849501 attach and detach accessibility tree root element (#4382) 2023-01-24 09:53:10 -06:00
Megan Rogge 5a21948b4a make accessibility buffer readonly (#4381) 2023-01-23 19:03:19 +00:00
Megan Rogge b24bda92ad use textbox instead of div for accessibility buffer (#4377) 2023-01-20 17:36:25 +00:00
Megan Rogge 9895691e0e use content editable (#4376) 2023-01-19 21:30:58 +00:00
Megan Rogge 77870b06b9 fix #4374 (#4375) 2023-01-19 16:49:53 +00:00
Daniel Imms 41dc93a008 Prototype improved a11y buffer view (#4340) 2023-01-18 23:00:21 +00:00
Arek Ouzounian 79d2f738a2 fixed linter warnings 2023-01-06 15:22:02 -08:00
Arek Ouzounian 7701557b99 Fix for #2488. Use element offsetHeight rather than
getBoundingRect
2023-01-06 14:56:20 -08:00
Daniel Imms ba9f2e0f11 Merge pull request #4364 from Tyriar/dom_remove
Replace dom helper with HTMLElement.remove()
2023-01-05 09:22:55 -08:00
Daniel Imms b3fb8db6b3 Replace dom helper with HTMLElement.remove() 2023-01-05 09:14:16 -08:00
Daniel Imms 91946dcd9b Merge pull request #4363 from xtermjs/dependabot/npm_and_yarn/json5-2.2.3
Bump json5 from 2.1.2 to 2.2.3
2023-01-05 08:23:59 -08:00
dependabot[bot] 41c0e26292 Bump json5 from 2.1.2 to 2.2.3
Bumps [json5](https://github.com/json5/json5) from 2.1.2 to 2.2.3.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v2.1.2...v2.2.3)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-05 16:15:34 +00:00
Daniel Imms fe89817667 Merge pull request #4361 from xtermjs/circularlist-cleanup
Removed unused imports in CircularList
2022-12-28 07:19:26 -08:00
Eugene 7ee7aa2719 removed unused imports in CircularList 2022-12-28 15:41:58 +01:00
Daniel Imms b05c26c6fd Merge pull request #4358 from Tyriar/4357
Request links after viewport resize
2022-12-27 06:50:53 -08:00
Daniel Imms 0adbedf98d Request links after viewport resize
This clears the underline when hovering the link but this is an edge
case that isn't so important. As it's difficult to do outside of the
demo.

Fixes #4357
2022-12-27 06:41:58 -08:00
Daniel Imms 89ead0b4b7 Merge pull request #4356 from Coding-Kiwi/Coding-Kiwi-patch-1
Add graSSHopper to Real-world uses
2022-12-27 03:56:47 -08:00
Erik 349510065b Add graSSHopper to Real-world uses 2022-12-26 20:48:44 +01:00
Daniel Imms fa8a0e7f24 Merge pull request #4355 from Tyriar/4354
Clear canvas selection state after a resize
2022-12-24 08:10:44 -08:00
Daniel Imms 0c4e9c6b27 Merge pull request #4347 from silamon/deprecatedubuntu
Switch to Ubuntu 20.04
2022-12-24 08:06:14 -08:00
Daniel Imms de2b62eeac Merge pull request #4352 from Tyriar/4096
Include right part of wide char in underline gap
2022-12-24 08:05:41 -08:00
Daniel Imms 65b2af10f8 Clear canvas selection state after a resize
Fixes #4354
2022-12-24 08:02:41 -08:00
Daniel Imms 1a20fbb25f Merge pull request #4353 from Tyriar/4057
Force canvas renderer selection redraw on theme change
2022-12-24 08:02:06 -08:00
Daniel Imms d51ba2578b Force canvas renderer selection redraw on theme change
Fixes #4057
2022-12-24 07:46:51 -08:00
Daniel Imms 56734874ac Include right part of wide char in underline gap
Fixes #4096
2022-12-24 07:30:26 -08:00
Daniel Imms 72f1d1048a Merge pull request #4350 from Tyriar/dotted
Improve rendering of dotted underlines
2022-12-23 14:16:15 -08:00
Daniel Imms d17df65fad Improve rendering of dotted underlines
Fixes #4060
2022-12-23 14:04:12 -08:00
Simon Lamon ed8c2269cb Add proposed fix 2022-12-23 17:00:48 +00:00
Simon Lamon f1b47485cd Revert "Revert "SerializeAsHTML(): Fallback to default ansi colors when running headless terminal""
This reverts commit 3a81ef54ca.
2022-12-23 16:47:31 +00:00
Simon Lamon c2504fa669 Ubuntu 18.04 deprecation 2022-12-23 17:37:26 +01:00
Daniel Imms 5a2bf6d039 Merge pull request #4346 from Tyriar/4314_2
Clamp start and end in render service to ensure they're valid
2022-12-21 17:03:15 -08:00
Daniel Imms 568e4c6935 Clamp start and end in render service to ensure they're valid
Fixes #4314
2022-12-21 16:55:03 -08:00
Daniel Imms 8b1c5c4ffb Revert "Fix NPE in webgl renderer"
This reverts commit ea7571c2b9.
2022-12-21 16:51:45 -08:00
Daniel Imms d34bb5ed79 Merge pull request #4343 from Tyriar/bump_other_addons
Bump other addons for v5.1 release
2022-12-21 08:28:46 -08:00
Daniel Imms ace4c3663f Bump other addons for v5.1 release
Fixes #4336
2022-12-21 08:27:01 -08:00
Daniel Imms 75075ebb70 Merge pull request #4342 from Tyriar/4341
Fix link event listener leak
2022-12-20 15:15:22 -08:00
Daniel Imms 7b8ad8c0b4 Improve comment 2022-12-20 15:01:30 -08:00
Daniel Imms f28c21eb77 Fix link event listener leak
The bug here is that _clearCurrentLink was being called only on the
line(s) that changed, but _askForLink was then being triggered
regardless. This caused more onRenderedViewportChange listeners to be
registered and for the thread blocking to get exponentially worse.

Fixes #4341
2022-12-20 14:58:35 -08:00
Daniel Imms 3142460db5 Merge pull request #4335 from jerch/cleanup_buffer
Cleanup Buffer.ts
2022-12-19 17:01:26 -08:00
Daniel Imms b97b1b6093 Merge pull request #4337 from Tyriar/whitespace_bump
Make package.json changes to trigger release
2022-12-19 12:55:19 -08:00
Daniel Imms 303db85763 Make package.json changes to trigger release
Fixes #4336
2022-12-19 12:55:01 -08:00
Jörg Breitbart 49ec7199e9 remover iterator and stringIndexToBufferIndex from buffer 2022-12-19 19:50:18 +01:00
Daniel Imms 19c760a204 Merge pull request #4333 from xtermjs/revert-4196-ansiheadless
Revert "SerializeAsHTML(): Fallback to default ansi colors when running headless terminal"
2022-12-19 10:20:24 -08:00
Daniel Imms 3a81ef54ca Revert "SerializeAsHTML(): Fallback to default ansi colors when running headless terminal" 2022-12-19 10:10:59 -08:00
Daniel Imms 6cfdcb1b0d Merge pull request #4332 from Tyriar/r5_1_0
v5.1.0
2022-12-19 09:56:50 -08:00
Daniel Imms 30ef1fcaa5 v5.1.0 2022-12-19 09:56:20 -08:00
Daniel Imms 84b8121105 Merge pull request #4288 from jerch/fix_weblinks
Fix weblinks
2022-12-19 08:57:15 -08:00
Jörg Breitbart 218cfd256c Merge branch 'master' into fix_weblinks 2022-12-19 15:59:11 +01:00
Jörg Breitbart 1757f2733c explicit type for link test data 2022-12-19 15:49:10 +01:00
Jörg Breitbart 9411acbc73 move link tests to demo 2022-12-19 15:33:10 +01:00
Daniel Imms cb4102cdcb Merge pull request #4330 from Tyriar/2533
Add VT buttons through CSI P
2022-12-19 06:30:11 -08:00
Daniel Imms 1926c9bbbc Add sequence buttons through CSI P 2022-12-19 06:20:01 -08:00
Daniel Imms 06145c02b1 Add IL vt button
Part of #2533
2022-12-19 06:11:34 -08:00
Jörg Breitbart d65898ffed Merge branch 'master' into fix_weblinks 2022-12-19 14:19:43 +01:00
Daniel Imms b5e6066f8c Merge pull request #4327 from Tyriar/2603
Improve IAttributeData jsdoc
2022-12-18 08:51:41 -08:00
Daniel Imms 0981ca8796 Merge pull request #4328 from Tyriar/vscode166909
Remove ! to improve safety
2022-12-16 11:16:17 -08:00
Daniel Imms ffeaa2b976 Remove ! to improve safety
See microsoft/vscode#166909

This doesn't necessarily fix that, but it does remove the hacky ! which could
potentially let an error slip past TS.
2022-12-16 11:05:03 -08:00
Daniel Imms 167765f932 Improve IAttributeData jsdoc
Fixes #2603
2022-12-16 10:53:21 -08:00
Daniel Imms bcdbb61ed4 Merge pull request #4326 from Tyriar/166878
Fix NPE in webgl renderer
2022-12-16 10:51:52 -08:00
Daniel Imms ea7571c2b9 Fix NPE in webgl renderer
Theory is this can happen when a resize down happens as end will be too big

See microsoft/vscode#166878
2022-12-16 10:42:05 -08:00
Daniel Imms 880b726bcb Merge pull request #4325 from Tyriar/4056
Fix canvas renderer selection not re-rendering sometimes
2022-12-16 09:39:28 -08:00
Daniel Imms 902da2800b Merge pull request #4324 from Ry0taK/feature/secure-links-by-default
Add allowNonHttpProtocols option to ILinkHandler
2022-12-16 09:32:54 -08:00
Daniel Imms 2bd9c4e839 Fix canvas renderer selection not re-rendering sometimes
Fixes #4056
2022-12-16 09:28:02 -08:00
RyotaK b3d36478ca Properly handle errors from URL constructor 2022-12-16 17:01:00 +00:00
RyotaK b07c016fea Adjust the comment 2022-12-16 16:58:55 +00:00
Daniel Imms 0b5625581e Merge pull request #4284 from JasonXJ/a11y-2
Fix a11y issues #4269
2022-12-16 08:57:55 -08:00
RyotaK e4b473785d Make comment more clear 2022-12-16 16:54:27 +00:00
RyotaK cb8d93a1ef Implement protocol validation 2022-12-16 16:50:57 +00:00
RyotaK 8636d0f820 Add allowNonHttpProtocols to typings 2022-12-17 01:31:11 +09:00
Daniel Imms f79644bd4c Merge pull request #4298 from jerch/fix_4295
re-eval previous active link
2022-12-16 08:12:19 -08:00
Daniel Imms 917e31ee17 Merge pull request #4322 from Tyriar/4124
Explain object option edge case
2022-12-15 17:06:24 -08:00
Daniel Imms aba59e9386 Explain object option edge case
Fixes #4124
2022-12-15 16:41:31 -08:00
Daniel Imms 1c7c4eb62f Merge pull request #4315 from Tyriar/2757
Allow scroll bar interaction in demo in screenReaderMode
2022-12-15 07:58:45 -08:00
Daniel Imms fc41968131 Allow scroll bar interaction in demo in screenReaderMode
Fixes #2757
2022-12-15 07:50:45 -08:00
Daniel Imms 04177de95b Merge pull request #4313 from Tyriar/4258
Support slash and triangle custom glyphs
2022-12-14 13:36:35 -08:00
Daniel Imms c9c07aeabd Support slash and triangle custom glyphs
This also fixes an issue with padding making glyphs regress

Fixes #4258
2022-12-14 13:15:05 -08:00
Daniel Imms 0cc759e0b1 Merge pull request #4312 from xtermjs/Tyriar-patch-1
Point new issue questions at GH discussions
2022-12-14 12:30:33 -08:00
Daniel Imms 852255ea03 Point new issue questions at GH discussions 2022-12-14 12:10:48 -08:00
Daniel Imms 88f459583e Merge pull request #4311 from Tyriar/vtbuttons
Add VT arrow buttons
2022-12-14 09:37:38 -08:00
Daniel Imms fd8a0690c9 Add VT arrow buttons
Part of #2064
2022-12-14 09:28:25 -08:00
Daniel Imms 6e2744a299 Merge pull request #4310 from Tyriar/4309
Improve explanation of danger with the linkHandler API
2022-12-14 08:21:55 -08:00
Daniel Imms 15e3b1ac12 Improve explanation of danger with the linkHandler API
Fixes #4309
2022-12-14 08:10:52 -08:00
Daniel Imms 5a5ed6d767 Merge pull request #4306 from Tyriar/4161
Add combo entries to sgr test for testing multiple sgr
2022-12-11 13:42:47 -08:00
Daniel Imms 74ea0c23ca Add combo entries to sgr test for testing multiple sgr 2022-12-11 13:11:16 -08:00
Daniel Imms 6d0a287ed9 Merge pull request #4305 from Tyriar/4221
Fix line height and letter spacing on canvas renderer
2022-12-11 11:54:57 -08:00
Daniel Imms 83ff4e97c3 Merge pull request #4304 from Tyriar/4259
Simplify options API
2022-12-11 11:52:08 -08:00
Daniel Imms 7b28dc4d59 Fix line height and letter spacing on canvas renderer
Fixes #4221
2022-12-11 11:47:09 -08:00
Daniel Imms 02d3ef599a Fix internal usages that needed Required<ITerminalOptions> 2022-12-11 11:42:14 -08:00
Daniel Imms c118f04187 Sync xterm-headless.d.ts 2022-12-11 11:36:32 -08:00
Daniel Imms 2bd3869547 Use a monospace font in example 2022-12-11 11:33:23 -08:00
Daniel Imms 69379ba59b Simplify options API
Fixes #4259
2022-12-11 11:33:03 -08:00
Daniel Imms 5ed05295ba Merge pull request #4301 from Tyriar/cleanup_coreservice
Avoid passing circular callback into CoreService ctor
2022-12-11 07:44:46 -08:00
Daniel Imms 4ee4a0f988 Merge branch 'master' into cleanup_coreservice 2022-12-11 07:36:01 -08:00
Daniel Imms d6b8a21219 Avoid passing circular callback into CoreService ctor 2022-12-11 07:35:11 -08:00
Daniel Imms 2e2d275d6c Merge pull request #4289 from JasonXJ/scroll-options
Add option scrollOnUserInput
2022-12-11 07:31:31 -08:00
Daniel Imms 33bba35be7 Merge pull request #4291 from jerch/fix_4223
fix demo in epiphany
2022-12-11 07:17:08 -08:00
jerch c7660fb49c Merge pull request #4299 from xtermjs/dependabot/npm_and_yarn/express-4.17.3
Bump express from 4.17.1 to 4.17.3
2022-12-10 20:50:46 +01:00
dependabot[bot] 6d527c42b0 Bump express from 4.17.1 to 4.17.3
Bumps [express](https://github.com/expressjs/express) from 4.17.1 to 4.17.3.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.17.1...4.17.3)

---
updated-dependencies:
- dependency-name: express
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-10 17:06:32 +00:00
Jörg Breitbart 440da4a5ff reeval previous active link, fixes #4295 2022-12-09 16:37:56 +01:00
Jörg Breitbart e66fd50383 cleanup & more comments 2022-12-09 14:37:31 +01:00
Jörg Breitbart f69f9decca allow ~ within urls 2022-12-08 17:50:55 +01:00
Jörg Breitbart e70138b63c revert temp changes in linkifier2 2022-12-08 17:46:45 +01:00
Jörg Breitbart becec91e98 better regexp, fix offset issue in #4294 2022-12-08 17:35:45 +01:00
Jörg Breitbart 03dc1908a5 Merge branch 'master' into fix_weblinks 2022-12-08 15:19:08 +01:00
Jörg Breitbart d2298d84a0 move safari check to WebglAddon ctor 2022-12-07 23:56:43 +01:00
Jörg Breitbart 0b56b56c06 skipping renderer on NUL and SP 2022-12-07 22:55:45 +01:00
Daniel Imms 6d46b7b86f Merge pull request #4115 from jerch/buffer_optimizations
some buffer handling optimizations
2022-12-07 13:43:40 -08:00
Jörg Breitbart b829ad8386 Merge branch 'master' into fix_4223 2022-12-07 22:28:00 +01:00
Jörg Breitbart 9bc65c36a6 Merge branch 'master' into buffer_optimizations 2022-12-07 19:45:14 +01:00
Jörg Breitbart a562f33d53 remove nonsense exp runtime of rescans 2022-12-07 19:43:56 +01:00
Daniel Imms fe30cd6ed8 Merge pull request #4290 from JasonXJ/alt-space
Send corresponding escape code for alt+space and ctrl+alt+space
2022-12-07 06:34:23 -08:00
Jason Lin 9e84895141 polish 2022-12-07 10:12:43 +11:00
Daniel Imms 497df06f3f Merge pull request #4293 from Tyriar/4292
Log a warning if task queues exceed deadline by 20ms
2022-12-06 14:20:37 -08:00
Daniel Imms 650cda31fd Tweak logging 2022-12-06 14:10:40 -08:00
Daniel Imms dec3791f4c Log a warning if task queues exceed deadline by 20ms
Fixes #4292
2022-12-06 14:04:10 -08:00
Jörg Breitbart e46a9e12d0 fix demo in epiphany 2022-12-06 13:40:25 +01:00
Jason Lin 7f64fa9ddd Send corresponding escape code for alt+space and ctrl+alt+space
The previous behavior is that this combinations do nothing.
2022-12-06 15:57:19 +11:00
Jason Lin 73d20c1d3b Add option scrollOnKeypress
Note that we also change the existing behavior a bit:
CoreService.triggerDataEvent() will not scroll to the bottom unless
`wasUserInput` is also true. This actually seems to be the intended
behavior according to the doc just above
ICoreService.triggerDataEvent().
2022-12-06 14:29:18 +11:00
Jörg Breitbart fd44d76262 api tests for uri and ranges 2022-12-05 22:49:25 +01:00
Jörg Breitbart 309ba47483 change batch size to 500 2022-12-05 20:03:25 +01:00
Jörg Breitbart 4084dc3949 remove _forceMemoryCleanup 2022-12-05 19:10:55 +01:00
Jörg Breitbart 42bacc3283 disabled faulty code in linkifier2 2022-12-05 18:30:25 +01:00
Jörg Breitbart 46bd97247d disabled faulty code in linkifier2 2022-12-05 18:28:42 +01:00
Jörg Breitbart 3bf72cdefc DOM renderer fix with much better performance 2022-12-05 15:50:29 +01:00
Jörg Breitbart de23616589 better line expansion 2022-12-05 13:45:47 +01:00
Jörg Breitbart 39c2e57ad3 hack around DOM renderer issue, fix api tests 2022-12-04 22:26:29 +01:00
Jörg Breitbart d19361ac85 initial rewrite of addon mechs 2022-12-04 19:53:36 +01:00
Jörg Breitbart 1f5159febe use IdleTaskQueue & Date.now; some testcases 2022-12-03 16:49:26 +01:00
Jörg Breitbart 3a2ae96960 Merge branch 'master' into buffer_optimizations 2022-12-03 11:24:51 +01:00
Daniel Imms ad1f2ac2a8 Merge pull request #4287 from xtermjs/dependabot/npm_and_yarn/minimatch-3.1.2
Bump minimatch from 3.0.4 to 3.1.2
2022-12-02 09:54:13 -08:00
dependabot[bot] b972e7f5ad Bump minimatch from 3.0.4 to 3.1.2
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2.
- [Release notes](https://github.com/isaacs/minimatch/releases)
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-02 17:45:59 +00:00
Daniel Imms 503fe99b0a Merge pull request #4286 from Tyriar/tyriar/mocha
Update mocha
2022-12-02 09:45:34 -08:00
Daniel Imms 953cf005c0 Update mocha
This resolves a security alert
2022-12-02 09:35:36 -08:00
Jason Lin d177acccee Fix a11y issues #4269 2022-12-02 18:35:09 +11:00
Daniel Imms 4451369c66 Merge pull request #4280 from Tyriar/4278
Add custom keymap example to attachCustomKeyEventHandler docs
2022-11-30 07:15:12 -08:00
Daniel Imms 6b09c1b576 Add custom keymap example to attachCustomKeyEventHandler docs
Fixes #4278
2022-11-30 07:06:28 -08:00
Daniel Imms 71eb514bd0 Merge pull request #4276 from bugwheels94/bugwheels94-add-dependent-to-readme
Added application that uses xterm.js
2022-11-28 13:04:48 -08:00
Ankit cd552500fc Update README.md 2022-11-29 02:23:56 +05:30
Daniel Imms ed1773c103 Merge pull request #4274 from JasonXJ/cap-atlas-pages
Cap webgl maxAtlasPages to 32
2022-11-26 07:24:47 -08:00
Jason Lin b7c119a071 Cap webgl maxAtlasPages to 32 2022-11-24 17:51:58 +11:00
Daniel Imms 452241ca92 Merge pull request #4271 from jeanp413/fix-vscode-166158
Fixes disappearance of characters in webgl renderer
2022-11-23 09:36:15 -08:00
Daniel Imms 1880399849 dirtyId -> version 2022-11-23 08:51:06 -08:00
Jean Pierre d4ee0309d9 Fixes https://github.com/microsoft/vscode/issues/166158 2022-11-22 00:07:23 -05:00
Daniel Imms a351f5758a Merge pull request #4255 from Tyriar/safari16
Enable the webgl renderer on safari 16 and above
2022-11-08 12:30:23 -08:00
Daniel Imms 3597d43788 Enable the webgl renderer on safari 16 and above
This does not fix the issue where webkit browsers cannot use it, I looked
at webkit versions in safari and was a bit confused. The wikipedia page
for safari shows v15 having webkit v612+ but I have safari v16 and it's
webkit v605, so trying to detect it based on webkit version is a risky
change.

Note that webgl2 shipped in safari 15, but it didn't work in webgl until
safari 16.

Fixes #3357
Related #3575
Related microsoft/vscode#165416
2022-11-08 12:23:20 -08:00
Daniel Imms f381783bd5 Merge pull request #4196 from silamon/ansiheadless
SerializeAsHTML(): Fallback to default ansi colors when running headless terminal
2022-11-04 08:15:59 -07:00
Daniel Imms 6969a94fe4 Merge branch 'master' into ansiheadless 2022-11-04 08:08:43 -07:00
Daniel Imms 2377472eca Merge pull request #4216 from JasonXJ/fix-csi-0-m
'CSI 0 m' should clear the extended attribute
2022-11-03 19:58:18 -07:00
Daniel Imms 0edabed595 Merge pull request #4240 from Tyriar/4222_shims
Remove promise and fetch shims and use await for fetch
2022-11-03 19:56:25 -07:00
Daniel Imms 7d98375ce4 Merge pull request #4254 from Tyriar/clear_green
Clear custom glyph test button sgr
2022-11-03 06:56:23 -07:00
Daniel Imms 862e83d7be Merge pull request #4252 from Tyriar/4243_merge
Continually merge texture atlas pages
2022-11-03 06:48:55 -07:00
Daniel Imms 7cf9b68e2c Clear custom glyph test button sgr 2022-11-03 06:48:33 -07:00
Daniel Imms 97a0df260b Remove unused event 2022-11-03 05:44:50 -07:00
Daniel Imms c66dc82286 Merge pull request #4251 from Tyriar/4250
Add 1024 character limit to web links addon
2022-11-02 06:56:22 -07:00
Daniel Imms 6c29864847 Add 1024 character limit to web links addon
Fixes #4250
2022-11-02 06:45:25 -07:00
Daniel Imms d82da195f3 Remove atlas pages from demo when they're merged 2022-11-02 06:02:53 -07:00
Daniel Imms abfe391d91 Add CJK with random SGR test button
This is good as a stress test for the renderer
2022-11-02 05:46:40 -07:00
Daniel Imms c22e684904 Fix exception when last 4 smallest pages are merged 2022-11-02 05:40:13 -07:00
Daniel Imms 6406fd79c1 Enforce a maximum page merge amount 2022-11-02 05:28:47 -07:00
Daniel Imms 0271fdf49d Merge 4 largest pages 2022-11-02 04:49:02 -07:00
Daniel Imms 805b41b80f Keep merging default texture size pages 2022-11-01 10:26:48 -07:00
Daniel Imms c9bacc80d7 Merge when max atlas pages is reached 2022-11-01 10:08:00 -07:00
Daniel Imms 54ee698ba6 Basic merging working
Part of #4243
2022-11-01 09:07:38 -07:00
Daniel Imms 295e3c7f0c Merge pull request #4249 from Tyriar/4241_fill
Remove fill polyfill
2022-10-31 12:34:17 -07:00
Daniel Imms d219da480c Remove fill polyfill
Fixes #4241
2022-10-31 12:24:59 -07:00
Daniel Imms 02fa38c334 Merge pull request #4244 from Tyriar/multi_page_atlas
Support multiple texture atlas pages
2022-10-30 18:30:11 -07:00
Daniel Imms 4d05905736 Polish 2022-10-30 18:11:24 -07:00
Jason Lin 81a3c80bc4 polish 2022-10-31 11:07:42 +11:00
Jason Lin f6fabebf8d polish 2022-10-31 11:07:42 +11:00
Jason Lin b735e7f1c4 polish 2022-10-31 11:07:42 +11:00
Jason Lin 839271096e Don't reset the url 2022-10-31 11:07:42 +11:00
Jason Lin 6a087e9896 'CSI 0 m' should clear the extended attribute
This change also removes `_currentLinkId` from `InputHandler` and uses
`_curAttrData.extended.urlId` directly so that we don't need to worry
about keep them in sync any more.
2022-10-31 11:07:42 +11:00
Daniel Imms dda3f5f545 Properly dispose of atlas event 2022-10-30 15:00:27 -07:00
Daniel Imms 04041d030f Generate bitmaps for all atlas pages 2022-10-30 14:56:27 -07:00
Daniel Imms 095ceda639 Show canvas atlas pages in demo 2022-10-30 14:53:01 -07:00
Daniel Imms 0aa9fc5bb8 Add border around atlas pages 2022-10-30 12:00:55 -07:00
Daniel Imms e092ca70e1 Simplify page reuse/creation 2022-10-30 10:33:38 -07:00
Daniel Imms 0592837620 Fill pages to capacity 2022-10-30 10:21:17 -07:00
Daniel Imms fc03a1698b Share page creation code 2022-10-30 10:16:48 -07:00
Daniel Imms c9ef4a8eb9 Increase texture size as it nears the maximum texture unit
#4243 would be a better, but more complicated, solution to this
2022-10-30 09:20:17 -07:00
Daniel Imms 846573bb01 Support up to maximum texture units (eg. 16 on my machine) 2022-10-30 03:16:29 -07:00
Daniel Imms 0f70055d76 Add CJK test button 2022-10-30 03:04:58 -07:00
Daniel Imms 3f2ed42e73 Fix glyphs getting clipped on bottom row 2022-10-29 20:57:42 -07:00
Daniel Imms 2a3c2d9520 Make better use of atlas space 2022-10-29 20:33:27 -07:00
Daniel Imms 5e95475d6b Use current row when filling in old atlas pages 2022-10-29 19:50:44 -07:00
Daniel Imms 57a65e0858 Continute filling atlas page after new pages are added 2022-10-29 19:44:12 -07:00
Daniel Imms bfa9dd5060 Clean up 2022-10-29 18:09:18 -07:00
Daniel Imms 04f79aa7b4 Share page texture binding code 2022-10-29 14:33:50 -07:00
Daniel Imms 86ccd709bf Avoid setting uniforms every frame 2022-10-29 14:26:51 -07:00
Daniel Imms c257a7247e Only upload textures that change 2022-10-29 13:27:33 -07:00
Daniel Imms 0a49a5dde1 Make texture atlas presentation nicer for multiple pages 2022-10-29 13:01:41 -07:00
Daniel Imms c6e3cc007c Stop atlas flickering on demo 2022-10-29 12:49:11 -07:00
Daniel Imms 713baeb64d Base atlas page size on device pixel ratio 2022-10-29 12:45:20 -07:00
Daniel Imms efba2c9040 Get pages working, add to demo 2022-10-28 19:30:42 -07:00
Daniel Imms 67ef455515 Get a second page kind of working 2022-10-28 19:20:26 -07:00
Daniel Imms 975d52fab9 Remove deprecated prop 2022-10-28 19:12:01 -07:00
Daniel Imms 6b37187b4d Create AtlasPage 2022-10-28 19:08:33 -07:00
Daniel Imms 84ab6a49c4 Bring texturePage partially to atlas 2022-10-28 18:56:49 -07:00
Daniel Imms 4ebc75aae0 Merge remote-tracking branch 'upstream/master' into multi_page_atlas 2022-10-28 18:42:46 -07:00
Daniel Imms 54335ef54a Create texture page attribute 2022-10-28 18:16:55 -07:00
Daniel Imms 43d8db77f9 Merge pull request #4239 from Tyriar/4224_mcr_canvas_ydisp
Correctly offset mcr check on canvas renderer
2022-10-28 14:10:25 -07:00
Daniel Imms 5a4a46f130 Remove promise and fetch shims and use await for fetch
Fixes #4222
2022-10-28 14:09:39 -07:00
Daniel Imms b0672aac09 Correctly offset mcr check on canvas renderer
Fixes #4224
2022-10-28 14:02:58 -07:00
Daniel Imms fcf378b172 Merge pull request #4237 from Tyriar/italic_bounds
Fix clipping of italic emoji
2022-10-28 09:12:07 -07:00
Daniel Imms 25551a4b98 Merge pull request #4236 from Tyriar/console_image
Add console.image helper
2022-10-28 09:04:37 -07:00
Daniel Imms 67b3faef01 Use bbox padding and width consistently 2022-10-28 09:01:22 -07:00
Daniel Imms 8b545a3fe4 Fix bbox right scan start offset
This causes particularly wide glyphs like italic emoji to get clipped
even if they're within the allowed padding.

Fixes #4229
2022-10-28 08:57:41 -07:00
Daniel Imms 4f4ce0146d Add console.image helper
This is useful for printing canvases that aren't attached to the DOM
2022-10-28 08:54:57 -07:00
Daniel Imms e7b347a2ef Merge pull request #4235 from Tyriar/opt_server_buffer
Avoid GC pressure from server data buffer
2022-10-27 22:41:07 -07:00
Daniel Imms 7908b7cf62 Avoid GC pressure from server data buffer 2022-10-27 22:30:46 -07:00
Daniel Imms 392f8e9c98 Merge pull request #4234 from Tyriar/slow_isFocused
Cache ICoreBrowserService.isFocused per task
2022-10-27 17:00:18 -07:00
Daniel Imms b5cd4f1492 Cache ICoreBrowserService.isFocused per task
This is called for every selected cell when rendering which ends up
consuming a bunch of CPU due to the DOM calls.
2022-10-27 16:50:45 -07:00
Daniel Imms 4edfc12cbf Merge pull request #4233 from Tyriar/4113_resize
Fix resize in demo
2022-10-27 14:03:17 -07:00
Daniel Imms 19c1ee56d3 Fix resize in demo
A double resize could happen due to the mismatch in canvas vs device
pixel widths

Fixes #4113
2022-10-27 13:55:24 -07:00
Daniel Imms a4f8b07a7f Merge pull request #4226 from Tyriar/dropped_frames
Fix dropped frame issue
2022-10-21 13:10:30 -07:00
Daniel Imms 8d4d5bd54b Fix dropped frame issue
This ensures decorations are refreshed in the same frame when called from
an animation frame callback

Fixes #4225
2022-10-21 13:02:24 -07:00
Daniel Imms d5710afcb3 Merge pull request #4219 from Tyriar/4218_safari_bitmap
Disable canvas ImageBitmap optimization on Safari
2022-10-19 10:58:43 -07:00
Daniel Imms 8bd8195e30 Disable canvas ImageBitmap optimization on Safari
Fixes #4218
2022-10-19 10:03:01 -07:00
Daniel Imms ffe066dfef Merge pull request #4217 from Nokel81/fix-dom-renderering-jsdom-v2
Change clearing innerText to using replaceChildren to fix testing using jsdom
2022-10-19 07:21:30 -07:00
Sebastian Malton 11e90591f9 Change clearing innerText to using replaceChildren to fix testing using jsdom
Signed-off-by: Sebastian Malton <sebastian@malton.name>
2022-10-19 10:09:12 -04:00
Megan Rogge 0b84d17b18 revert decoration dispose changes (#4215) 2022-10-18 21:32:22 +00:00
Daniel Imms 376f0d9972 Merge pull request #4211 from hackermondev/patch-1
replit branding change
2022-10-17 07:45:06 -07:00
hackermon 478535d236 replit branding change 2022-10-17 09:49:54 -04:00
Daniel Imms 3f12d2925a Merge pull request #4208 from Tyriar/todo_attach_socket
Warn/throw on unexpected attach addon socket state
2022-10-17 03:48:31 -07:00
Daniel Imms 8e65ee8832 Merge pull request #4210 from Tyriar/todo_type_tabs
Fix a bunch of TODOs
2022-10-16 07:15:19 -07:00
Daniel Imms a7f0c3868c Merge pull request #4209 from Tyriar/todo_onData_event_test
Re-enable onData event test
2022-10-16 07:08:59 -07:00
Daniel Imms 96486c414b Acquire char size service better in webgl renderer 2022-10-16 07:04:34 -07:00
Daniel Imms b560d85553 Remove unneeded params as link provider is stable 2022-10-16 07:04:24 -07:00
Daniel Imms 7796b4101a Type Buffer.tabs 2022-10-16 06:58:20 -07:00
Daniel Imms a982484392 Re-enable onData event test 2022-10-16 06:55:04 -07:00
Daniel Imms e15c6b9f3e Warn/throw on unexpected attach addon socket state 2022-10-16 06:49:27 -07:00
Daniel Imms 807183bba6 Merge pull request #4207 from Tyriar/3925_dimensions
Improve names of IRenderDimensions interface members
2022-10-16 06:41:37 -07:00
Daniel Imms dc19628014 Fix width -> height 2022-10-16 06:34:12 -07:00
Daniel Imms ca68a5c59a Fix typo in test 2022-10-15 20:13:24 -07:00
Daniel Imms 22e8a2f8e7 Remove old dimension properties 2022-10-15 18:07:05 -07:00
Daniel Imms 171ba446bd Update other files 2022-10-15 15:32:02 -07:00
Daniel Imms 89e0b87eac Move to device/css in domrenderer 2022-10-15 14:22:04 -07:00
Daniel Imms 2c9e711cae Move all other refs in webgl over 2022-10-15 13:55:36 -07:00
Daniel Imms b0f1c38fd2 Move all other refs in canvas over 2022-10-15 13:44:52 -07:00
Daniel Imms b393008b54 Use new render dimensions in canvasrenderer 2022-10-15 13:19:04 -07:00
Daniel Imms 65f41a151e Use new render dimensions in webglrenderer 2022-10-15 13:15:12 -07:00
Daniel Imms ddc4e75583 Add new dimensions properties, helper creation function
Part of #3925
2022-10-15 10:31:43 -07:00
Daniel Imms 869ce3aa35 Merge pull request #4194 from silamon/fixmissingline
Capture data between terminal creation and connected to terminal in demo
2022-10-15 10:17:51 -07:00
Daniel Imms 16ea621a5f Merge pull request #4206 from Tyriar/eslint-jsdoc
Add eslint-plugin-jsdoc with minimal comment rules
2022-10-15 08:55:07 -07:00
Daniel Imms 5204374494 Add no-multi-asterisks rule 2022-10-15 08:43:06 -07:00
Daniel Imms 9f1a71485c Fix invalid jsdoc tags 2022-10-15 08:41:49 -07:00
Daniel Imms 49e0480f78 Add rule jsdoc/check-alignment 2022-10-15 08:39:32 -07:00
Daniel Imms 114924f3fa Add eslint-plugin-jsdoc and enable check-param-names 2022-10-15 08:38:14 -07:00
Daniel Imms aeba140c75 Merge pull request #4204 from silamon/proposedApiCheck
Remove check proposed api checks
2022-10-15 08:04:12 -07:00
Daniel Imms aea7680f76 Merge pull request #4205 from Tyriar/triangle_strip
Draw quads with triangle strip
2022-10-15 08:02:07 -07:00
Daniel Imms fb4a4e6127 Draw quads with triangle strip
This reduces the number of needed vertices from 6 to 4, it may not have a
noticeable performance impact but it's less data and simpler
2022-10-15 07:54:31 -07:00
Simon Lamon cc82f605fe delete clean up 2022-10-15 10:47:59 +00:00
Simon Lamon a055948937 unsentOutput instead of logs and dispose temporary listener 2022-10-15 10:45:23 +00:00
Simon Lamon 8246440795 Merge branch 'master' into fixmissingline 2022-10-15 10:11:40 +00:00
Simon Lamon 073aa6d439 Fix test "Proposed API check" 2022-10-15 10:25:39 +02:00
Simon Lamon 6bbe5a82a8 Remove check proposed api checks 2022-10-15 07:28:21 +00:00
Daniel Imms 1e6dfec4ae Merge pull request #4201 from Tyriar/4197_2
Remove clipImageData completely
2022-10-13 06:34:37 -07:00
Daniel Imms 5285b9ac45 Merge branch 'master' into 4197_2 2022-10-13 06:25:59 -07:00
Daniel Imms 50d1834871 Remove clipImageData completely
putImageData works a little differently to drawImage which confused me
for a bit, you need to offset the destination as putImageData draws the
'dirty' parts of the texture using the same source image dimensions

Fixes #4197
2022-10-13 06:25:13 -07:00
Daniel Imms b1f785879b Merge pull request #4200 from Tyriar/4197
Optimize _clipImageData
2022-10-12 10:24:28 -07:00
Daniel Imms 28fcffe46c Optimize _clipImageData
This makes clip image data around twice as fast, it's a little tricky to
measure it though. Changes:

- Work on Uint32Array instead of Uint8 so it's 1 assignment per pixel
  instead of 1 per channel.
- Perform the clipping in-place using the original image data to avoid
  allocation a new buffer.

Fixes #4197
2022-10-12 10:16:26 -07:00
Jörg Breitbart 14b99dca76 Merge branch 'master' into buffer_optimizations 2022-10-09 21:41:06 +02:00
Daniel Imms 1b13127f0b Merge pull request #4195 from Tyriar/4190
Create onSpecificOptionChange and onMultipleOptionChange helpers
2022-10-09 11:04:59 -07:00
Daniel Imms a37ea14d10 Use options service in renderers 2022-10-09 10:57:49 -07:00
Simon Lamon f8a5d3a91e Fallback to default_ansi_colors when running headless terminal 2022-10-09 17:16:06 +00:00
Daniel Imms 8d7137417b Split renderer safe vs unsafe api on activate 2022-10-09 10:12:19 -07:00
Daniel Imms e47296c1b9 Use options service directly in renderers 2022-10-09 09:28:18 -07:00
Daniel Imms 96b26b2e4a Merge pull request #4193 from silamon/fixdemoagain
Fix demo again
2022-10-09 09:23:07 -07:00
Daniel Imms 8ac88bc6bf Move Terminal option updates into owning components 2022-10-09 09:07:01 -07:00
Daniel Imms 7eead44048 Add onMultipleOptionChange 2022-10-09 08:14:56 -07:00
Daniel Imms 49a0904857 Move onSpecificOptionChange adoption 2022-10-09 08:07:43 -07:00
Simon Lamon a4c62792df revert formatting changes 2022-10-09 15:03:54 +00:00
Simon Lamon 1c345bef48 Handle onWillFire if it fails, so it loads dom renderer 2022-10-09 15:01:15 +00:00
Daniel Imms 415100e8b8 Adopt onSpecificOptionChange 2022-10-09 07:46:10 -07:00
Simon Lamon 627410a537 Add broken demo to investigate 2022-10-09 14:26:08 +00:00
Daniel Imms cad9c4164c onOptionChange/onSpecificOptionChange tests 2022-10-09 07:16:56 -07:00
Daniel Imms a87b6c3f2e Implement IOptionsService.onSpecificOptionChange
Part of #4190
2022-10-09 07:09:35 -07:00
Daniel Imms a2a855d747 Merge pull request #4192 from silamon/ubuntudeprecation
Switch to Ubuntu 20.04
2022-10-09 06:25:11 -07:00
Simon Lamon fb91b01217 Adds logs again in server.js 2022-10-09 12:16:26 +00:00
Simon Lamon bd661fdb07 Fix demo again 2022-10-09 11:29:33 +00:00
Simon Lamon f983b3cac1 Ubuntu 18.04 deprecation 2022-10-09 10:15:13 +00:00
Daniel Imms 0ba12301a6 Merge pull request #4188 from Tyriar/4184
Create theme service
2022-10-08 15:51:44 -07:00
Daniel Imms 97e084aa50 Remove document from test 2022-10-08 15:44:21 -07:00
Daniel Imms e0fb3b4d19 Remove ColorManager 2022-10-08 15:23:29 -07:00
Daniel Imms a1050d1a11 Merge ColorManager into ThemeService 2022-10-08 15:20:15 -07:00
Daniel Imms 7f9e3b1225 Remove resolved todo 2022-10-08 14:09:00 -07:00
Daniel Imms 48f15cb543 Remove remaining setColors methods 2022-10-08 14:07:28 -07:00
Daniel Imms 6e35bcd767 Use theme service in more places 2022-10-08 13:55:23 -07:00
Daniel Imms 80e474c6ad Viewport listen to theme service 2022-10-08 13:31:31 -07:00
Daniel Imms 4dee0c5d4b Build out theme service more 2022-10-08 12:15:21 -07:00
Daniel Imms a22ad76bd8 Merge pull request #4189 from Tyriar/ensure_canvas_changed
Ensure stale bitmap is not used when drawing new characters
2022-10-08 12:04:27 -07:00
Daniel Imms 4f5154fd21 Ensure stale bitmap is not used when drawing new characters
This would cause a problem where the first time a glyph is used it would
not be visible because it was using the old invalidated bitmap.
2022-10-08 11:56:29 -07:00
Daniel Imms cc6def57e4 Use readonly color set in canvas addon 2022-10-08 11:46:41 -07:00
Daniel Imms c4750eaf77 Create basic theme service
Part of #4184
2022-10-08 11:43:19 -07:00
Daniel Imms 8db2244f5d Merge pull request #4187 from Tyriar/4183_on_naming
Lint rule for on=event emitter and rename all methods with on prefix to handle
2022-10-08 11:14:15 -07:00
Daniel Imms 5d8e4a0af4 Add lint rule to ensure _on prefix is only for event emitters 2022-10-08 11:05:52 -07:00
Daniel Imms eecf52e949 Add lint rule to ensure on prefix is only for event emitters
Fixes #4183
2022-10-08 10:57:31 -07:00
Daniel Imms d8c31d7ad0 Merge pull request #4185 from Tyriar/4167_register_emitter
Fix a bunch of memory retention problems
2022-10-08 10:35:04 -07:00
Daniel Imms d8cf3af555 Fix Marker.dispose 2022-10-08 10:23:19 -07:00
Daniel Imms d3976c05f1 Fix marker dispose unit tests 2022-10-08 09:27:58 -07:00
Daniel Imms 94c3d11239 Fix compile 2022-10-08 09:11:28 -07:00
Daniel Imms 00f6d3a88c Register all event emitters, make some services disposable 2022-10-08 09:00:14 -07:00
Daniel Imms f61d8c91a3 Properly register disposables in SearchAddon 2022-10-08 08:30:39 -07:00
Daniel Imms ac659cb412 Prevent overriding of Disposable.dispose
A common mistake made was overriding dispose and not calling super.dispose,
so a TS trick is now used to move it to a readonly property instead of a
member method which prevents it being overridden.
2022-10-08 08:27:10 -07:00
Daniel Imms 34475f24c3 Release canvas texture atlas 2022-10-08 08:11:28 -07:00
Daniel Imms 8116e035b4 Register webgl renderers 2022-10-08 08:06:43 -07:00
Daniel Imms 56b37a73ac Free WebglAddon 2022-10-08 07:37:55 -07:00
Daniel Imms fc8043a25a Ensure webgl texture atlas is released on dispose 2022-10-08 07:32:51 -07:00
Daniel Imms f88c0e7480 Polish emitter registering in canvas renderer
Part of #4167
2022-10-08 07:24:35 -07:00
Daniel Imms d948b31304 Merge pull request #4181 from Tyriar/clear_race
Fix race condition that could when clearing atlas on 2+ terminals
2022-10-07 16:34:27 -07:00
Daniel Imms 9a4d5dbe26 Merge pull request #4182 from Tyriar/delete_canvas_atlas
Remove the canvas char atlas
2022-10-07 16:32:51 -07:00
Daniel Imms 8e7093f03b Remove the canvas char atlas
This was unused and removing from cache was broken apparently
2022-10-07 16:20:01 -07:00
Daniel ImmsandMegan Rogge c4eef2f470 Fix race condition that could when clearing atlas on 2+ terminals
This is the proper fix for microsoft/vscode#162996, after a bunch of
investigation we found that this is causing the problem:

VS Code clears the texture atlas on OS resume to fix an issue where the texture
could be corrupted. This calls clearTextureAtlas twice which should be fine, but
it's not because inside WebglRenderer.clear we're calling updateModel which is
meant to only ever be called in an animation frame. This calls potentially both
renderers to have stale views of the texture atlas for those rows and our perf
caching of cells (the main purpose of the model) doesn't know it's stale.

Co-Authored-By: Megan Rogge <merogge@microsoft.com>
2022-10-07 16:17:11 -07:00
Daniel Imms 5fcb084089 Merge pull request #4180 from Tyriar/clear_atlas
Fix clearTextureAtlas call as implemented on IRenderer
2022-10-07 14:31:28 -07:00
Daniel Imms e89c507d06 Fix clearTextureAtlas call as implemented on IRenderer
This is the root cause of microsoft/vscode#162996
2022-10-07 14:22:51 -07:00
Daniel Imms eb3de3ebd5 Merge pull request #4151 from Tyriar/renderer_before_open
Allow renderer to be set before Terminal.open is called
2022-10-07 10:17:54 -07:00
Daniel Imms ffa4d921bb Merge remote-tracking branch 'upstream/master' into renderer_before_open 2022-10-07 10:10:24 -07:00
Daniel Imms a060c64a08 Merge pull request #4179 from Tyriar/4178
Split Utf8ToUtf32 decoder unit tests up
2022-10-06 10:38:03 -07:00
Daniel Imms 0d5a526e86 Split Utf8ToUtf32 decoder unit tests up
Fixes #4178
2022-10-06 10:28:54 -07:00
Daniel Imms 3ec08948ec Merge pull request #4177 from Tyriar/tyriar/revert_events
Revert "Merge pull request #4166 from Tyriar/event_with_emitter"
2022-10-06 09:59:50 -07:00
Daniel Imms 4d97729010 Revert "Merge pull request #4166 from Tyriar/event_with_emitter"
This reverts commit d22f7c9edd, reversing
changes made to 1f8e6f0952.
2022-10-06 09:48:54 -07:00
Daniel Imms 5f1651ffbc Merge pull request #4176 from xtermjs/Tyriar-patch-1
Bump copyright year
2022-10-06 09:15:02 -07:00
Daniel Imms ab0082b73a Bump copyright year 2022-10-06 09:14:50 -07:00
Daniel Imms aa55d1bde6 Merge pull request #4170 from Tyriar/shared_cache
Share texture atlas cache code between webgl and canvas renderers
2022-10-05 10:19:17 -07:00
Daniel Imms e0324e32a8 Bring back ImageBitmap optimization in canvas renderer
It's more simple than it used to be, it will clear and debounce all
glyphs when any change is made.
2022-10-04 15:24:10 -07:00
Daniel Imms 3b260a2193 Fix resolved todo 2022-10-04 14:46:00 -07:00
Daniel Imms 622acaaad7 Remove old selection start/end values 2022-10-04 14:45:19 -07:00
Daniel Imms 0d7e18d236 Add CellColorResolver to share load color code 2022-10-04 14:41:27 -07:00
Daniel Imms 83c09c34db Share selection render model code 2022-10-04 14:23:09 -07:00
Daniel Imms 03e3105c55 Fix fg/bg overrides in canvas 2022-10-04 14:10:27 -07:00
Daniel Imms c5d015c908 Merge branch 'master' into shared_cache 2022-10-04 13:54:14 -07:00
Daniel Imms e1e8f28d97 Merge pull request #4118 from coderaiser/feature/lint-by-putout
Lint using 🐊Putout: part 7
2022-10-04 10:45:24 -07:00
Daniel Imms f6a61acc86 Merge branch 'master' into feature/lint-by-putout 2022-10-04 09:52:10 -07:00
Daniel Imms 6316eee672 Comment out debug log 2022-10-02 21:32:24 -07:00
Daniel Imms 9259ec258f Fix combined chars in canvas 2022-10-02 14:27:43 -07:00
Daniel Imms c741c13409 Add ps to output string 2022-10-02 14:25:08 -07:00
Daniel Imms f2ff3a6a7a Add sgr attr test 2022-10-02 14:21:06 -07:00
Daniel Imms dbef850039 Hook up attributes to new texture atlas 2022-10-02 13:57:35 -07:00
Daniel Imms 590a8219e3 Merge remote-tracking branch 'upstream/master' into shared_cache 2022-10-02 13:42:44 -07:00
Daniel Imms d22f7c9edd Merge pull request #4166 from Tyriar/event_with_emitter
Create new event with emitter object to simplify code
2022-10-02 13:41:37 -07:00
Daniel Imms c2bf87ee34 Initial working shared atlas 2022-10-02 11:38:46 -07:00
Daniel Imms 0bc5ca8f80 Fix imports 2022-10-02 11:08:06 -07:00
Daniel Imms 5b785d29a1 Expose _listeners for debug/test 2022-10-02 09:09:05 -07:00
Daniel Imms f38a49b4d5 Move remaining webgl atlas to shared 2022-10-02 08:48:55 -07:00
Daniel Imms 26c974f9b6 Shuffle some utils 2022-10-02 08:37:13 -07:00
Daniel Imms 333b0e28ac Add ITextureAtlas and use that instead of WebglCharAtlas 2022-10-02 07:48:43 -07:00
Daniel Imms 0bc71156bf Merge remote-tracking branch 'upstream/master' into shared_cache 2022-10-02 07:26:23 -07:00
Daniel Imms dece3cc41c DynamicCharAtlas -> CanvasCharAtlas 2022-10-02 07:20:55 -07:00
Daniel Imms 8752c4fa8e Remove canvas char atlas base class 2022-10-02 07:19:57 -07:00
Daniel Imms 53d0297898 Lint client.ts 2022-10-02 07:15:12 -07:00
Daniel Imms af6f03600f Show canvas texture atlas on demo 2022-10-02 07:10:37 -07:00
Daniel Imms 1f8e6f0952 Merge pull request #4169 from Tyriar/canvas_freq
Add willReadFrequently to canvas renderer too
2022-10-01 21:46:13 -07:00
Daniel Imms f087bd121e Add willReadFrequently to canvas renderer too 2022-10-01 21:38:01 -07:00
Daniel Imms d669c6e9d6 Fix remaining references 2022-10-01 18:03:43 -07:00
Daniel Imms c5f9eacbb1 Move other render parts to shared folder 2022-10-01 17:41:16 -07:00
Daniel Imms 6fcc496547 Move cache utils to core shared/ folder
Part of #4065
2022-10-01 17:36:16 -07:00
Daniel Imms 5dee66353a Merge pull request #4168 from Tyriar/4128
Ensure texture atlas comparison uses rgba not object
2022-10-01 10:30:17 -07:00
Daniel Imms 2a602ef231 Ensure texture atlas comparison uses rgba not object
Fixes #4128
2022-10-01 10:20:37 -07:00
Daniel Imms e3e3c347ea Merge pull request #4160 from jerch/tweak_serverside_buffering
tweak buffering in server.js
2022-10-01 09:59:01 -07:00
Daniel Imms 2e8b793ba0 Format files 2022-10-01 08:56:56 -07:00
Daniel Imms 03fcf60aaf Docs 2022-10-01 08:55:01 -07:00
Daniel Imms ed15a8e170 Create new event with emitter object to simplify code 2022-10-01 08:45:40 -07:00
Daniel Imms 969db2bcf9 Merge pull request #4163 from Tyriar/4147
Inline dirty row service into input handler
2022-10-01 08:28:29 -07:00
Daniel Imms 8e378c07e9 Merge pull request #4165 from Tyriar/4164
Define all events and emitters consistently
2022-10-01 08:28:18 -07:00
Daniel Imms 1a22c44f2b Make all emitters readonly 2022-10-01 08:02:32 -07:00
Daniel Imms cf9949840b Use readonly over getter for exposing events
Fixes #4164
2022-10-01 07:52:26 -07:00
Daniel Imms 3a90f7e7c0 Inline dirty row service into input handler
Fixes #4147
2022-10-01 07:39:02 -07:00
Daniel Imms 035025dba3 Merge pull request #4162 from Tyriar/w_refactor
Move w objects to $ prefix variables
2022-10-01 07:33:45 -07:00
Daniel Imms 52ed65d7bd Move w objects to $ prefix variables
Fixes #4138
2022-10-01 07:26:07 -07:00
Jörg Breitbart 150499b7e5 comment fix 2022-09-29 10:46:25 +02:00
Jörg Breitbart 193d305dcd revert timeout to 5ms, remove log in server.js 2022-09-29 10:36:08 +02:00
Jörg Breitbart 2236e230fc Merge branch 'master' into tweak_serverside_buffering 2022-09-28 20:06:01 +02:00
Jörg Breitbart cd6f370c28 unbuffered delivery for userInput response only 2022-09-28 20:03:55 +02:00
Daniel Imms 2659de2291 Merge pull request #4159 from Tyriar/4158
Use microtask to flush the write buffer after input
2022-09-28 10:46:15 -07:00
Daniel Imms 34df17e36d Call innerWrite immediately over a microtask 2022-09-28 09:51:28 -07:00
Daniel Imms 44388e5e6c Use microtask to flush the write buffer after input
Fixes #4158
2022-09-28 09:06:07 -07:00
Jörg Breitbart 785b932d75 lazy cleanup on buffer 2022-09-28 15:57:58 +02:00
Jörg Breitbart e58f73dc82 Merge branch 'master' into buffer_optimizations 2022-09-28 13:22:16 +02:00
Jörg Breitbart 7bba07e24e rename fourByteCells to uint32Cells 2022-09-28 13:18:31 +02:00
Jörg Breitbart 39eea8ed88 remove useless condition 2022-09-28 13:15:34 +02:00
Jörg Breitbart 20c1f1a5f7 fix resize edge cases: always report cleanup state 2022-09-28 12:14:09 +02:00
Jörg Breitbart e87f70d6d6 removed EMPTY_DATA branch 2022-09-28 11:55:26 +02:00
Daniel Imms 51c580e7a4 Merge pull request #4157 from Tyriar/4156
Fix trusted types in dom renderer
2022-09-27 13:15:25 -07:00
Daniel Imms 23328a394b Fix trusted types in dom renderer
Fixes #4156
2022-09-27 13:01:36 -07:00
Daniel Imms 399621eeb5 Merge pull request #4154 from Tyriar/qmt
Remove queuemicrotask polyfill
2022-09-27 07:41:57 -07:00
Daniel Imms 66675ef649 Merge pull request #4153 from Tyriar/microtask_fix
Fix repetitive microtask problem
2022-09-27 06:57:00 -07:00
Daniel Imms c6df208a3e Merge pull request #4155 from Tyriar/fix_priority_queue
Fix priority task queue and idle callback fallback
2022-09-27 06:56:52 -07:00
Daniel Imms 8201925fe8 Fix priority task queue and idle callback fallback
Mistake of not hooking up the abstract methods, this should throw on
Safari for example.
2022-09-27 06:46:19 -07:00
Daniel Imms d74e43a42a Remove queuemicrotask polyfill
Support seems good
2022-09-27 06:44:18 -07:00
Daniel Imms 25b69e5ed0 Fix repetitive microtask problem
See #4145
2022-09-27 06:43:07 -07:00
Daniel Imms 37c722d556 Merge pull request #4152 from dstein64/master
Move real-world uses section down
2022-09-26 08:27:21 -07:00
Daniel Steinberg 2b597a82a1 Move real-world uses section down 2022-09-26 11:19:44 -04:00
Daniel Imms 318abe641b Merge pull request #4145 from Tyriar/microtask_server
Optimize critical I/O path for input latency
2022-09-25 09:39:02 -07:00
Daniel Imms eca9c721d4 Merge pull request #4144 from Tyriar/priority_queue
Add PriorityTaskQueue
2022-09-25 09:38:44 -07:00
Daniel Imms 0b8120808c Prefer microtask to timeout in write queue
This seems to make little difference but it's more correct as we want
to write as fast as possible to minimize input latency
2022-09-24 16:27:56 -07:00
Daniel Imms c6e4788344 Use microtasks on server
This reduces demo input latency on my mac approx 10ms -> 4ms
2022-09-24 16:23:14 -07:00
Daniel Imms c548977a3e Fix imports 2022-09-24 16:16:14 -07:00
Daniel Imms 04d196a32d Use task queue interface 2022-09-24 16:14:38 -07:00
Daniel Imms 494fe3a95d Fill in ITaskQueue 2022-09-24 16:13:53 -07:00
Daniel Imms 8f8813a19a Add PriorityTaskQueue 2022-09-24 15:16:02 -07:00
Daniel Imms df8a57d14b Merge pull request #4143 from Tyriar/idle_deadline
Use the browser's IdleDeadline to determine max task time
2022-09-24 15:00:02 -07:00
Daniel Imms 868fc7ec29 Allow loading canvas before open 2022-09-24 14:44:13 -07:00
Daniel Imms 2febe19222 Set renderer after screen element is attached to dom 2022-09-24 14:42:57 -07:00
Daniel Imms 693d09cbad Allow setting renderer addon before open is called 2022-09-24 14:41:48 -07:00
Daniel Imms ab90dcd0dc Clarify comment 2022-09-24 14:13:04 -07:00
Daniel Imms 1f45d5344c Use the browser's IdleDeadline to determine max task time 2022-09-24 14:10:29 -07:00
Daniel Imms a9eea3fd46 Merge pull request #4142 from Tyriar/defer_paused_resize
Defer paused renderer resize to idle callback
2022-09-24 12:35:40 -07:00
Daniel Imms aec1f2b2be Fix import 2022-09-24 11:58:17 -07:00
Daniel Imms 66efb0388a Move prop to top 2022-09-24 11:57:12 -07:00
Daniel Imms 4ac5a91c95 Make DebouncedIdleTask helper 2022-09-24 11:56:46 -07:00
Daniel Imms 4e8e6b9788 Defer paused renderer resize to idle callback 2022-09-24 11:45:48 -07:00
Daniel Imms 275a8a91ee Merge pull request #4141 from Tyriar/idle_task_queue
Do char atlas warmup via new IdleTaskQueue
2022-09-24 11:44:10 -07:00
Daniel Imms 270ac1c7d9 Docs 2022-09-24 11:17:39 -07:00
Daniel Imms c42f290eb8 Do char atlas warmup via new IdleTaskQueue 2022-09-24 11:10:54 -07:00
Daniel Imms 8b323bbf69 Merge pull request #4140 from Tyriar/fast_setTheme
Share rgba vars throughout Color.ts, fast setTheme parseColor
2022-09-24 11:09:51 -07:00
Daniel Imms e68bf3ca5b Merge remote-tracking branch 'ups/master' into fast_setTheme 2022-09-24 11:02:15 -07:00
Daniel Imms b742da4bfa Merge pull request #4137 from Tyriar/willReadFrequently
Optimize contexts for reading frequently
2022-09-24 10:40:39 -07:00
Daniel Imms 6368cb4cff Move canvas-based parsing to Color.ts lib
This removed the allowTransparency code, it didn't actually do much
before, just whether the 'advanced parsing' would allow transparency.
Since we already do the common formats, I'm not even sure what other
format it could be.
2022-09-24 10:37:40 -07:00
Daniel Imms 81d68b115c Share rgba vars throughout Color.ts, fast setTheme parseColor 2022-09-24 10:00:57 -07:00
Daniel Imms d62b4531c3 Optimize contexts for reading frequently
This was a recommendation from Edge.
Docs here: https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-will-read-frequently
2022-09-24 06:58:01 -07:00
Daniel Imms 1431210ecd Merge pull request #4125 from BattlefieldDuck/master
Update allowProposedApi comments
2022-09-23 13:27:25 -07:00
Daniel Imms 50b1e1045c Merge pull request #4134 from Tyriar/fix_upper_one_eighth_block
Fix width of upper 1/8 block char
2022-09-23 10:33:19 -07:00
Daniel Imms 09a5da5d33 Fix width of upper 1/8 block char
See microsoft/vscode#161323
2022-09-23 10:11:28 -07:00
Daniel Imms 349acbba34 Merge pull request #4131 from Tyriar/idleWarmup
Perform texture atlas warm up in an idle callback
2022-09-22 13:48:56 -07:00
Daniel Imms 78128762f9 Perform texture atlas warm up in an idle callback
Part of #4103
2022-09-22 13:06:08 -07:00
coderaiser 497cfcb74c Merge branch 'master' into feature/lint-by-putout 2022-09-19 12:00:14 +03:00
TatLead 11c806a4d0 Update xterm.d.ts 2022-09-19 05:54:11 +08:00
TatLead 9fb84cb89d Update xterm-headless.d.ts 2022-09-19 05:53:50 +08:00
Daniel Imms 1ceec0b6fa Merge pull request #4123 from Tyriar/remove_v5_publishing
Remove special v5 beta publishing
2022-09-15 13:02:17 -07:00
coderaiser bbff3e8396 Update src/common/services/InstantiationService.ts 2022-09-15 22:52:50 +03:00
Daniel Imms 73cb96ddec Remove special v5 beta publishing 2022-09-15 12:39:32 -07:00
Daniel Imms 904ddffc8d Merge pull request #4122 from Tyriar/release_v5
Release v5, including addon version bumps
2022-09-15 12:33:47 -07:00
Daniel Imms c23f4313fb Release v5, including addon version bumps 2022-09-15 12:22:32 -07:00
coderaiser 25eb7d6472 chore: lint using 🐊Putout 2022-09-15 17:19:34 +03:00
Jörg Breitbart 6225cc2b9c optimize certain resize conditions 2022-09-13 14:37:29 +02:00
Daniel Imms d3fd3d0f22 Merge pull request #4098 from jerch/protected
protection flag support
2022-09-12 05:52:43 -07:00
Daniel Imms 3387d7fe46 Merge pull request #4109 from Tyriar/4108
Copy extended attributes to dest in reflow
2022-09-12 05:50:08 -07:00
jerch 74b5d3f881 Merge branch 'master' into protected 2022-09-09 22:58:29 +02:00
Daniel Imms 822ac527ba Correct type and use ! to highlight the unsafe part 2022-09-09 12:48:55 -07:00
Daniel Imms 2032f409cf Copy extended attributes to dest in reflow
Fixes #4108
2022-09-09 12:28:27 -07:00
Daniel Imms f3622dfedc Merge pull request #4069 from mihaip/master
Make terminal rendering work in popout windows
2022-09-09 10:18:11 -07:00
Mihai Parparita a39a468f19 Make terminal rendering work in popout windows
Add support for the parent element (what is passed to Terminal.open)
being in a different (same origin) window. Accesses to DOM APIs such
as requestAnimationFrame and devicePixelRatio thus need to be scoped
to the corrent window, instead of assuming that it's the same as the
window/global scope where the code is running.

This is done by inferring a parent window at the creation time, and
then storing it in CoreBrowserService, which is already passed to most
places that need it.

To catch future regressions, an ESLint rule that checks for global
accesses is added (it uses AST selectors via the no-restricted-syntax
rule).

This should also be applicable when the parent element is an iframe.

Fixes #3758
2022-09-08 16:59:31 -07:00
Mihai Parparita 2935d9feb7 Add demo of opening the terminal in a popout window
The poput window is just a rendering surface, the termminal code still
executes in the original/parent window.
2022-09-08 15:13:17 -07:00
Jörg Breitbart 21105b4017 Merge remote-tracking branch 'upstream/master' into protected 2022-09-07 19:51:51 +02:00
Jörg Breitbart b01a94f5bb rename to respectProtect 2022-09-07 19:47:28 +02:00
Daniel Imms c57a52b900 Merge pull request #4105 from jeanp413/jp/fix-vs-160070
Fixes https://github.com/microsoft/vscode/issues/160070
2022-09-07 10:00:43 -07:00
Jean Pierre 3953db0d68 Fixes https://github.com/microsoft/vscode/issues/160070 2022-09-07 11:56:52 -05:00
Daniel Imms 1923473a73 Merge pull request #4007 from ink404/handle-delete-composition-helper
Handle Delete in `CompositionHelper`
2022-09-07 09:43:49 -07:00
Daniel Imms d739191adb Merge branch 'master' into handle-delete-composition-helper 2022-09-07 09:39:54 -07:00
Daniel Imms 9ef2397918 Merge pull request #4101 from JasonXJ/render-bug
WebglRenderer always clears _glyphRenderer if _model is cleared
2022-09-07 09:01:41 -07:00
Daniel Imms e9d276a827 Merge pull request #4095 from jerch/request_mode
DECRQM support
2022-09-07 08:58:33 -07:00
Daniel Imms 327ece07e8 Merge pull request #4102 from JasonXJ/clip-cursor-cell
Webgl cursor renderer makes sure a char is not drawn outside of the cell
2022-09-07 07:49:24 -07:00
Jason Lin 4752f99e03 Webgl cursor renderer makes sure a char is not drawn outside of the cell 2022-09-06 14:12:15 +10:00
Jason Lin e5ad3ce9b9 Polish 2022-09-05 17:32:02 +10:00
Jason Lin faf5f72c1f Clear glyph render when the webgl model is cleared 2022-09-05 13:32:04 +10:00
Jörg Breitbart e72e575c0f fix typo in docs 2022-09-04 13:15:47 +02:00
Jörg Breitbart 318b60418e cleanup vtfeaures docs 2022-09-04 12:54:27 +02:00
Jörg Breitbart 68fe100841 cleanup 2022-09-04 11:51:41 +02:00
Jörg Breitbart d511720f63 reshape DECRQSS 2022-09-04 11:34:31 +02:00
Jörg Breitbart 2608db2728 unit tests, fix DECRQSS report 2022-09-04 11:04:07 +02:00
Jörg Breitbart fc46d69a57 add protection flag to DECSED/DECSEL, add DECSCA 2022-09-03 23:52:30 +02:00
jerch 74fc7e2686 Merge branch 'master' into request_mode 2022-09-03 13:32:27 +02:00
Jörg Breitbart f08ad87e7a unit tests 2022-09-03 13:25:20 +02:00
Jörg Breitbart 65f0f9a1d4 restructure code, enable DECSET 12 and SM 20 2022-09-03 12:14:53 +02:00
Daniel Imms 93bc9bf152 Merge pull request #4004 from silamon/requiredoptions3
ITerminalOptions: Make fields required
2022-09-02 13:20:49 -07:00
Daniel Imms e8e2f16cff Merge remote-tracking branch 'upstream/master' into pr/silamon/4004 2022-09-02 13:05:52 -07:00
Daniel Imms 1b38a004a3 Merge pull request #3992 from SvanT/master
Bump minimum target to es2015
2022-09-02 12:43:40 -07:00
Jörg Breitbart df1d72aee5 make linter happy 2022-09-02 01:51:27 +02:00
Jörg Breitbart bb4fbf2659 DECRQM impl 2022-09-02 01:43:31 +02:00
jerch f9db65c52e Merge pull request #4093 from jerch/sgr_pixels
SGR-pixels mouse report
2022-09-01 18:04:50 +02:00
Daniel Imms f816ed0f71 Merge pull request #4094 from sorenisanerd/gotty-maintainer-change
Reference sorenisanerd fork of gotty
2022-09-01 08:26:21 -07:00
Søren L. Hansen 64c8c16730 Reference sorenisanerd fork of gotty
I took over maintainership of gotty a while ago. The original project
is dormant or dead.
2022-09-01 08:22:07 -07:00
Jörg Breitbart 70d30f5a09 fix mouse api tests 2022-09-01 13:31:51 +02:00
Jörg Breitbart edc4fb4a29 document pixels mouse mode 2022-09-01 12:46:10 +02:00
Jörg Breitbart ef1b773fd7 fix pixel to cell offsets 2022-09-01 12:35:44 +02:00
Jörg Breitbart 06d6e08347 sgr_pixels mouse report 2022-09-01 00:50:11 +02:00
Daniel Imms 3acf08bc0c Merge pull request #4090 from xtermjs/Tyriar-patch-1
Create codeql-analysis.yml
2022-08-31 07:39:29 -07:00
Daniel Imms ab8ce61e56 Remove python scanning 2022-08-31 07:26:23 -07:00
Daniel Imms c5a6b997d1 Remove lgtm comments in favor of codeql dismissable alerts 2022-08-31 07:25:03 -07:00
Daniel Imms 6d2d4555e7 Create codeql-analysis.yml 2022-08-31 06:28:58 -07:00
Daniel Imms d673e02afe Merge pull request #4089 from Tyriar/lgtm
Fix lgtm recommendations
2022-08-31 06:26:56 -07:00
Daniel Imms 151a5d88ce Merge pull request #4075 from kenrussell/context-restoration
Handle WebGL context loss/restore events in WebGL renderer.
2022-08-31 05:56:30 -07:00
Daniel Imms f309affff2 Tweak handling of _contextRestorationTimeout 2022-08-31 05:44:48 -07:00
Kenneth Russell 9459553665 Handle WebGL context loss/restore events in WebGL renderer.
If the context is restored within a brief period of time (3 seconds),
recreate members dependent on WebGL state, and fire onRequestRedraw to
the renderer's listeners.

Handle changes to the char atlas better in the demo client.

Tested in Chrome Canary on macOS by running the demo in one window,
and visiting the internal URL about:gpucrash in another window. The
terminal now recovers and redraws properly.
2022-08-30 19:42:22 -07:00
Daniel Imms 4a6e7bee21 Fix lgtm recommendations 2022-08-30 14:09:39 -07:00
Daniel Imms ef759a6d7b Merge pull request #4086 from Tyriar/padding_powerline
Only remove padding from restricted powerline chars
2022-08-30 08:53:03 -07:00
Daniel Imms 9dca3c48c7 Merge pull request #4088 from Tyriar/hyperlink_ternary_fix
Fix ternary in activate call
2022-08-30 07:57:21 -07:00
Daniel Imms 284d9f9893 Fix ternary in activate call
Part of #1134
2022-08-30 07:51:32 -07:00
Daniel Imms 99c47eb3c3 Merge pull request #4087 from Tyriar/hyperlink_range
Expose range on link handler
2022-08-30 07:08:06 -07:00
Daniel Imms c5861d512c Import from Types 2022-08-30 07:01:23 -07:00
Daniel Imms 22cca3495a Expose range on link handler
This is needed for proper tooltip positioning

Part of #1134
2022-08-30 07:00:33 -07:00
Daniel Imms 0c864cddba Merge pull request #4005 from Tyriar/1134_3
OSC hyperlink support
2022-08-30 06:03:36 -07:00
Daniel Imms 3f3fc183f4 Merge branch 'master' into 1134_3 2022-08-30 05:53:36 -07:00
Ian K cad6b49e39 Merge branch 'master' into handle-delete-composition-helper 2022-08-29 21:27:17 -05:00
Daniel Imms 7dae4bc8bc Merge pull request #4085 from Tyriar/fix_gap
Fix rounding error gap in webgl renderer
2022-08-28 13:36:13 -07:00
Daniel Imms 5c6d16ef0a Only remove padding from restricted powerline chars 2022-08-28 13:33:09 -07:00
Daniel Imms f7e0f69598 Fix rounding error gap in webgl renderer
Fixes #4054
Fixes #4076
2022-08-28 13:22:29 -07:00
Daniel Imms 591f178d31 Merge pull request #4084 from Tyriar/more_gc_2
Optimize gc in WebglRenderer and RectangleRenderer render loops
2022-08-28 09:30:55 -07:00
Daniel Imms 3b4ecd0709 Optimize gc in rectangle renderer 2022-08-28 09:20:24 -07:00
Daniel Imms 4a50ca81c6 Optimize gc for WebglRenderer._updateModel and BufferLine.loadCell
RectangleRenderer not done yet
2022-08-28 09:06:42 -07:00
Daniel Imms 4effa381f7 Merge pull request #4081 from Tyriar/more_gc
More garbage collection optimizations for GlyphRenderer
2022-08-28 08:53:48 -07:00
Daniel Imms caaca169eb Add multi key map tests 2022-08-28 08:45:36 -07:00
Daniel Imms 1d945fdffc Create 2 and 4 key map objects, use in color and glyph caches 2022-08-28 08:34:51 -07:00
Daniel Imms 23ee719f6e Remove more garbage collection issues from cell render loop
Part of #4079
2022-08-28 08:02:35 -07:00
Daniel Imms eb31850e87 Merge pull request #4080 from Tyriar/decoration_gc
Optimize gc from decoration-related hot code paths
2022-08-28 07:44:34 -07:00
Daniel Imms 1b64a09f50 Correct conditions after moving to work object 2022-08-27 21:35:56 -07:00
Daniel Imms 823f7345b0 Merge pull request #4078 from Tyriar/fix_demo_resize
Fix resize on demo
2022-08-27 20:52:16 -07:00
Daniel Imms 0727741ca1 Remove iterators from IDecorationService
They're too low performance for typical use cases

Fixes #4079
2022-08-27 20:50:16 -07:00
Daniel Imms 5e3f63e876 Avoid garbage collection in webgl decoration hot code
Part of #4079
2022-08-27 20:06:58 -07:00
Daniel Imms 0237b2965e Fix resize on demo 2022-08-27 19:28:20 -07:00
Daniel Imms 3105797da1 Merge pull request #4077 from Tyriar/4063
Take char width into account when drawing underlines/strokethrough
2022-08-27 07:02:33 -07:00
Daniel Imms 4d837e2802 Add an emoji to underline test 2022-08-27 06:39:07 -07:00
Daniel Imms 4ecc098de4 Get width of emoji/combined chars 2022-08-27 06:38:15 -07:00
Daniel Imms e76918eddc Take char width into account when drawing underlines/strokethrough
Fixes #4063
2022-08-27 06:34:38 -07:00
Daniel Imms 903830ce1f Add CJK chars to underline test 2022-08-27 06:06:17 -07:00
Daniel Imms 2526409ca5 Merge pull request #4074 from Tyriar/4059
Only draw character outline if it has a descent
2022-08-26 14:38:40 -07:00
Daniel Imms 7ffd9e1f7f Only draw character outline if it has a descent
Fixes #4059
2022-08-26 14:16:02 -07:00
Daniel Imms 4979e9d2be Show test characters in underline test 2022-08-26 13:26:00 -07:00
Daniel Imms 8bbbc98ac8 Merge pull request #4073 from Tyriar/4072
Show entire glyph for extra powerline symbols
2022-08-26 08:33:53 -07:00
Daniel Imms 98ceafb57a Merge pull request #4070 from Tyriar/4066
Improve glyph padding, especially in large font sizes
2022-08-26 08:15:57 -07:00
Daniel Imms f6860442fb Show entire glyph for extra powerline symbols
Fixes #4072
2022-08-26 08:12:00 -07:00
Daniel Imms 4382a551d1 Use powerline extra symbols in demo font family 2022-08-26 07:53:20 -07:00
Daniel Imms 774ebe40e6 Improve glyph padding, especially in large font sizes
Fixes #4066
2022-08-26 07:26:36 -07:00
Ian K 6e3388a2c4 Merge branch 'master' into handle-delete-composition-helper 2022-08-26 09:09:02 -05:00
Daniel Imms d92d7904ab Merge pull request #4068 from Tyriar/vscode_158497
Properly clear underscore glyph's background
2022-08-24 15:43:42 -07:00
Daniel Imms 523de80162 Merge branch 'master' into vscode_158497 2022-08-24 15:36:44 -07:00
Daniel Imms e40cf0684e Properly clear underscore glyph's background
See microsoft/vscode#158497
2022-08-24 15:36:03 -07:00
Daniel Imms b68f82078b Merge pull request #4055 from GreenMashimaro/fix-typo
Fix typo and remove unnecessary eslint comments
2022-08-24 08:55:41 -07:00
Daniel Imms 86ba6cd288 Tweak comment 2022-08-24 08:55:11 -07:00
greenmashimaro ac81d3f72d feat: restore LocalizableStrings.ts const export 2022-08-24 22:43:00 +08:00
greenmashimaro 559b6950b2 remove unnecessary eslint comments 2022-08-23 15:09:52 +08:00
greenmashimaro b59de22c36 fix typo 2022-08-23 15:09:43 +08:00
Ian K 86c8ff0e33 Merge branch 'master' into handle-delete-composition-helper 2022-08-21 09:26:45 -05:00
Daniel Imms c0ef2ccb5a Merge pull request #4053 from Tyriar/underline_polish
Polish underline for more fonts to prevent odd overlap look
2022-08-19 13:54:41 -07:00
Daniel Imms 0698afaf12 Only stroke glyph when font size >= 12 2022-08-19 13:24:39 -07:00
Daniel Imms 79d47893df Move webgl underline rendering down
See microsoft/vscode#158326
2022-08-19 13:19:08 -07:00
Daniel Imms 7e6282e9c2 Merge pull request #4010 from xtermjs/Tyriar-patch-1
Whitespace change to trigger release
2022-08-10 06:27:44 -07:00
Daniel Imms 9a119a7292 Whitespace change to trigger release 2022-08-10 06:27:32 -07:00
Daniel Imms 45b019bd89 Merge pull request #4009 from Tyriar/vscode157444
Ensure canvas device dims are > 0x0 when firing callback
2022-08-10 06:02:49 -07:00
Daniel Imms 81a62306af Ensure canvas device dims are > 0x0 when firing callback
See microsoft/vscode#157444
2022-08-10 05:59:29 -07:00
Ian K 5312602692 handle same length case 2022-08-08 17:02:21 -05:00
Ian K 413a885b5e handle if the newValue.length is less than the old value by deleting 2022-08-08 12:41:12 -05:00
Daniel Imms bc528cd870 Ensure extended underline style is cleared on SGR 24 2022-08-07 11:51:52 -07:00
Daniel Imms 2cfbee413f Add some OscLinkService unit tests 2022-08-07 11:30:08 -07:00
Daniel Imms e87e15e87b Only override AttributeData.isUnderline when urlId is set 2022-08-07 10:36:42 -07:00
Daniel Imms 8191483fbd Evict links from maps when markers are disposed 2022-08-07 08:05:42 -07:00
Daniel Imms 9c9ff2b38c Clean up 2022-08-07 07:46:16 -07:00
Daniel Imms c39d235168 Fix adjacent link edge case 2022-08-07 07:42:21 -07:00
Daniel Imms d9b8c6a838 linkHandler option and default handler 2022-08-07 07:35:46 -07:00
Daniel Imms ace99d125d Progress on osc link service 2022-08-07 07:02:42 -07:00
Daniel Imms fd79100d95 Fix test service injection 2022-08-07 06:10:27 -07:00
Daniel Imms 7970b5c1e3 Fix edge cases in link creation 2022-08-07 06:05:42 -07:00
Daniel Imms 91d46dae50 OSC link progress 2022-08-07 05:08:13 -07:00
Simon Lamon 6e123e90ee Add example back 2022-08-05 16:56:54 +00:00
Simon Lamon a1385f589b Required options 2022-08-05 16:16:12 +00:00
Svante Boberg 4f46f8e123 Merge branch 'xtermjs:master' into master 2022-08-04 22:05:07 +02:00
Daniel Imms c021571194 Merge pull request #4003 from Tyriar/liga_no_node
Remove node target from ligatures
2022-08-04 10:31:02 -07:00
Daniel Imms f0926e7821 Remove tests that rely on node
We can test this primarily with api tests instead
2022-08-04 10:11:11 -07:00
Daniel Imms d495fdd9ab Bring back webpack resolve config 2022-08-04 10:01:58 -07:00
Daniel Imms f3f6a91f16 Remove node target from ligatures
This complicates bundling for sandboxed electron a lot, ideally we would only
rely on browser APIs for font access
2022-08-04 09:57:37 -07:00
Daniel Imms 409cb4bb73 Merge pull request #4002 from Tyriar/liga_target
Remove electron renderer target from ligatures addon
2022-08-04 06:29:41 -07:00
Daniel Imms d608912efd Remove electron renderer target
This causes global to be required which is not available in sandboxed electron
renderers

See microsoft/vscode#157008
2022-08-04 06:20:45 -07:00
Daniel Imms 9d7cef3d63 Merge pull request #3991 from deepanshu44/ansi-colors
print colors
2022-08-04 06:08:40 -07:00
Daniel Imms bc57ea3a7a Reduce duplicate, improve layout 2022-08-04 06:06:31 -07:00
Daniel Imms daba61bd35 Merge pull request #3999 from Tyriar/peer_dep
Bump all peer deps to v5
2022-08-04 05:04:09 -07:00
Daniel Imms 76d9d5a324 Bump all peer deps to v5
Fixes #3997
2022-08-03 09:45:23 -07:00
Daniel Imms 830f8f3231 Merge pull request #3983 from silamon/fixdemo
Fix demo
2022-08-03 04:26:02 -07:00
Simon Lamon 04eb6a4160 Fix wrong merge conflict resolution 2022-08-01 18:00:23 +02:00
Simon Lamon 3f1480a52a Merge branch 'master' into fixdemo 2022-08-01 17:56:30 +02:00
Svante Boberg 14b02b9ad5 Bump minimum target to es2015
Now that we no longer support IE we can raise the minimum ES target a
bit. Changing to es2015 shaves off ~17% from the main bundle size, also
it prevents generators from being transpiled into regular functions etc.
so I would guess it has the potential to give some general performance
improvements too.

For fun I also tried to raise it to es2022, this gave a slightly bigger
bundle than es2015 so I think es2015 could be an ok target unless we
need something newer for some reason.
2022-08-01 10:17:10 +02:00
deepanshu f3cd6134f1 print colors 2022-08-01 11:29:12 +05:30
Daniel Imms 9261780a1d Merge pull request #3989 from Tyriar/3970_2
Don't dim default bg in webgl
2022-07-31 16:28:15 -07:00
Daniel Imms a434800303 Merge pull request #3988 from Tyriar/fix_remove_atlas
Fix unchecking webgl in demo
2022-07-31 15:42:44 -07:00
Daniel Imms 39959c2861 Don't dim default bg in webgl
Fixes #3970
2022-07-31 15:39:34 -07:00
Daniel Imms 03c75360b1 Fix unchecking webgl in demo 2022-07-31 15:28:27 -07:00
Daniel Imms 85984681bb Merge pull request #3987 from Tyriar/3986
Polish powerline rendering
2022-07-31 14:39:54 -07:00
Daniel Imms 0910e58b15 Merge pull request #3924 from silamon/allowmarkersonaltbuffer
Allow markers on the alt buffer
2022-07-31 14:39:45 -07:00
Daniel Imms 14d59999e4 Update src/common/buffer/BufferSet.test.ts 2022-07-31 14:39:37 -07:00
Daniel Imms 8f6bde9641 Polish powerline rendering
Fixes #3986
2022-07-31 14:27:07 -07:00
Daniel Imms 436f8556fd Merge pull request #3972 from silamon/rowscolsheadless
Apply row/col changes from xterm to xterm-headless
2022-07-31 12:43:47 -07:00
Daniel Imms face97d012 Merge pull request #3985 from Tyriar/3969
Fix powerlines, custom glyph offset and improve demo
2022-07-31 12:39:18 -07:00
Daniel Imms 8d92f91029 Always update terminal size in demo 2022-07-31 12:19:39 -07:00
Daniel Imms 1ede106ab4 Fix powerlines not clipped when bg changes 2022-07-31 12:05:20 -07:00
Daniel Imms e7e71dbd3c Merge remote-tracking branch 'upstream/master' into 3969 2022-07-31 11:35:47 -07:00
Daniel Imms 208364147a Allow texture atlas to be zoomed on hover 2022-07-31 11:35:12 -07:00
Daniel Imms d6fe5c7384 Update size when letter spacing changes 2022-07-31 11:21:27 -07:00
Daniel Imms 75ca5006a2 Expose texture atlas change event and use in demo 2022-07-31 11:16:12 -07:00
Daniel Imms 266d795179 Fix line height and letter spacing for custom glyphs 2022-07-31 10:58:04 -07:00
Simon Lamon d0571d8634 Fix formatting 2022-07-31 08:59:25 +00:00
Simon Lamon ca10ef5cad Fix demo 2022-07-31 08:55:44 +00:00
Simon Lamon 31686fda61 Fix tests 2022-07-31 07:54:29 +00:00
Simon Lamon 0a83040ac8 clearAllMarkers 2022-07-31 07:38:14 +00:00
Daniel Imms 889e3ad14b Merge pull request #3980 from Tyriar/3971
Fix underline color and polish width and positioning
2022-07-30 23:50:20 -07:00
Daniel Imms 8a596b220a Merge pull request #3981 from Tyriar/3978
Include purpose of canvas addon in readme
2022-07-30 23:49:17 -07:00
Daniel Imms 8d57c64d29 Include purpose of canvas addon in readme
Fixes #3978
2022-07-30 23:48:37 -07:00
Daniel Imms bbd03fb5fe Merge pull request #3976 from Tyriar/clip_underline
Clip outline drawing near underline
2022-07-30 23:43:42 -07:00
Daniel Imms bff529e1e2 Simplify dom tests and fix assertion 2022-07-30 23:41:08 -07:00
Daniel Imms f4618b229f Bring underlines closer to characters 2022-07-30 23:30:52 -07:00
Daniel Imms 686105942c Reduce line width of webgl underline 2022-07-30 23:25:18 -07:00
Daniel Imms 0514b55936 Fix default underline color
Fixes #3971
2022-07-30 23:16:47 -07:00
Daniel Imms 04a14a748e Merge pull request #3979 from Tyriar/3977
Implement improved texture packing strategy
2022-07-30 22:58:12 -07:00
Daniel Imms ed492f7253 Implement improved texture packing strategy
Fixes #3977
2022-07-30 19:24:09 -07:00
Daniel Imms d459e520d3 Merge pull request #3973 from Tyriar/3970
Fix dim flag applying to selection in webgl
2022-07-30 17:58:41 -07:00
Daniel Imms ce4a85d07d Clip outline drawing near underline
Fixes #3975
2022-07-30 17:52:00 -07:00
Daniel Imms 309487a020 Fix character offset to respect texture padding in webgl
Fixes #3974
2022-07-30 11:07:33 -07:00
Daniel Imms c7122e8447 Fix dim flag applying to selection in webgl
Fixes #3970
2022-07-30 10:29:41 -07:00
Simon Lamon 027fffbadc Fix comment 2022-07-30 15:48:49 +00:00
Simon Lamon dc30a51017 Apply row/col changes from xterm to xtmer-headless 2022-07-30 15:46:07 +00:00
Daniel Imms 640596b043 Merge pull request #3921 from Tyriar/underline
Underline style and color rendering
2022-07-30 07:46:56 -07:00
Daniel Imms d3b3357f3f Merge pull request #3966 from silamon/mochaunittestback
Fix mocha test explorer
2022-07-30 07:39:10 -07:00
Daniel Imms f0ddaf49ea Disable threshold check for glyphs with colored underlines
This causes some underlines to disappear which looks very bad for
colored underlines
2022-07-30 07:36:48 -07:00
Simon Lamon d80a47f539 Add functionality back to mocha test explorer 2022-07-30 14:27:29 +00:00
Daniel Imms d0ec9f2591 Support letter spacing in webgl 2022-07-30 07:05:05 -07:00
Daniel Imms 941cc5100d Increase curly height when dpr <= 1 2022-07-30 06:56:32 -07:00
Daniel Imms e0d4384d3c Merge branch 'master' into underline 2022-07-30 06:40:41 -07:00
Simon Lamon f45eb02394 Keep the root directory clean 2022-07-30 13:35:56 +00:00
Daniel Imms db79efdbc1 Merge pull request #3967 from Tyriar/3962
Include version of Windows to disable windowsMode from
2022-07-30 06:27:27 -07:00
Daniel Imms 8afa714276 Include version of Windows to disable windowsMode from
Fixes #3962
2022-07-30 06:27:02 -07:00
Daniel Imms 0befb9c251 Merge pull request #3965 from Tyriar/inactiveSelection
Inactive selection background support
2022-07-30 06:05:57 -07:00
Simon Lamon a8f63c478c Introduce mocharc again 2022-07-30 12:59:08 +00:00
Daniel Imms ff33834eb2 Merge branch 'master' into inactiveSelection 2022-07-29 14:51:22 -07:00
Daniel Imms a2e07de90c Merge pull request #3963 from Tyriar/fallback_ligatures
Support fallback ligatures and correctly dispose of char joiner
2022-07-29 14:51:00 -07:00
Daniel Imms b0c29cac9a Merge pull request #3932 from Tyriar/tyriar/dim_bg
Fix inverse dim rendering on canvas+webgl
2022-07-29 14:50:47 -07:00
Daniel Imms 7f58c48503 Fix tests as errors are no longer thrown on font access fail 2022-07-29 11:27:23 -07:00
Daniel Imms eb93b02c6c Use debug log level to avoid polluting embedder logs 2022-07-29 11:24:23 -07:00
Daniel Imms 11affae761 Merge branch 'master' into inactiveSelection 2022-07-29 11:21:15 -07:00
Daniel Imms 53e4e9ad72 Inactive selection bg in canvas 2022-07-29 11:20:32 -07:00
Daniel Imms 4ac8e4ca51 Remove reliance on core in some webgl parts 2022-07-29 11:09:19 -07:00
Daniel Imms 954d9371a1 Inactive selection in webgl 2022-07-29 11:05:41 -07:00
Daniel Imms 6493679b2e Inactive selection bg in dom renderer
Part of #3803
2022-07-29 11:00:36 -07:00
Daniel Imms 1af6175e48 Merge pull request #3964 from Tyriar/selection_inconsistency
Rename ITheme.selection to selectionBackground
2022-07-29 11:00:11 -07:00
Daniel Imms 906a6fc558 Rename test selection -> selectionBackground 2022-07-29 10:35:48 -07:00
Daniel Imms 9cb25fbf06 Rename ITheme.selection to selectionBackground
This makes it consistent with selectionForeground and other theme keys and
as a result will make the API easier to work with, especially when inactive
selection is done (#3803)
2022-07-29 10:34:27 -07:00
Daniel Imms 48142eca23 Merge branch 'master' into tyriar/dim_bg 2022-07-29 10:20:22 -07:00
Daniel Imms 1fe0ec8d2c Merge branch 'master' into underline 2022-07-29 10:19:52 -07:00
Daniel Imms f8a9eb1db6 Support fallback ligatures and correctly dispose of char joiner 2022-07-29 10:18:21 -07:00
Daniel Imms df14ce49d7 Merge pull request #3936 from Tyriar/ligatures
Get ligatures addon working with latest version of Chrome
2022-07-29 07:33:49 -07:00
Daniel Imms 7f5ed40db1 Merge pull request #3960 from Tyriar/init_options
Make cols/rows only usable in the ctor
2022-07-29 07:20:51 -07:00
Daniel Imms 29aca47e0d Merge pull request #3961 from Tyriar/canvas_fix2
Remove instantiation service from canvas addon completely
2022-07-28 16:50:29 -07:00
Daniel Imms 055bcce18d Remove instantiation service from canvas addon completely 2022-07-28 11:31:51 -07:00
Daniel Imms 006249ce65 Make cols/rows only usable in the ctor
Fixes #3825
2022-07-28 11:16:12 -07:00
Daniel Imms 60f9bf4398 Merge pull request #3959 from Tyriar/canvas_services
Fix service access in canvas addon
2022-07-28 11:11:08 -07:00
Daniel Imms 848aceaa25 Merge branch 'master' into allowmarkersonaltbuffer 2022-07-28 11:05:24 -07:00
Daniel Imms 9e650cea9f Fix service access in canvas addon 2022-07-28 11:00:08 -07:00
Daniel Imms de78344165 Merge pull request #3919 from SvanT/master
Dispose events
2022-07-28 10:52:09 -07:00
Daniel Imms 2862832f6f Merge pull request #3958 from Tyriar/dispose_doc
Clarify you shouldn't use the object after Terminal.dispose
2022-07-28 10:51:21 -07:00
Daniel Imms 8df878b8a9 Clarify you shouldn't use the object after Terminal.dispose
Fixes #3939
2022-07-28 10:46:12 -07:00
Daniel Imms 0e07e419a2 Merge branch 'master' into allowmarkersonaltbuffer 2022-07-28 10:26:12 -07:00
Daniel Imms 04b513cf9b Merge pull request #3926 from Tyriar/rounding
FINALLY fix blurry canvas/webgl renderer issues due to device pixel rounding errors 🎉
2022-07-28 10:25:01 -07:00
Daniel Imms f310f118b4 Merge pull request #3957 from Tyriar/publish_addon
Fix publishing addons as xterm
2022-07-28 10:21:39 -07:00
Daniel Imms 8d308a7309 Merge branch 'master' into publish_addon 2022-07-28 10:21:26 -07:00
Daniel Imms d9880c0270 Fix publishing addons as xterm 2022-07-28 10:20:57 -07:00
Daniel Imms f6940aa201 Merge pull request #3940 from Tyriar/1140
Implement duration-based smooth scrolling
2022-07-28 09:27:49 -07:00
Daniel Imms 2482c0e8d3 Merge pull request #3956 from Tyriar/fix_addon_version
Fix version of published addons
2022-07-28 09:13:37 -07:00
Daniel Imms 1be14e4338 Fix version of published addons 2022-07-28 09:09:44 -07:00
Daniel Imms 51f89bbe4d Merge pull request #3954 from Tyriar/v0_1_0
xterm-addon-canvas@0.1.0
2022-07-28 07:30:03 -07:00
Daniel Imms 0b0662c4d9 xterm-addon-canvas@0.1.0 2022-07-28 07:27:27 -07:00
Daniel Imms 9cbd9a0456 Merge pull request #3931 from Tyriar/3930
Never clear color when allowTransparency is on
2022-07-28 07:19:58 -07:00
Daniel Imms 770de8d05b Merge pull request #3929 from Tyriar/3845
Retain hue when flipping luminance
2022-07-28 07:19:38 -07:00
Daniel Imms 4372d8ccca Merge pull request #3933 from Tyriar/3784
Warn when ligature fonts fail to download
2022-07-28 07:18:52 -07:00
Daniel Imms 0b81af1811 Merge pull request #3935 from Tyriar/3759
Remove shift+backspace -> ^H binding
2022-07-28 07:18:30 -07:00
Daniel Imms ed92cc0c3f Merge pull request #3938 from Tyriar/1963
Redraw canvas renderer selection on resize
2022-07-28 07:18:20 -07:00
Daniel Imms 8b276c1a54 Merge remote-tracking branch 'upstream/master' into pr/SvanT/3919 2022-07-28 07:04:11 -07:00
Daniel Imms d2be5f992a Merge remote-tracking branch 'upstream/master' into underline 2022-07-28 07:01:41 -07:00
Daniel Imms 437c8aac5c Merge remote-tracking branch 'upstream/master' into rounding 2022-07-28 07:00:13 -07:00
Daniel Imms 84b259dfa5 Merge remote-tracking branch 'upstream/master' into tyriar/dim_bg 2022-07-28 06:57:27 -07:00
Daniel Imms a57b69d5de Merge remote-tracking branch 'upstream/master' into 1963 2022-07-28 06:56:36 -07:00
Daniel Imms 8f0f33fd6d Merge branch 'master' into 1140 2022-07-28 06:50:46 -07:00
Daniel Imms 4d542d23c6 Merge branch 'master' into ligatures 2022-07-28 06:50:40 -07:00
Daniel Imms 4637983888 Merge branch 'master' into 3759 2022-07-28 06:50:35 -07:00
Daniel Imms f1ff42410e Merge branch 'master' into 3784 2022-07-28 06:50:32 -07:00
Daniel Imms 1ef9f7c1d1 Merge branch 'master' into 3930 2022-07-28 06:50:26 -07:00
Daniel Imms 8901740113 Merge branch 'master' into 3845 2022-07-28 06:50:23 -07:00
Daniel Imms 2fffe9fc89 Merge branch 'master' into allowmarkersonaltbuffer 2022-07-28 06:50:17 -07:00
Daniel Imms 423d5402f7 Merge pull request #3917 from xtermjs/Tyriar-patch-1
Remove note about IE11 which is EOL
2022-07-28 06:50:02 -07:00
Daniel Imms 2b2d6c5fb3 Merge pull request #3943 from xtermjs/v5
v5 breaking changes
2022-07-28 06:46:12 -07:00
Daniel Imms c55075b666 Merge pull request #3953 from Tyriar/v5_publish_stable
Hardcode release step to publish v5.0.0 beta
2022-07-28 06:32:47 -07:00
Daniel Imms 09df783959 Merge pull request #3952 from Tyriar/v5_buffer_range
Replace ISelectionPosition with IBufferRange
2022-07-28 06:32:13 -07:00
Daniel Imms 3bdbfb6d5f Hardcode release step to publish v5.0.0 beta 2022-07-28 06:31:52 -07:00
Daniel Imms 520a270afc Update tests to use new getSelectionPosition API 2022-07-28 06:17:44 -07:00
Daniel Imms d1f53e2b02 Replace ISelectionPosition with IBufferRange
Fixes #2480
2022-07-28 05:58:15 -07:00
Daniel Imms 940c93e9cd Merge pull request #3951 from Tyriar/v5_api
Stabilize buffer and parser APIs
2022-07-28 05:33:15 -07:00
Daniel Imms 1a5c881c7c Stabilize buffer and parser APIs 2022-07-28 05:28:28 -07:00
Daniel Imms fb3886a9ae Merge pull request #3950 from Tyriar/v5_publish_canvas
Publish canvas addon in CI release step
2022-07-28 05:09:26 -07:00
Daniel Imms 3271b45681 Publish canvas addon in CI release step
Part of #3271
2022-07-28 05:08:46 -07:00
Daniel Imms 43f38cddb1 Merge pull request #3949 from Tyriar/v5_canvas
Move the canvas renderer into an addon
2022-07-28 05:07:31 -07:00
Daniel Imms e5037a6d1e Fix fit addon api tests on linux+firefox 2022-07-28 05:00:10 -07:00
Daniel Imms 119b64181b Fix fit addon api tests 2022-07-28 04:51:49 -07:00
Daniel Imms c52eb8898f Simplify addon tsconfigs 2022-07-28 04:50:40 -07:00
Daniel Imms ced5c304f1 Move CustomGlyphs.ts into core 2022-07-28 04:46:18 -07:00
Daniel Imms b3cffe2d30 Fix fit addon api tests on linux 2022-07-28 04:43:03 -07:00
Daniel Imms 73b3e31b38 Canvas readme 2022-07-28 04:37:42 -07:00
Daniel Imms cbddd2029e Get canvas renderer addon working 2022-07-27 16:04:24 -07:00
Daniel Imms 48cfd23219 Fix tests that depended on canvas renderer 2022-07-27 10:33:10 -07:00
Daniel Imms 1c6ce99419 Move canvas renderer contents into addon 2022-07-27 10:26:02 -07:00
Daniel Imms ab7da06cc1 Stub canvas addon 2022-07-27 09:26:52 -07:00
Daniel Imms 440ab765c4 Remove rendererType setting 2022-07-27 09:22:05 -07:00
Daniel Imms e7dbdb1362 Merge pull request #3947 from Tyriar/v5_deprecated
Remove remaining deprecated APIs for v5 (getOption, setOption, writeUtf8)
2022-07-27 09:13:15 -07:00
Daniel Imms 8c16d9d506 Remove writeUtf8 deprecated API 2022-07-27 09:04:43 -07:00
Daniel Imms 71911b501b Remove set/getOption deprecated APIs 2022-07-27 09:04:07 -07:00
Daniel Imms b2aa100c58 Merge pull request #3946 from Tyriar/v5_remove_add_apis
Remove add* APIs and remaining link matcher parts
2022-07-27 08:58:32 -07:00
Daniel Imms 6d932ec812 Fix addMarker in api tests 2022-07-27 08:49:07 -07:00
Daniel Imms 5a658ebfcc Remove remaining link hover tooltip duration 2022-07-27 08:45:32 -07:00
Daniel Imms 7bd3801f58 Remove mouse zone manager
Part of #2703
2022-07-27 08:41:27 -07:00
Daniel Imms 8cfa7a0ee2 Remove deprecated add* APIs
Fixes #2652
2022-07-27 08:35:40 -07:00
Daniel Imms 140073675c Merge pull request #3945 from Tyriar/v5_proposed_api
Set allowProposedApi default to false
2022-07-27 08:29:08 -07:00
Daniel Imms 238a2bafb1 Set allowProposedApi default to false
Fixes #2826
2022-07-27 08:20:54 -07:00
Daniel Imms 09672feca0 Merge pull request #3944 from Tyriar/v5_link_matcher
Remove link matchers
2022-07-27 06:57:30 -07:00
Daniel Imms 7a17a26e18 Remove link matchers
Fixes #2703
2022-07-27 06:49:35 -07:00
Daniel Imms fafa7efd5e Merge pull request #3941 from Tyriar/v5_bell
Remove bell settings and sound service in favor of Terminal.onBell
2022-07-27 06:29:34 -07:00
Daniel Imms 841cd636ca Merge remote-tracking branch 'upstream/v5' into v5_bell 2022-07-27 06:18:48 -07:00
Daniel Imms ceeb8585d7 Merge pull request #3942 from Tyriar/v5_ci
Enable CI on v5 branch
2022-07-27 06:17:19 -07:00
Daniel Imms dc4940e26e Try different syntax 2022-07-27 06:13:28 -07:00
Daniel Imms 8a19f8c1cc Enable CI on v5 branch 2022-07-27 06:04:53 -07:00
Daniel Imms 66d6ed65ef Remove all bellSound/bellStyle/etc. references
Fixes #3316
2022-07-27 05:58:26 -07:00
Daniel Imms 09ae3a9b44 Remove sound service and simplify bell 2022-07-27 05:55:00 -07:00
Daniel Imms 78bfe0b89d Revert accidental setting change 2022-07-27 05:47:39 -07:00
Daniel Imms 4b8e7a5768 Change default to 0 2022-07-27 05:45:56 -07:00
Daniel Imms fb4cb0f140 Reduce smooth scroll GC 2022-07-27 05:44:34 -07:00
Daniel Imms a55728dd28 Add duration-based smooth scroll
Fixes #1140
2022-07-27 05:41:00 -07:00
Svante Boberg 52dc6a456c Remove redundant disposal 2022-07-27 14:34:00 +02:00
Svante Boberg 078b83fcaf Use disposal registration 2022-07-27 14:24:42 +02:00
Svante Boberg c895f67fd8 Add missing webgl cursor blink disposal
Same disposal code as the non-webgl cursor blink layer
2022-07-27 00:00:41 +02:00
Daniel Imms 9159fc68e1 Redraw canvas renderer selection on resize
Fixes #1963
2022-07-25 09:44:54 -07:00
Daniel Imms 5664ff3101 Fix ligature tests in node 2022-07-25 07:42:38 -07:00
Daniel Imms 78ddb4b14a Add link to font access api change 2022-07-25 07:00:58 -07:00
Daniel Imms 4744ef34b2 Remove unneeded comment 2022-07-24 20:44:06 -07:00
Daniel Imms f4bc9f1ecd Pull fonts using latest font access API in Chrome
Part of #958
2022-07-24 19:47:24 -07:00
Daniel Imms b11217aa36 Merge pull request #3920 from Tyriar/task_improvement
Allow start task to be restarted without killing watch
2022-07-24 17:52:41 -07:00
Daniel Imms 34fe6e2260 Merge pull request #3922 from Tyriar/theme_picker
Add simple theme picker to demo
2022-07-24 17:52:28 -07:00
Daniel Imms 160e9ee35b Merge pull request #3923 from Tyriar/atlas_bottom
Don't overlap the atlas ontop of options
2022-07-24 17:52:16 -07:00
Daniel Imms d98198e1b9 Merge pull request #3927 from Tyriar/overviewRulerWidth
Support overviewRulerWidth in demo
2022-07-24 17:52:07 -07:00
Daniel Imms 493975b41e Merge pull request #3934 from Tyriar/3777
Fix test-api running unit tests as well
2022-07-24 17:51:57 -07:00
Daniel Imms 3322418728 Remove shift+backspace -> ^H binding
This has been there since the first commit of term.js, it's not clear
why it's there but Terminal.app, iTerm2 and kitty seem to just do a
regular backspace

Fixes #3759
2022-07-24 17:45:14 -07:00
Daniel Imms 2ceec34809 Remove log 2022-07-24 17:20:41 -07:00
Daniel Imms 8c55c83e4a Fix test-api running unit tests as well
This file was added to support the mocha test explorer in vscode
but it turns out it causes all calls to mocha to run the unit
tests. I couldn't figure out how to ignore through args.

Fixes #3777
2022-07-24 17:12:23 -07:00
Daniel Imms 9acc36f8c0 Warn when ligature fonts fail to download
Fixes #3784
2022-07-24 16:52:11 -07:00
Daniel Imms a4f7e2d8e9 Fix inverse dim on canvas renderer 2022-07-24 13:20:04 -07:00
Daniel Imms ee2e54487d Fix inverse dim for webgl
Fixes #3534
2022-07-24 13:13:26 -07:00
Daniel Imms f4ffb13ece Render dim bg in all renderers
Fixes #3534
2022-07-24 10:59:24 -07:00
Daniel Imms a39c7fb636 Never clear color when allowTransparency is on
It's redundant and wasn't working as expected anyway.

Fixes #3930
2022-07-24 08:31:12 -07:00
Daniel Imms 0ccf3beec7 Retain hue when flipping luminance
Fixes #3845
2022-07-24 08:03:27 -07:00
Daniel Imms 90b6f058c9 Support overviewRulerWidth in demo
Fixes #3771
2022-07-24 07:38:48 -07:00
Daniel Imms 0165c3612d Fix canvas device pixel dimension setting 2022-07-24 07:11:56 -07:00
Daniel Imms 47b1fdd147 Refactor to share observeDevicePixelDimensions 2022-07-24 04:09:34 -07:00
Daniel Imms ad8404417e ts-loader update, correct css cell dimension error 2022-07-24 03:57:22 -07:00
Daniel Imms 3d6c25bf2d typescript@4.7
Required for devicePixelContentBoxSize ResizeObserver API
2022-07-24 03:43:28 -07:00
Daniel Imms e25471361f Fix device pixel value in canvas renderer 2022-07-24 03:41:25 -07:00
Daniel Imms cb10aadb06 Fix device pixel rounding errors in webgl renderer
This uses the ResizeObserver devicePixelContentBoxSize API in order to
fetch the exact device pixel dimensions from the browser. The old
possibly blurry behavior is used as a fallback if that API is not
available.

Part of #2662
Part of microsoft/vscode#85154
2022-07-24 03:31:05 -07:00
Simon Lamon 89f5c2e06c Allow markers on the alt buffer 2022-07-24 09:30:13 +00:00
Daniel Imms dbe2b98bb4 Don't overlap the atlas ontop of options
This would prevent mouse input in some options in narrow screens
2022-07-23 18:57:16 -07:00
Daniel Imms 4087284822 Fix ext caching issues 2022-07-23 15:19:17 -07:00
Daniel Imms 759a5928a2 Continuous curly underline 2022-07-23 14:48:26 -07:00
Daniel Imms 43bbea8eb5 Set webgl as default demo renderer 2022-07-23 14:43:14 -07:00
Daniel Imms fd8fc18302 Clear stroke around text in underline 2022-07-23 14:28:15 -07:00
Daniel Imms f34a30b24f Add theme from xtermjs.org and make it the demo default 2022-07-23 14:08:15 -07:00
Daniel Imms 91a362be7b Add 3 sample themes to demo 2022-07-23 13:58:31 -07:00
Daniel Imms 19fb806214 Fix tests 2022-07-23 13:44:12 -07:00
Daniel Imms 70b5e0249c Underline demo formatting 2022-07-23 11:22:40 -07:00
Daniel Imms d9703aa7fe Webgl underline color rendering 2022-07-23 11:19:35 -07:00
Daniel Imms 03f15a7fb7 Store extended attributes in single number 2022-07-23 11:12:17 -07:00
Daniel Imms 8cd01dc421 Add underline color test to demo 2022-07-23 10:38:58 -07:00
Daniel Imms 1f89223c2a DOM renderer underline color 2022-07-23 10:26:20 -07:00
Daniel Imms 8287e783a2 Underline color canvas 2022-07-23 10:24:51 -07:00
Daniel Imms 8184e0824b Add tests for dom underline styles 2022-07-23 10:13:26 -07:00
Daniel Imms 72d18f7d5e DOM renderer underline style 2022-07-23 09:20:53 -07:00
Daniel Imms 1d9b4d4778 Canvas renderer underline style 2022-07-23 08:58:10 -07:00
Daniel Imms baa8a1e6ec Stub out canvas renderer underline style 2022-07-23 08:27:15 -07:00
Daniel Imms c116fa645c Render underline style on webgl 2022-07-23 08:23:02 -07:00
Daniel Imms 8b4e8199d7 Get underline style affecting webgl rendering 2022-07-23 07:38:34 -07:00
Daniel Imms 34cd5966b9 Webgl 3 key cache map 2022-07-23 07:31:51 -07:00
Daniel Imms cc8748313e Expose underline attrs as getter/setter
In preparation for storing them in a packed format for glyph caching
2022-07-23 07:13:29 -07:00
Daniel Imms 1207acc495 Add underline test button to demo 2022-07-23 07:05:52 -07:00
Daniel Imms c29cd83213 Allow start task to be restarted without killing watch 2022-07-23 06:39:41 -07:00
Svante Boberg 9df996cb3d Dispose events
https://github.com/microsoft/vscode/issues/155232

The events that is setup for needs to be disposed for js to be able
to garbage collect the terminals.
2022-07-23 13:28:52 +02:00
Daniel Imms d2ac7fc90c Merge pull request #3916 from SvanT/master
Fix memory leak
2022-07-22 05:11:54 -07:00
Svante Boberg 92855ba399 Remove terminal from cache on disposal 2022-07-22 13:03:07 +02:00
Daniel Imms b5b7a5d7d0 Remove note about IE11 which is EOL 2022-07-21 15:24:44 -07:00
Daniel Imms 36830266e2 Merge pull request #3913 from xtermjs/dependabot/npm_and_yarn/terser-5.14.2
Bump terser from 5.7.0 to 5.14.2
2022-07-20 06:10:26 -07:00
dependabot[bot] 3ffbcbf201 Bump terser from 5.7.0 to 5.14.2
Bumps [terser](https://github.com/terser/terser) from 5.7.0 to 5.14.2.
- [Release notes](https://github.com/terser/terser/releases)
- [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/terser/terser/commits)

---
updated-dependencies:
- dependency-name: terser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-20 01:30:11 +00:00
Daniel Imms 9997f263a6 Merge pull request #3912 from silamon/extendedcolors2
Allow unsetting extending ansi colors
2022-07-19 11:19:57 -07:00
Simon Lamon 1fdbd504c7 Add another unit test to test partial unset 2022-07-19 17:37:02 +00:00
Simon Lamon 06627cb0bb Allow unsetting extending ansicolors 2022-07-19 17:22:54 +00:00
Daniel Imms 2a0681f0c0 Merge pull request #3909 from Tyriar/3905_followup
Be more defensive when setting extended ansi colors
2022-07-19 06:08:50 -07:00
Daniel Imms 67f83d51c9 Fix min color count 2022-07-19 05:58:20 -07:00
Daniel Imms abe7dddfbc Merge pull request #3908 from uellenberg/patch-1
Add Cratecode to Real-world uses
2022-07-19 05:18:45 -07:00
Daniel Imms 8b3f9ad8e1 Be more defensive when setting extended ansi colors
Part of #3601
2022-07-19 05:10:31 -07:00
uellenberg f26264ac23 Add Cratecode to Real-world uses 2022-07-18 15:45:53 -07:00
Daniel Imms cbb13aa4d4 Merge pull request #3905 from silamon/extendedcolors
Support setting extended ansi colors (16-255)
2022-07-18 10:46:37 -07:00
Daniel Imms 86cfad545a Merge pull request #3907 from silamon/devcontainerdeprecation
Fix devcontainer docker build
2022-07-18 10:42:19 -07:00
Simon Lamon c786c6a072 Apply review comments 2022-07-18 17:35:13 +00:00
Simon Lamon 8153aea64c Fix devcontainer docker build 2022-07-18 17:05:34 +00:00
Daniel Imms ebc1c73585 Merge pull request #3906 from silamon/macosdeprecation
Update MacOS base image
2022-07-18 09:32:59 -07:00
Simon Lamon 306efa4d82 Update macos base image 2022-07-18 18:22:33 +02:00
Daniel Imms 0fbd980d38 Whitespace to trigger webgl release 2022-07-13 11:44:08 -07:00
Daniel Imms 0a50c0d27e Merge pull request #3903 from Tyriar/3902
Fix \u259B rendering
2022-07-13 10:13:56 -07:00
Daniel Imms a8405b86de Fix \u259B rendering
Fixes #3902
2022-07-13 02:59:56 -07:00
Daniel Imms 6b447951f1 Merge pull request #3897 from Tyriar/3892
Clear glyph pixels when all channels differ below a threshold
2022-07-11 12:06:33 -07:00
Daniel Imms 788f248de4 Merge pull request #3896 from Tyriar/3877
Clip pixels to left of cell when background color changes
2022-07-11 11:43:29 -07:00
Daniel Imms 9336d7f776 Merge pull request #3900 from Tyriar/3477
Throw when setting an invalid cursorStyle
2022-07-11 11:42:59 -07:00
Daniel Imms 685822df0d Merge pull request #3899 from Tyriar/3898
Fix allowTransparency not working when set after ctor
2022-07-11 11:42:51 -07:00
Daniel Imms c26f605d4c Merge pull request #3895 from Tyriar/3894
Make corner box chars more round
2022-07-11 11:42:33 -07:00
Daniel Imms 0525f43b34 Merge pull request #3901 from Tyriar/3885_2
Be more defensive around SortedList key
2022-07-11 11:35:18 -07:00
Daniel Imms f61ff9fd46 Fix lint 2022-07-11 05:56:42 -07:00
Daniel Imms adcf841a39 Be more defensive around SortedList key
Fixes #3885
Part of microsoft/vscode#151225
2022-07-11 05:50:20 -07:00
Daniel Imms 71195b5370 Throw when setting an invalid cursorStyle
Fixes #3477
2022-07-10 11:43:45 -07:00
Daniel Imms e2b8002d4a Fix allowTransparency not working when set after ctor
Fixes #3898
2022-07-10 08:54:56 -07:00
Daniel Imms ea79194925 Make the clear threshold relative to fg/bg difference 2022-07-10 08:49:09 -07:00
Daniel Imms 89b320a5e2 Clear glyph pixels when all channels differ < 35
Fixes #3892
Also helps with #3877
2022-07-10 07:22:33 -07:00
Daniel Imms 5e8133b4be Clip pixels to left of cell when background color changes
Fixes #3877
2022-07-10 06:54:39 -07:00
Daniel Imms 4cbfd86db0 Make corder box chars more round
Fixes #3894
2022-07-10 05:44:44 -07:00
Daniel Imms b6885a67e7 Merge pull request #3893 from xtermjs/Tyriar-patch-1
Whitespace change to trigger webgl release
2022-07-08 12:53:47 -07:00
Daniel Imms 9f3e3f1700 Comment change to trigger webgl release 2022-07-08 12:53:25 -07:00
Daniel Imms 8f6e473aa7 Whitespace change to trigger webgl release 2022-07-08 12:51:37 -07:00
Daniel Imms 683f85f93d Merge pull request #3891 from sbatten/sbatten/powerline-circles
add powerline semi circle glyphs
2022-07-06 19:01:55 -07:00
Daniel Imms 8959d6ae1d Fix indentation 2022-07-06 18:52:24 -07:00
Megan Rogge 17b1eed0d2 Update src/browser/renderer/CustomGlyphs.ts 2022-07-06 21:50:09 -04:00
Megan Rogge 72eb7e245d Update src/browser/renderer/CustomGlyphs.ts 2022-07-06 21:49:38 -04:00
Daniel Imms 28108fbe2a Add semi-circles to demo 2022-07-06 18:41:47 -07:00
Daniel Imms 83551eaf60 Remove horizontal padding for semi-circles 2022-07-06 18:39:31 -07:00
SteVen Batten 56a1a7613c add powerline semi circle glyphs 2022-07-06 17:13:47 -07:00
Daniel Imms c580b6bf4c Merge pull request #3890 from Tyriar/dispose_webgl
Dispose of webgl resources properly
2022-07-06 07:51:49 -07:00
Daniel Imms 54cbbf600d Dispose of webgl resources properly
Fixes #3889
2022-07-06 07:32:42 -07:00
Daniel Imms d441bd3e53 Merge pull request #3888 from Tyriar/3885
Prevent exception when deleting non-existent element
2022-07-06 07:23:38 -07:00
Daniel Imms 098b2252d0 Prevent exception when deleting non-existent element
Fixes #3885
2022-07-06 04:39:12 -07:00
Daniel Imms 915fa49ce3 Merge pull request #3883 from wuchihsu/add-go-ssh-web-client-to-list-of-xterm-users
Add Go SSH Web Client to list of xterm users
2022-07-05 06:03:17 -07:00
Daniel Imms aa7ff23929 Merge branch 'master' into add-go-ssh-web-client-to-list-of-xterm-users 2022-07-05 05:53:49 -07:00
Daniel Imms 1102f5a4d0 Merge pull request #3884 from web3os-org/master
Update README.md to add web3os to Real-world Uses
2022-07-05 05:53:06 -07:00
Jay Mathis 9aa5a2452a Update README.md
Added link to web3os under Real-world uses
2022-07-05 02:41:03 -05:00
wuchihsu edea440cd8 Add Go SSH Web Client to list of xterm users 2022-07-04 11:26:40 +08:00
Daniel Imms 28ef0c85be Merge pull request #3882 from jerch/fix_3881
fix return type declaration of proposeDimensions
2022-07-01 03:35:07 -07:00
Jörg Breitbart d05cfcd6a7 fix return type declaration of proposeDimensions 2022-07-01 00:22:45 +02:00
Simon Lamon a54426ccb8 extended colors 2022-06-04 12:39:43 +00:00
318 changed files with 22023 additions and 14901 deletions
-29
View File
@@ -1,29 +0,0 @@
FROM node:14
# Configure apt
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get -y install --no-install-recommends apt-utils 2>&1
# Verify git and process tools are installed
RUN apt-get install -y git procps
# Install yarn, puppeteer deps
RUN apt-get install -y curl apt-transport-https lsb-release \
&& curl -sS https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/pubkey.gpg | apt-key add - 2>/dev/null \
&& echo "deb https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
&& apt-get update \
&& apt-get -y install --no-install-recommends \
yarn fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \
# https://github.com/Googlechrome/puppeteer/issues/290#issuecomment-322921352
gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \
libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \
libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \
libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
# Clean up
RUN apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*
ENV DEBIAN_FRONTEND=dialog
+17 -7
View File
@@ -1,10 +1,20 @@
{
"name": "xterm.js",
"dockerFile": "Dockerfile",
"appPort": 3000,
"extensions": [
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig",
"hbenl.vscode-mocha-test-adapter"
]
"image": "mcr.microsoft.com/devcontainers/typescript-node:0-18-buster",
"features": {
"ghcr.io/devcontainers/features/node:1": {} // yarn
},
"forwardPorts": [
3000
],
"postCreateCommand": "yarn install",
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig",
"hbenl.vscode-mocha-test-adapter"
]
}
}
}
+85 -6
View File
@@ -7,15 +7,20 @@
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": [
"demo/tsconfig.json",
"src/browser/tsconfig.json",
"src/common/tsconfig.json",
"src/headless/tsconfig.json",
"test/api/tsconfig.json",
"test/benchmark/tsconfig.json",
"test/playwright/tsconfig.json",
"addons/xterm-addon-attach/src/tsconfig.json",
"addons/xterm-addon-attach/test/tsconfig.json",
"addons/xterm-addon-canvas/src/tsconfig.json",
"addons/xterm-addon-fit/src/tsconfig.json",
"addons/xterm-addon-fit/test/tsconfig.json",
"addons/xterm-addon-image/src/tsconfig.json",
"addons/xterm-addon-image/test/tsconfig.json",
"addons/xterm-addon-ligatures/src/tsconfig.json",
"addons/xterm-addon-search/src/tsconfig.json",
"addons/xterm-addon-search/test/tsconfig.json",
@@ -32,12 +37,14 @@
"sourceType": "module"
},
"ignorePatterns": [
"**/inwasm-sdks/*",
"**/typings/*.d.ts",
"**/node_modules",
"**/*.js"
],
"plugins": [
"@typescript-eslint"
"@typescript-eslint",
"jsdoc"
],
"rules": {
"no-extra-semi": "error",
@@ -84,7 +91,12 @@
],
"@typescript-eslint/naming-convention": [
"warn",
{ "selector": "default", "format": ["camelCase"] },
{ "selector": "default", "format": ["camelCase"],
"filter": {
"regex": "^[a-z]",
"match": true
}
},
// variableLike
{ "selector": "variable", "format": ["camelCase", "UPPER_CASE"] },
{ "selector": "variable", "filter": "^I.+Service$", "format": ["PascalCase"], "prefix": ["I"] },
@@ -93,12 +105,34 @@
{ "selector": "memberLike", "modifiers": ["protected"], "format": ["camelCase"], "leadingUnderscore": "require" },
{ "selector": "enumMember", "format": ["UPPER_CASE"] },
// memberLike - Allow enum-like objects to use UPPER_CASE
{ "selector": "property", "modifiers": ["public"], "format": ["camelCase", "UPPER_CASE"] },
{ "selector": "method", "modifiers": ["public"], "format": ["camelCase", "UPPER_CASE"] },
{ "selector": "property", "modifiers": ["public"], "format": ["camelCase", "UPPER_CASE"],
"filter": {
"regex": "^[a-z]",
"match": true
}
},
// restrict on* naming for events only
{ "selector": "method", "modifiers": ["public"], "format": ["camelCase", "UPPER_CASE"], "custom": {
"regex": "^on[A-Z].+",
"match": false
} },
{ "selector": "method", "modifiers": ["private"], "format": ["camelCase"], "leadingUnderscore": "require", "custom": {
"regex": "^on[A-Z].+",
"match": false
} },
{ "selector": "method", "modifiers": ["protected"], "format": ["camelCase"], "leadingUnderscore": "require", "custom": {
"regex": "^on[A-Z].+",
"match": false
} },
// typeLike
{ "selector": "typeLike", "format": ["PascalCase"] },
{ "selector": "interface", "format": ["PascalCase"], "prefix": ["I"] }
],
"@typescript-eslint/no-confusing-void-expression": [
"warn",
{ "ignoreArrowShorthand": true }
],
"@typescript-eslint/no-useless-constructor": "warn",
"@typescript-eslint/prefer-namespace-keyword": "warn",
"@typescript-eslint/type-annotation-spacing": "warn",
"@typescript-eslint/quotes": [
@@ -127,7 +161,20 @@
"warn",
"always"
],
"jsdoc/check-alignment": 1,
"jsdoc/check-param-names": 1,
"jsdoc/no-multi-asterisks": 1,
"keyword-spacing": "warn",
"max-len": [
"warn",
{
"code": 1000, // Don't enforce for code
"comments": 100,
"ignoreTrailingComments": true,
"ignoreUrls": true,
"ignorePattern": "^ *((?<vt_comment>(//|\\*) @vt)|(?<table_comment>\\* \\| )|(?<commented_code>// ))"
}
],
"new-parens": "warn",
"no-duplicate-imports": "warn",
"no-else-return": [
@@ -146,8 +193,35 @@
]
}
],
"no-restricted-syntax": [
"warn",
{
"selector": "CallExpression[callee.name='requestAnimationFrame']",
"message": "The global requestAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService."
},
{
"selector": "CallExpression[callee.name='cancelAnimationFrame']",
"message": "The global cancelAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService."
},
{
"selector": "CallExpression > MemberExpression[object.name='window'][property.name='requestAnimationFrame']",
"message": "window.requestAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService."
},
{
"selector": "CallExpression > MemberExpression[object.name='window'][property.name='cancelAnimationFrame']",
"message": "window.cancelAnimationFrame() should be avoided, call it on the parent window from ICoreBrowserService."
},
{
"selector": "MemberExpression[object.name='window'][property.name='devicePixelRatio']",
"message": "window.devicePixelRatio should be avoided, get it from ICoreBrowserService."
}
],
"no-trailing-spaces": "warn",
"no-unsafe-finally": "warn",
"no-unused-vars": ["warn", {
"vars": "all",
"args": "none"
}],
"no-var": "warn",
"one-var": [
"warn",
@@ -169,9 +243,14 @@
},
"overrides": [
{
"files": ["**/*.test.ts"],
"files": [
"**/*.api.ts",
"**/*.test.ts"
],
"rules": {
"object-curly-spacing": "off"
"object-curly-spacing": "off",
"max-len": "off",
"no-unused-vars": "off"
}
}
]
+104
View File
@@ -0,0 +1,104 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint",
"jsdoc"
],
"rules": {
"no-extra-semi": "error",
"@typescript-eslint/array-type": [
"warn",
{
"default": "array",
"readonly": "generic"
}
],
"@typescript-eslint/explicit-function-return-type": [
"warn",
{
"allowExpressions": true
}
],
"@typescript-eslint/indent": [
"warn",
2
],
"@typescript-eslint/member-delimiter-style": [
"warn",
{
"multiline": {
"delimiter": "semi",
"requireLast": true
},
"singleline": {
"delimiter": "comma",
"requireLast": false
}
}
],
"@typescript-eslint/naming-convention": [
"warn",
{ "selector": "typeLike", "format": ["PascalCase"] },
{ "selector": "interface", "format": ["PascalCase"], "prefix": ["I"] }
],
"@typescript-eslint/prefer-namespace-keyword": "warn",
"@typescript-eslint/type-annotation-spacing": "warn",
"@typescript-eslint/quotes": [
"warn",
"single",
{ "allowTemplateLiterals": true }
],
"@typescript-eslint/semi": [
"warn",
"always"
],
"comma-dangle": [
"warn",
{
"objects": "never",
"arrays": "never",
"functions": "never"
}
],
"curly": [
"warn",
"multi-line"
],
"eol-last": "warn",
"eqeqeq": [
"warn",
"always"
],
"jsdoc/check-alignment": 1,
"jsdoc/check-param-names": 1,
"keyword-spacing": "warn",
"max-len": [
"warn",
{
"code": 1000, // Don't enforce for code
"comments": 80,
"ignoreUrls": true,
"ignorePattern": "^ *(?<ps_description>\\* Ps=)"
}
],
"no-irregular-whitespace": "warn",
"no-trailing-spaces": "warn",
"object-curly-spacing": [
"warn",
"always"
],
"spaced-comment": [
"warn",
"always",
{
"markers": ["/"],
"exceptions": ["-"]
}
]
}
}
+6 -3
View File
@@ -1,5 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Question
url: https://stackoverflow.com/questions/tagged/xtermjs
about: Please ask and answer questions here.
- name: FAQ
url: https://github.com/xtermjs/xterm.js/wiki/FAQ
about: See our frequently asked questions before filing a bug report
- name: Support / Q&A
url: https://github.com/xtermjs/xterm.js/discussions/categories/q-a
about: Use GitHub Discussions for community support and general Q&A
+300
View File
@@ -0,0 +1,300 @@
name: CI
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'yarn'
- name: Install dependencies
run: yarn --frozen-lockfile
- name: Build
run: yarn setup
- name: Zip artifacts
run: |
zip -r compressed-build \
./out/* \
./out-test/* \
./addons/xterm-addon-attach/out/* \
./addons/xterm-addon-attach/out-test/* \
./addons/xterm-addon-canvas/out/* \
./addons/xterm-addon-canvas/out-test/* \
./addons/xterm-addon-fit/out/* \
./addons/xterm-addon-fit/out-test/* \
./addons/xterm-addon-image/out/* \
./addons/xterm-addon-image/out-test/* \
./addons/xterm-addon-ligatures/out/* \
./addons/xterm-addon-ligatures/out-test/* \
./addons/xterm-addon-search/out/* \
./addons/xterm-addon-search/out-test/* \
./addons/xterm-addon-serialize/out/* \
./addons/xterm-addon-serialize/out-test/* \
./addons/xterm-addon-unicode11/out/* \
./addons/xterm-addon-unicode11/out-test/* \
./addons/xterm-addon-web-links/out/* \
./addons/xterm-addon-web-links/out-test/* \
./addons/xterm-addon-webgl/out/* \
./addons/xterm-addon-webgl/out-test/*
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: build-artifacts
path: compressed-build.zip
if-no-files-found: error
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'yarn'
- name: Install dependencies
run: |
yarn --frozen-lockfile
yarn install-addons
- name: Lint code
run: yarn lint
- name: Lint API
run: yarn lint-api
test-unit-coverage:
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'yarn'
- name: Install dependencies
run: |
yarn --frozen-lockfile
yarn install-addons
- uses: actions/download-artifact@v3
with:
name: build-artifacts
- name: Unzip artifacts
shell: bash
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
pwsh -Command "7z x compressed-build.zip -aoa -o${{ github.workspace }}"
else
unzip -o compressed-build.zip
fi
ls -R
- name: Unit test coverage
run: |
yarn test-unit-coverage --forbid-only
EXIT_CODE=$?
./node_modules/.bin/nyc report --reporter=cobertura
exit $EXIT_CODE
test-unit-parallel:
timeout-minutes: 20
strategy:
matrix:
node-version: [18]
runs-on: [ubuntu, macos, windows]
runs-on: ${{ matrix.runs-on }}-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}.x
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}.x
cache: 'yarn'
- name: Install dependencies
run: |
yarn --frozen-lockfile
yarn install-addons
- name: Wait for build job
uses: NathanFirmo/wait-for-other-job@v1.1.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
job: build
- uses: actions/download-artifact@v3
with:
name: build-artifacts
- name: Unzip artifacts
shell: bash
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
pwsh -Command "7z x compressed-build.zip -aoa -o${{ github.workspace }}"
else
unzip -o compressed-build.zip
fi
ls -R
- name: Unit tests
run: yarn test-unit --forbid-only
test-unit:
needs: build
timeout-minutes: 20
strategy:
matrix:
node-version: [16]
runs-on: [ubuntu, macos, windows]
runs-on: ${{ matrix.runs-on }}-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}.x
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}.x
cache: 'yarn'
- name: Install dependencies
run: |
yarn --frozen-lockfile
yarn install-addons
- uses: actions/download-artifact@v3
with:
name: build-artifacts
- name: Unzip artifacts
shell: bash
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
pwsh -Command "7z x compressed-build.zip -aoa -o${{ github.workspace }}"
else
unzip -o compressed-build.zip
fi
ls -R
- name: Unit tests
run: yarn test-unit --forbid-only
test-api-parallel:
timeout-minutes: 20
strategy:
matrix:
node-version: [18] # just one as integration tests are about testing in browser
runs-on: [ubuntu] # macos is flaky
browser: [chromium, firefox]
runs-on: ${{ matrix.runs-on }}-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}.x
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}.x
cache: 'yarn'
- name: Install dependencies
run: |
yarn --frozen-lockfile
yarn install-addons
- name: Install playwright
run: npx playwright install
- name: Wait for build job
uses: NathanFirmo/wait-for-other-job@v1.1.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
job: build
- uses: actions/download-artifact@v3
with:
name: build-artifacts
- name: Unzip artifacts
shell: bash
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
pwsh -Command "7z x compressed-build.zip -aoa -o${{ github.workspace }}"
else
unzip -o compressed-build.zip
fi
ls -R
- name: Integration tests (${{ matrix.browser }})
run: yarn test-api-${{ matrix.browser }} --headless --forbid-only
test-playwright-parallel:
timeout-minutes: 20
strategy:
matrix:
node-version: [18] # just one as integration tests are about testing in browser
runs-on: [ubuntu] # macos is flaky
browser: [chromium, firefox, webkit]
runs-on: ${{ matrix.runs-on }}-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}.x
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}.x
cache: 'yarn'
- name: Install dependencies
run: |
yarn --frozen-lockfile
yarn install-addons
- name: Install playwright
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Wait for build job
uses: NathanFirmo/wait-for-other-job@v1.1.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
job: build
- uses: actions/download-artifact@v3
with:
name: build-artifacts
- name: Unzip artifacts
shell: bash
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
pwsh -Command "7z x compressed-build.zip -aoa -o${{ github.workspace }}"
else
unzip -o compressed-build.zip
fi
ls -R
- name: Build demo
run: yarn build-demo
- name: Integration tests
run: yarn test-playwright-${{ matrix.browser }} --forbid-only
test-api:
needs: build
timeout-minutes: 20
strategy:
matrix:
node-version: [18] # just one as integration tests are about testing in browser
runs-on: [windows] # macos is flaky
browser: [chromium, firefox]
runs-on: ${{ matrix.runs-on }}-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}.x
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}.x
cache: 'yarn'
- name: Install dependencies
run: |
yarn --frozen-lockfile
yarn install-addons
- name: Install playwright
run: npx playwright install
- uses: actions/download-artifact@v3
with:
name: build-artifacts
- name: Unzip artifacts
shell: bash
run: |
if [ "$RUNNER_OS" == "Windows" ]; then
pwsh -Command "7z x compressed-build.zip -aoa -o${{ github.workspace }}"
else
unzip -o compressed-build.zip
fi
ls -R
- name: Integration tests (${{ matrix.browser }})
run: yarn test-api-${{ matrix.browser }} --headless --forbid-only
+35
View File
@@ -0,0 +1,35 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: '41 17 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+30
View File
@@ -0,0 +1,30 @@
name: Release
on:
push:
# If a commit reaches master, assume it has passed CI via PR and publish
# without running tests to save time to publish
branches: [ "master" ]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'yarn'
- name: Install dependencies
run: yarn --frozen-lockfile
- name: Build
run: yarn setup
- name: Package headless
run: |
yarn package-headless
node ./bin/package_headless.js
- name: Publish to npm
env:
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: node ./bin/publish.js
-6
View File
@@ -1,11 +1,5 @@
require:
- source-map-support/register
spec:
- out/**/*.test.js
- addons/**/out/*.test.js
watch-files:
- out/**/*.js
- addons/**/out/*.js
reporter: spec
color: true
check-leaks: true
+1
View File
@@ -0,0 +1 @@
16
+14 -3
View File
@@ -1,5 +1,16 @@
{
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.quoteStyle": "single",
"mochaExplorer.envPath": ".mocha.env"
"files.associations": {
".eslintrc.json.typings": "jsonc"
},
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.quoteStyle": "single",
"mochaExplorer.envPath": ".mocha.env",
"mochaExplorer.files": [
"out/**/*.test.js",
"addons/**/out/*.test.js"
],
"mochaExplorer.watch": [
"out/**/*.js",
"addons/**/out/*.js"
]
}
+1 -2
View File
@@ -32,7 +32,6 @@
"label": "start",
"type": "npm",
"script": "start",
"dependsOn": "watch",
"group": "build",
"isBackground": true,
"problemMatcher": [],
@@ -42,7 +41,7 @@
},
{
"label": "Start demo",
"dependsOn": "start",
"dependsOn": ["start", "watch"],
"group": {
"kind": "build",
"isDefault": true
+4
View File
@@ -28,6 +28,10 @@ opening an issue, read these pointers.
- Be polite. Issues with an indignant or belligerent tone tend to be moved to the
bottom of the pile.
## Answering discussion questions
Issues are only meant to track (likely) feature requests and bugs. We use [GitHub Discussions](https://github.com/xtermjs/xterm.js/discussions) for general Q&A as well as discussing possible features. If you want to help out, many questions get asked over at the [discussions page](https://github.com/xtermjs/xterm.js/discussions) which could use an expert as the core team is often stretched thin.
## Contributing code
You can find issues to work on by looking at the [help wanted](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) or [good first issue](https://github.com/xtermjs/xterm.js/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) issues. It's a good idea to comment on the issue saying that you're taking it, just in case someone else comes along and you duplicate work. Once you have your issue, here are the steps to contribute:
+32 -27
View File
@@ -1,7 +1,5 @@
# [![xterm.js logo](logo-full.png)](https://xtermjs.org)
[![Build Status](https://dev.azure.com/xtermjs/xterm.js/_apis/build/status/xtermjs.xterm.js)](https://dev.azure.com/xtermjs/xterm.js/_build/latest?definitionId=3)
Xterm.js is a front-end component written in TypeScript that lets applications bring fully-featured terminals to their users in the browser. It's used by popular projects such as VS Code, Hyper and Theia.
## Features
@@ -87,8 +85,6 @@ The xterm.js team maintains the following addons, but anyone can build them:
Since xterm.js is typically implemented as a developer tool, only modern browsers are supported officially. Specifically the latest versions of *Chrome*, *Edge*, *Firefox*, and *Safari*.
We also partially support *Internet Explorer 11*, meaning xterm.js should work for the most part, but we reserve the right to not provide workarounds specifically for it unless it's absolutely necessary to get the basic input/output flow working.
Xterm.js works seamlessly in [Electron](https://electronjs.org/) apps and may even work on earlier versions of the browsers. These are the versions we strive to keep working.
### Node.js Support
@@ -101,6 +97,26 @@ The full API for xterm.js is contained within the [TypeScript declaration file](
Note that some APIs are marked *experimental*, these are added to enable experimentation with new ideas without committing to support it like a normal [semver](https://semver.org/) API. Note that these APIs can change radically between versions, so be sure to read release notes if you plan on using experimental APIs.
## Releases
Xterm.js follows a monthly release cycle roughly.
All current and past releases are available on this repo's [Releases page](https://github.com/sourcelair/xterm.js/releases), you can view the [high-level roadmap on the wiki](https://github.com/xtermjs/xterm.js/wiki/Roadmap) and see what we're working on now by looking through [Milestones](https://github.com/sourcelair/xterm.js/milestones).
### Beta builds
Our CI releases beta builds to npm for every change that goes into master. Install the latest beta build with:
```bash
npm install -S xterm@beta
```
These should generally be stable, but some bugs may slip in. We recommend using the beta build primarily to test out new features and to verify bug fixes.
## Contributing
You can read the [guide on the wiki](https://github.com/xtermjs/xterm.js/wiki/Contributing) to learn how to contribute and set up xterm.js for development.
## Real-world uses
Xterm.js is used in several world-class applications to provide great terminal experiences.
@@ -142,7 +158,7 @@ Xterm.js is used in several world-class applications to provide great terminal e
- [**Fluent Terminal**](https://github.com/felixse/FluentTerminal): A terminal emulator based on UWP and web technologies.
- [**Hyper**](https://hyper.is): A terminal built on web technologies.
- [**Diag**](https://diag.ai): A better way to troubleshoot problems faster. Capture, share and reapply troubleshooting knowledge so you can focus on solving problems that matter.
- [**GoTTY**](https://github.com/yudai/gotty): A simple command line tool that shares your terminal as a web application based on xterm.js.
- [**GoTTY**](https://github.com/sorenisanerd/gotty): A simple command line tool that shares your terminal as a web application based on xterm.js.
- [**genact**](https://github.com/svenstaro/genact): A nonsense activity generator.
- [**cPanel & WHM**](https://cpanel.com): The hosting platform of choice.
- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js.
@@ -168,7 +184,7 @@ Xterm.js is used in several world-class applications to provide great terminal e
- [**DockerStacks**](https://docker-stacks.com/): Local LAMP/LEMP development studio
- [**Codecademy**](https://codecademy.com/): Uses xterm.js in its courses on Bash.
- [**Laravel Ssh Web Client**](https://github.com/roke22/Laravel-ssh-client): Laravel server inventory with ssh web client to connect at server using xterm.js
- [**Repl.it**](https://repl.it): Collaborative browser based IDE with support for 50+ different languages.
- [**Replit**](https://replit.com): Collaborative browser based IDE with support for 50+ different languages.
- [**TeleType**](https://github.com/akshaykmr/TeleType): cli tool that allows you to share your terminal online conveniently. Show off mad cli-fu, help a colleague, teach, or troubleshoot.
- [**Intervue**](https://www.intervue.io): Pair programming for interviews. Multiple programming languages are supported, with results displayed by xterm.js.
- [**TRASA**](https://trasa.io): Zero trust access to Web, SSH, RDP, and Database services.
@@ -192,34 +208,23 @@ Xterm.js is used in several world-class applications to provide great terminal e
- [**hack.courses**](https://hack.courses): Interactive Linux and command-line classes using xterm.js to expose a real terminal available for everyone.
- [**Render**](https://render.com): Platform-as-a-service for your apps, websites, and databases using xterm.js to provide a command prompt for user containers and for streaming build and runtime logs.
- [**CloudTTY**](https://github.com/cloudtty/cloudtty): A Friendly Kubernetes CloudShell (Web Terminal).
- [**Go SSH Web Client**](https://github.com/wuchihsu/go-ssh-web-client): A simple SSH web client using Go, WebSocket and Xterm.js.
- [**web3os**](https://web3os.sh): A decentralized operating system for the next web
- [**Cratecode**](https://cratecode.com): Learn to program for free through interactive online lessons. Cratecode uses xterm.js to give users access to their own Linux environment.
- [**Super Terminal**](https://github.com/bugwheels94/super-terminal): It is a http based terminal for developers who dont like repetition and save time.
- [**graSSHopper**](https://grasshopper.coding.kiwi): A simple SSH client with file explorer, history and many more features.
- [**DomTerm**](https://domterm.org/xtermjs.html): Tiles and tabs. Detachable sessions (like tmux). [Remote connections](https://domterm.org/Remoting-over-ssh.html) using a nice ssh wrapper with predictive echo. Qt, Electron, Tauri/Wry, or desktop browser front-ends. Choose between xterm.js engine (faster) or native DomTerm (more functionality and graphics) - or both.
- [**Cloudtutor.io**](https://cloudtutor.io): innovative online learning platform that offers users access to an interactive lab.
- [**Helix Editor Playground**](https://github.com/tomgroenwoldt/helix-editor-playground): Online playground for the terminal based helix editor.
- [**Coder**](https://github.com/coder/coder): Self-Hosted Remote Development Environments
- [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.
## Releases
Xterm.js follows a monthly release cycle roughly.
All current and past releases are available on this repo's [Releases page](https://github.com/sourcelair/xterm.js/releases), you can view the [high-level roadmap on the wiki](https://github.com/xtermjs/xterm.js/wiki/Roadmap) and see what we're working on now by looking through [Milestones](https://github.com/sourcelair/xterm.js/milestones).
### Beta builds
Our CI releases beta builds to npm for every change that goes into master. Install the latest beta build with:
```bash
npm install -S xterm@beta
```
These should generally be stable, but some bugs may slip in. We recommend using the beta build primarily to test out new features and to verify bug fixes.
## Contributing
You can read the [guide on the wiki](https://github.com/xtermjs/xterm.js/wiki/Contributing) to learn how to contribute and set up xterm.js for development.
## License Agreement
If you contribute code to this project, you implicitly allow your code to be distributed under the MIT license. You are also implicitly verifying that all code is your original work.
Copyright (c) 2017-2019, [The xterm.js authors](https://github.com/xtermjs/xterm.js/graphs/contributors) (MIT License)<br>
Copyright (c) 2017-2022, [The xterm.js authors](https://github.com/xtermjs/xterm.js/graphs/contributors) (MIT License)<br>
Copyright (c) 2014-2017, SourceLair, Private Company ([www.sourcelair.com](https://www.sourcelair.com/home)) (MIT License)<br>
Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-attach",
"version": "0.6.0",
"version": "0.9.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
@@ -21,6 +21,6 @@
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^4.0.0"
"xterm": "^5.0.0"
}
}
+18 -4
View File
@@ -47,16 +47,14 @@ export class AttachAddon implements ITerminalAddon {
}
private _sendData(data: string): void {
// TODO: do something better than just swallowing
// the data if the socket is not in a working condition
if (this._socket.readyState !== 1) {
if (!this._checkOpenSocket()) {
return;
}
this._socket.send(data);
}
private _sendBinary(data: string): void {
if (this._socket.readyState !== 1) {
if (!this._checkOpenSocket()) {
return;
}
const buffer = new Uint8Array(data.length);
@@ -65,6 +63,22 @@ export class AttachAddon implements ITerminalAddon {
}
this._socket.send(buffer);
}
private _checkOpenSocket(): boolean {
switch (this._socket.readyState) {
case WebSocket.OPEN:
return true;
case WebSocket.CONNECTING:
throw new Error('Attach addon was loaded before socket was open');
case WebSocket.CLOSING:
console.warn('Attach addon socket is closing');
return false;
case WebSocket.CLOSED:
throw new Error('Attach addon socket is closed');
default:
throw new Error('Unexpected socket state');
}
}
}
function addSocketListener<K extends keyof WebSocketEventMap>(socket: WebSocket, type: K, handler: (this: WebSocket, ev: WebSocketEventMap[K]) => any): IDisposable {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"target": "es2015",
"lib": [
"dom",
"es2015"
@@ -5,7 +5,7 @@
import WebSocket = require('ws');
import { openTerminal, pollFor, launchBrowser } from '../../../out-test/api/TestUtils';
import { Browser, Page } from 'playwright';
import { Browser, Page } from '@playwright/test';
const APP = 'http://127.0.0.1:3001/test';
@@ -28,7 +28,7 @@ describe('AttachAddon', () => {
beforeEach(async () => await page.goto(APP));
it('string', async function(): Promise<any> {
await openTerminal(page, { rendererType: 'dom' });
await openTerminal(page);
const port = 8080;
const server = new WebSocket.Server({ port });
server.on('connection', socket => socket.send('foo'));
@@ -38,7 +38,7 @@ describe('AttachAddon', () => {
});
it('utf8', async function(): Promise<any> {
await openTerminal(page, { rendererType: 'dom' });
await openTerminal(page);
const port = 8080;
const server = new WebSocket.Server({ port });
const data = new Uint8Array([102, 111, 111]);
+1 -1
View File
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"target": "es2015",
"lib": [
"es2015"
],
+1 -1
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { Terminal, ILinkMatcherOptions, ITerminalAddon } from 'xterm';
import { Terminal, ITerminalAddon } from 'xterm';
declare module 'xterm-addon-attach' {
export interface IAttachOptions {
+2
View File
@@ -0,0 +1,2 @@
lib
node_modules
+29
View File
@@ -0,0 +1,29 @@
# Blacklist - exclude everything except npm defaults such as LICENSE, etc
*
!*/
# Whitelist - lib/
!lib/**/*.d.ts
!lib/**/*.js
!lib/**/*.js.map
!lib/**/*.css
# Whitelist - src/
!src/**/*.ts
!src/**/*.d.ts
!src/**/*.js
!src/**/*.js.map
!src/**/*.css
# Blacklist - src/ test files
src/**/*.test.ts
src/**/*.test.d.ts
src/**/*.test.js
src/**/*.test.js.map
# Whitelist - typings/
!typings/*.d.ts
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+28
View File
@@ -0,0 +1,28 @@
## xterm-addon-canvas
An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables a canvas-based renderer using a 2d context to draw. This addon requires xterm.js v5+.
The purpose of this addon is to be used as a fallback for the [webgl addon](https://www.npmjs.com/package/xterm-addon-webgl) when better performance is desired over the default DOM renderer, but WebGL2 isn't supported or performant for some reason.
### Install
```bash
npm install --save xterm-addon-canvas
```
### Usage
```ts
import { Terminal } from 'xterm';
import { CanvasAddon } from 'xterm-addon-canvas';
const terminal = new Terminal();
terminal.open(element);
terminal.loadAddon(new CanvasAddon());
```
See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/xterm-addon-canvas/typings/xterm-addon-canvas.d.ts) for more advanced usage.
### See also
- [xterm-addon-webgl](https://www.npmjs.com/package/xterm-addon-webgl) A renderer for xterm.js that uses WebGL
+27
View File
@@ -0,0 +1,27 @@
{
"name": "xterm-addon-canvas",
"version": "0.5.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
},
"main": "lib/xterm-addon-canvas.js",
"types": "typings/xterm-addon-canvas.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"keywords": [
"terminal",
"canvas",
"xterm",
"xterm.js"
],
"scripts": {
"build": "../../node_modules/.bin/tsc -p .",
"prepackage": "npm run build",
"package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^5.0.0"
}
}
@@ -0,0 +1,499 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ReadonlyColorSet } from 'browser/Types';
import { CellColorResolver } from 'browser/renderer/shared/CellColorResolver';
import { acquireTextureAtlas } from 'browser/renderer/shared/CharAtlasCache';
import { TEXT_BASELINE } from 'browser/renderer/shared/Constants';
import { tryDrawCustomChar } from 'browser/renderer/shared/CustomGlyphs';
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel';
import { IRasterizedGlyph, IRenderDimensions, ISelectionRenderModel, ITextureAtlas } from 'browser/renderer/shared/Types';
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
import { isSafari } from 'common/Platform';
import { ICellData } from 'common/Types';
import { CellData } from 'common/buffer/CellData';
import { WHITESPACE_CELL_CODE } from 'common/buffer/Constants';
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
import { Terminal } from 'xterm';
import { IRenderLayer } from './Types';
export abstract class BaseRenderLayer extends Disposable implements IRenderLayer {
private _canvas: HTMLCanvasElement;
protected _ctx!: CanvasRenderingContext2D;
private _deviceCharWidth: number = 0;
private _deviceCharHeight: number = 0;
private _deviceCellWidth: number = 0;
private _deviceCellHeight: number = 0;
private _deviceCharLeft: number = 0;
private _deviceCharTop: number = 0;
protected _selectionModel: ISelectionRenderModel = createSelectionRenderModel();
private _cellColorResolver: CellColorResolver;
private _bitmapGenerator: (BitmapGenerator | undefined)[] = [];
protected _charAtlas!: ITextureAtlas;
protected _charAtlasDisposable = this.register(new MutableDisposable());
public get canvas(): HTMLCanvasElement { return this._canvas; }
public get cacheCanvas(): HTMLCanvasElement { return this._charAtlas?.pages[0].canvas!; }
private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter<HTMLCanvasElement>());
public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event;
constructor(
private readonly _terminal: Terminal,
private _container: HTMLElement,
id: string,
zIndex: number,
private _alpha: boolean,
protected readonly _themeService: IThemeService,
protected readonly _bufferService: IBufferService,
protected readonly _optionsService: IOptionsService,
protected readonly _decorationService: IDecorationService,
protected readonly _coreBrowserService: ICoreBrowserService
) {
super();
this._cellColorResolver = new CellColorResolver(this._terminal, this._selectionModel, this._decorationService, this._coreBrowserService, this._themeService);
this._canvas = document.createElement('canvas');
this._canvas.classList.add(`xterm-${id}-layer`);
this._canvas.style.zIndex = zIndex.toString();
this._initCanvas();
this._container.appendChild(this._canvas);
this._refreshCharAtlas(this._themeService.colors);
this.register(this._themeService.onChangeColors(e => {
this._refreshCharAtlas(e);
this.reset();
// Trigger selection changed as it's handled separately to regular rendering
this.handleSelectionChanged(this._selectionModel.selectionStart, this._selectionModel.selectionEnd, this._selectionModel.columnSelectMode);
}));
this.register(toDisposable(() => {
this._canvas.remove();
}));
}
private _initCanvas(): void {
this._ctx = throwIfFalsy(this._canvas.getContext('2d', { alpha: this._alpha }));
// Draw the background if this is an opaque layer
if (!this._alpha) {
this._clearAll();
}
}
public handleBlur(): void {}
public handleFocus(): void {}
public handleCursorMove(): void {}
public handleGridChanged(startRow: number, endRow: number): void {}
public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {
this._selectionModel.update(this._terminal, start, end, columnSelectMode);
}
protected _setTransparency(alpha: boolean): void {
// Do nothing when alpha doesn't change
if (alpha === this._alpha) {
return;
}
// Create new canvas and replace old one
const oldCanvas = this._canvas;
this._alpha = alpha;
// Cloning preserves properties
this._canvas = this._canvas.cloneNode() as HTMLCanvasElement;
this._initCanvas();
this._container.replaceChild(this._canvas, oldCanvas);
// Regenerate char atlas and force a full redraw
this._refreshCharAtlas(this._themeService.colors);
this.handleGridChanged(0, this._bufferService.rows - 1);
}
/**
* Refreshes the char atlas, aquiring a new one if necessary.
* @param colorSet The color set to use for the char atlas.
*/
private _refreshCharAtlas(colorSet: ReadonlyColorSet): void {
if (this._deviceCharWidth <= 0 && this._deviceCharHeight <= 0) {
return;
}
this._charAtlas = acquireTextureAtlas(this._terminal, this._optionsService.rawOptions, colorSet, this._deviceCellWidth, this._deviceCellHeight, this._deviceCharWidth, this._deviceCharHeight, this._coreBrowserService.dpr);
this._charAtlasDisposable.value = forwardEvent(this._charAtlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas);
this._charAtlas.warmUp();
for (let i = 0; i < this._charAtlas.pages.length; i++) {
this._bitmapGenerator[i] = new BitmapGenerator(this._charAtlas.pages[i].canvas);
}
}
public resize(dim: IRenderDimensions): void {
this._deviceCellWidth = dim.device.cell.width;
this._deviceCellHeight = dim.device.cell.height;
this._deviceCharWidth = dim.device.char.width;
this._deviceCharHeight = dim.device.char.height;
this._deviceCharLeft = dim.device.char.left;
this._deviceCharTop = dim.device.char.top;
this._canvas.width = dim.device.canvas.width;
this._canvas.height = dim.device.canvas.height;
this._canvas.style.width = `${dim.css.canvas.width}px`;
this._canvas.style.height = `${dim.css.canvas.height}px`;
// Draw the background if this is an opaque layer
if (!this._alpha) {
this._clearAll();
}
this._refreshCharAtlas(this._themeService.colors);
}
public abstract reset(): void;
public clearTextureAtlas(): void {
this._charAtlas?.clearTexture();
}
/**
* Fills 1+ cells completely. This uses the existing fillStyle on the context.
* @param x The column to start at.
* @param y The row to start at
* @param width The number of columns to fill.
* @param height The number of rows to fill.
*/
protected _fillCells(x: number, y: number, width: number, height: number): void {
this._ctx.fillRect(
x * this._deviceCellWidth,
y * this._deviceCellHeight,
width * this._deviceCellWidth,
height * this._deviceCellHeight);
}
/**
* Fills a 1px line (2px on HDPI) at the middle of the cell. This uses the
* existing fillStyle on the context.
* @param x The column to fill.
* @param y The row to fill.
*/
protected _fillMiddleLineAtCells(x: number, y: number, width: number = 1): void {
const cellOffset = Math.ceil(this._deviceCellHeight * 0.5);
this._ctx.fillRect(
x * this._deviceCellWidth,
(y + 1) * this._deviceCellHeight - cellOffset - this._coreBrowserService.dpr,
width * this._deviceCellWidth,
this._coreBrowserService.dpr);
}
/**
* Fills a 1px line (2px on HDPI) at the bottom of the cell. This uses the
* existing fillStyle on the context.
* @param x The column to fill.
* @param y The row to fill.
*/
protected _fillBottomLineAtCells(x: number, y: number, width: number = 1, pixelOffset: number = 0): void {
this._ctx.fillRect(
x * this._deviceCellWidth,
(y + 1) * this._deviceCellHeight + pixelOffset - this._coreBrowserService.dpr - 1 /* Ensure it's drawn within the cell */,
width * this._deviceCellWidth,
this._coreBrowserService.dpr);
}
protected _curlyUnderlineAtCell(x: number, y: number, width: number = 1): void {
this._ctx.save();
this._ctx.beginPath();
this._ctx.strokeStyle = this._ctx.fillStyle;
const lineWidth = this._coreBrowserService.dpr;
this._ctx.lineWidth = lineWidth;
for (let xOffset = 0; xOffset < width; xOffset++) {
const xLeft = (x + xOffset) * this._deviceCellWidth;
const xMid = (x + xOffset + 0.5) * this._deviceCellWidth;
const xRight = (x + xOffset + 1) * this._deviceCellWidth;
const yMid = (y + 1) * this._deviceCellHeight - lineWidth - 1;
const yMidBot = yMid - lineWidth;
const yMidTop = yMid + lineWidth;
this._ctx.moveTo(xLeft, yMid);
this._ctx.bezierCurveTo(
xLeft, yMidBot,
xMid, yMidBot,
xMid, yMid
);
this._ctx.bezierCurveTo(
xMid, yMidTop,
xRight, yMidTop,
xRight, yMid
);
}
this._ctx.stroke();
this._ctx.restore();
}
protected _dottedUnderlineAtCell(x: number, y: number, width: number = 1): void {
this._ctx.save();
this._ctx.beginPath();
this._ctx.strokeStyle = this._ctx.fillStyle;
const lineWidth = this._coreBrowserService.dpr;
this._ctx.lineWidth = lineWidth;
this._ctx.setLineDash([lineWidth * 2, lineWidth]);
const xLeft = x * this._deviceCellWidth;
const yMid = (y + 1) * this._deviceCellHeight - lineWidth - 1;
this._ctx.moveTo(xLeft, yMid);
for (let xOffset = 0; xOffset < width; xOffset++) {
// const xLeft = x * this._deviceCellWidth;
const xRight = (x + width + xOffset) * this._deviceCellWidth;
this._ctx.lineTo(xRight, yMid);
}
this._ctx.stroke();
this._ctx.closePath();
this._ctx.restore();
}
protected _dashedUnderlineAtCell(x: number, y: number, width: number = 1): void {
this._ctx.save();
this._ctx.beginPath();
this._ctx.strokeStyle = this._ctx.fillStyle;
const lineWidth = this._coreBrowserService.dpr;
this._ctx.lineWidth = lineWidth;
this._ctx.setLineDash([lineWidth * 4, lineWidth * 3]);
const xLeft = x * this._deviceCellWidth;
const xRight = (x + width) * this._deviceCellWidth;
const yMid = (y + 1) * this._deviceCellHeight - lineWidth - 1;
this._ctx.moveTo(xLeft, yMid);
this._ctx.lineTo(xRight, yMid);
this._ctx.stroke();
this._ctx.closePath();
this._ctx.restore();
}
/**
* Fills a 1px line (2px on HDPI) at the left of the cell. This uses the
* existing fillStyle on the context.
* @param x The column to fill.
* @param y The row to fill.
*/
protected _fillLeftLineAtCell(x: number, y: number, width: number): void {
this._ctx.fillRect(
x * this._deviceCellWidth,
y * this._deviceCellHeight,
this._coreBrowserService.dpr * width,
this._deviceCellHeight);
}
/**
* Strokes a 1px rectangle (2px on HDPI) around a cell. This uses the existing
* strokeStyle on the context.
* @param x The column to fill.
* @param y The row to fill.
*/
protected _strokeRectAtCell(x: number, y: number, width: number, height: number): void {
const lineWidth = this._coreBrowserService.dpr;
this._ctx.lineWidth = lineWidth;
this._ctx.strokeRect(
x * this._deviceCellWidth + lineWidth / 2,
y * this._deviceCellHeight + (lineWidth / 2),
width * this._deviceCellWidth - lineWidth,
(height * this._deviceCellHeight) - lineWidth);
}
/**
* Clears the entire canvas.
*/
protected _clearAll(): void {
if (this._alpha) {
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
} else {
this._ctx.fillStyle = this._themeService.colors.background.css;
this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);
}
}
/**
* Clears 1+ cells completely.
* @param x The column to start at.
* @param y The row to start at.
* @param width The number of columns to clear.
* @param height The number of rows to clear.
*/
protected _clearCells(x: number, y: number, width: number, height: number): void {
if (this._alpha) {
this._ctx.clearRect(
x * this._deviceCellWidth,
y * this._deviceCellHeight,
width * this._deviceCellWidth,
height * this._deviceCellHeight);
} else {
this._ctx.fillStyle = this._themeService.colors.background.css;
this._ctx.fillRect(
x * this._deviceCellWidth,
y * this._deviceCellHeight,
width * this._deviceCellWidth,
height * this._deviceCellHeight);
}
}
/**
* Draws a truecolor character at the cell. The character will be clipped to
* ensure that it fits with the cell, including the cell to the right if it's
* a wide character. This uses the existing fillStyle on the context.
* @param cell The cell data for the character to draw.
* @param x The column to draw at.
* @param y The row to draw at.
*/
protected _fillCharTrueColor(cell: CellData, x: number, y: number): void {
this._ctx.font = this._getFont(false, false);
this._ctx.textBaseline = TEXT_BASELINE;
this._clipRow(y);
// Draw custom characters if applicable
let drawSuccess = false;
if (this._optionsService.rawOptions.customGlyphs !== false) {
drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._deviceCellWidth, y * this._deviceCellHeight, this._deviceCellWidth, this._deviceCellHeight, this._optionsService.rawOptions.fontSize, this._coreBrowserService.dpr);
}
// Draw the character
if (!drawSuccess) {
this._ctx.fillText(
cell.getChars(),
x * this._deviceCellWidth + this._deviceCharLeft,
y * this._deviceCellHeight + this._deviceCharTop + this._deviceCharHeight);
}
}
/**
* Draws one or more characters at a cell. If possible this will draw using
* the character atlas to reduce draw time.
*/
protected _drawChars(cell: ICellData, x: number, y: number): void {
const chars = cell.getChars();
this._cellColorResolver.resolve(cell, x, this._bufferService.buffer.ydisp + y);
if (!this._charAtlas) {
return;
}
let glyph: IRasterizedGlyph;
if (chars && chars.length > 1) {
glyph = this._charAtlas.getRasterizedGlyphCombinedChar(chars, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, true);
} else {
glyph = this._charAtlas.getRasterizedGlyph(cell.getCode() || WHITESPACE_CELL_CODE, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, true);
}
if (!glyph.size.x || !glyph.size.y) {
return;
}
this._ctx.save();
this._clipRow(y);
// Draw the image, use the bitmap if it's available
// HACK: If the canvas doesn't match, delete the generator. It's not clear how this happens but
// something is wrong with either the lifecycle of _bitmapGenerator or the page canvases are
// swapped out unexpectedly
if (this._bitmapGenerator[glyph.texturePage] && this._charAtlas.pages[glyph.texturePage].canvas !== this._bitmapGenerator[glyph.texturePage]!.canvas) {
this._bitmapGenerator[glyph.texturePage]?.bitmap?.close();
delete this._bitmapGenerator[glyph.texturePage];
}
if (this._charAtlas.pages[glyph.texturePage].version !== this._bitmapGenerator[glyph.texturePage]?.version) {
if (!this._bitmapGenerator[glyph.texturePage]) {
this._bitmapGenerator[glyph.texturePage] = new BitmapGenerator(this._charAtlas.pages[glyph.texturePage].canvas);
}
this._bitmapGenerator[glyph.texturePage]!.refresh();
this._bitmapGenerator[glyph.texturePage]!.version = this._charAtlas.pages[glyph.texturePage].version;
}
this._ctx.drawImage(
this._bitmapGenerator[glyph.texturePage]?.bitmap || this._charAtlas!.pages[glyph.texturePage].canvas,
glyph.texturePosition.x,
glyph.texturePosition.y,
glyph.size.x,
glyph.size.y,
x * this._deviceCellWidth + this._deviceCharLeft - glyph.offset.x,
y * this._deviceCellHeight + this._deviceCharTop - glyph.offset.y,
glyph.size.x,
glyph.size.y
);
this._ctx.restore();
}
/**
* Clips a row to ensure no pixels will be drawn outside the cells in the row.
* @param y The row to clip.
*/
private _clipRow(y: number): void {
this._ctx.beginPath();
this._ctx.rect(
0,
y * this._deviceCellHeight,
this._bufferService.cols * this._deviceCellWidth,
this._deviceCellHeight);
this._ctx.clip();
}
/**
* Gets the current font.
* @param isBold If we should use the bold fontWeight.
*/
protected _getFont(isBold: boolean, isItalic: boolean): string {
const fontWeight = isBold ? this._optionsService.rawOptions.fontWeightBold : this._optionsService.rawOptions.fontWeight;
const fontStyle = isItalic ? 'italic' : '';
return `${fontStyle} ${fontWeight} ${this._optionsService.rawOptions.fontSize * this._coreBrowserService.dpr}px ${this._optionsService.rawOptions.fontFamily}`;
}
}
/**
* The number of milliseconds to wait before generating the ImageBitmap, this is to debounce/batch
* the operation as window.createImageBitmap is asynchronous.
*/
const GLYPH_BITMAP_COMMIT_DELAY = 100;
const enum BitmapGeneratorState {
IDLE = 0,
GENERATING = 1,
GENERATING_INVALID = 2
}
class BitmapGenerator {
private _state: BitmapGeneratorState = BitmapGeneratorState.IDLE;
private _commitTimeout: number | undefined = undefined;
private _bitmap: ImageBitmap | undefined = undefined;
public get bitmap(): ImageBitmap | undefined { return this._bitmap; }
public version: number = -1;
constructor(public readonly canvas: HTMLCanvasElement) {
}
public refresh(): void {
// Clear the bitmap immediately as it's stale
this._bitmap?.close();
this._bitmap = undefined;
// Disable ImageBitmaps on Safari because of https://bugs.webkit.org/show_bug.cgi?id=149990
if (isSafari) {
return;
}
if (this._commitTimeout === undefined) {
this._commitTimeout = window.setTimeout(() => this._generate(), GLYPH_BITMAP_COMMIT_DELAY);
}
if (this._state === BitmapGeneratorState.GENERATING) {
this._state = BitmapGeneratorState.GENERATING_INVALID;
}
}
private _generate(): void {
if (this._state === BitmapGeneratorState.IDLE) {
this._bitmap?.close();
this._bitmap = undefined;
this._state = BitmapGeneratorState.GENERATING;
window.createImageBitmap(this.canvas).then(bitmap => {
if (this._state === BitmapGeneratorState.GENERATING_INVALID) {
this.refresh();
} else {
this._bitmap = bitmap;
}
this._state = BitmapGeneratorState.IDLE;
});
if (this._commitTimeout) {
this._commitTimeout = undefined;
}
}
}
}
@@ -0,0 +1,68 @@
/**
* Copyright (c) 2022 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
import { ITerminal } from 'browser/Types';
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
import { Disposable, toDisposable } from 'common/Lifecycle';
import { setTraceLogger } from 'common/services/LogService';
import { IBufferService, IDecorationService, ILogService } from 'common/services/Services';
import { ITerminalAddon, Terminal } from 'xterm';
import { CanvasRenderer } from './CanvasRenderer';
export class CanvasAddon extends Disposable implements ITerminalAddon {
private _terminal?: Terminal;
private _renderer?: CanvasRenderer;
private readonly _onChangeTextureAtlas = this.register(new EventEmitter<HTMLCanvasElement>());
public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event;
private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter<HTMLCanvasElement>());
public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event;
public get textureAtlas(): HTMLCanvasElement | undefined {
return this._renderer?.textureAtlas;
}
public activate(terminal: Terminal): void {
const core = (terminal as any)._core as ITerminal;
if (!terminal.element) {
this.register(core.onWillOpen(() => this.activate(terminal)));
return;
}
this._terminal = terminal;
const coreService = core.coreService;
const optionsService = core.optionsService;
const screenElement = core.screenElement!;
const linkifier = core.linkifier2;
const unsafeCore = core as any;
const bufferService: IBufferService = unsafeCore._bufferService;
const renderService: IRenderService = unsafeCore._renderService;
const characterJoinerService: ICharacterJoinerService = unsafeCore._characterJoinerService;
const charSizeService: ICharSizeService = unsafeCore._charSizeService;
const coreBrowserService: ICoreBrowserService = unsafeCore._coreBrowserService;
const decorationService: IDecorationService = unsafeCore._decorationService;
const logService: ILogService = unsafeCore._logService;
const themeService: IThemeService = unsafeCore._themeService;
// Set trace logger just in case it hasn't been yet which could happen when the addon is
// bundled separately to the core module
setTraceLogger(logService);
this._renderer = new CanvasRenderer(terminal, screenElement, linkifier, bufferService, charSizeService, optionsService, characterJoinerService, coreService, coreBrowserService, decorationService, themeService);
this.register(forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas));
this.register(forwardEvent(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas));
renderService.setRenderer(this._renderer);
renderService.handleResize(bufferService.cols, bufferService.rows);
this.register(toDisposable(() => {
renderService.setRenderer((this._terminal as any)._core._createRenderer());
renderService.handleResize(terminal.cols, terminal.rows);
this._renderer?.dispose();
this._renderer = undefined;
}));
}
}
@@ -0,0 +1,186 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ILinkifier2 } from 'browser/Types';
import { removeTerminalFromCache } from 'browser/renderer/shared/CharAtlasCache';
import { observeDevicePixelDimensions } from 'browser/renderer/shared/DevicePixelObserver';
import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils';
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
import { Disposable, toDisposable } from 'common/Lifecycle';
import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
import { Terminal } from 'xterm';
import { CursorRenderLayer } from './CursorRenderLayer';
import { LinkRenderLayer } from './LinkRenderLayer';
import { SelectionRenderLayer } from './SelectionRenderLayer';
import { TextRenderLayer } from './TextRenderLayer';
import { IRenderLayer } from './Types';
export class CanvasRenderer extends Disposable implements IRenderer {
private _renderLayers: IRenderLayer[];
private _devicePixelRatio: number;
public dimensions: IRenderDimensions;
private readonly _onRequestRedraw = this.register(new EventEmitter<IRequestRedrawEvent>());
public readonly onRequestRedraw = this._onRequestRedraw.event;
private readonly _onChangeTextureAtlas = this.register(new EventEmitter<HTMLCanvasElement>());
public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event;
private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter<HTMLCanvasElement>());
public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event;
constructor(
private readonly _terminal: Terminal,
private readonly _screenElement: HTMLElement,
linkifier2: ILinkifier2,
private readonly _bufferService: IBufferService,
private readonly _charSizeService: ICharSizeService,
private readonly _optionsService: IOptionsService,
characterJoinerService: ICharacterJoinerService,
coreService: ICoreService,
private readonly _coreBrowserService: ICoreBrowserService,
decorationService: IDecorationService,
private readonly _themeService: IThemeService
) {
super();
const allowTransparency = this._optionsService.rawOptions.allowTransparency;
this._renderLayers = [
new TextRenderLayer(this._terminal, this._screenElement, 0, allowTransparency, this._bufferService, this._optionsService, characterJoinerService, decorationService, this._coreBrowserService, _themeService),
new SelectionRenderLayer(this._terminal, this._screenElement, 1, this._bufferService, this._coreBrowserService, decorationService, this._optionsService, _themeService),
new LinkRenderLayer(this._terminal, this._screenElement, 2, linkifier2, this._bufferService, this._optionsService, decorationService, this._coreBrowserService, _themeService),
new CursorRenderLayer(this._terminal, this._screenElement, 3, this._onRequestRedraw, this._bufferService, this._optionsService, coreService, this._coreBrowserService, decorationService, _themeService)
];
for (const layer of this._renderLayers) {
forwardEvent(layer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas);
}
this.dimensions = createRenderDimensions();
this._devicePixelRatio = this._coreBrowserService.dpr;
this._updateDimensions();
this.register(observeDevicePixelDimensions(this._renderLayers[0].canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h)));
this.register(toDisposable(() => {
for (const l of this._renderLayers) {
l.dispose();
}
removeTerminalFromCache(this._terminal);
}));
}
public get textureAtlas(): HTMLCanvasElement | undefined {
return this._renderLayers[0].cacheCanvas;
}
public handleDevicePixelRatioChange(): void {
// If the device pixel ratio changed, the char atlas needs to be regenerated
// and the terminal needs to refreshed
if (this._devicePixelRatio !== this._coreBrowserService.dpr) {
this._devicePixelRatio = this._coreBrowserService.dpr;
this.handleResize(this._bufferService.cols, this._bufferService.rows);
}
}
public handleResize(cols: number, rows: number): void {
// Update character and canvas dimensions
this._updateDimensions();
// Resize all render layers
for (const l of this._renderLayers) {
l.resize(this.dimensions);
}
// Resize the screen
this._screenElement.style.width = `${this.dimensions.css.canvas.width}px`;
this._screenElement.style.height = `${this.dimensions.css.canvas.height}px`;
}
public handleCharSizeChanged(): void {
this.handleResize(this._bufferService.cols, this._bufferService.rows);
}
public handleBlur(): void {
this._runOperation(l => l.handleBlur());
}
public handleFocus(): void {
this._runOperation(l => l.handleFocus());
}
public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {
this._runOperation(l => l.handleSelectionChanged(start, end, columnSelectMode));
// Selection foreground requires a full re-render
if (this._themeService.colors.selectionForeground) {
this._onRequestRedraw.fire({ start: 0, end: this._bufferService.rows - 1 });
}
}
public handleCursorMove(): void {
this._runOperation(l => l.handleCursorMove());
}
public clear(): void {
this._runOperation(l => l.reset());
}
private _runOperation(operation: (layer: IRenderLayer) => void): void {
for (const l of this._renderLayers) {
operation(l);
}
}
/**
* Performs the refresh loop callback, calling refresh only if a refresh is
* necessary before queueing up the next one.
*/
public renderRows(start: number, end: number): void {
for (const l of this._renderLayers) {
l.handleGridChanged(start, end);
}
}
public clearTextureAtlas(): void {
for (const layer of this._renderLayers) {
layer.clearTextureAtlas();
}
}
/**
* Recalculates the character and canvas dimensions.
*/
private _updateDimensions(): void {
if (!this._charSizeService.hasValidSize) {
return;
}
// See the WebGL renderer for an explanation of this section.
const dpr = this._coreBrowserService.dpr;
this.dimensions.device.char.width = Math.floor(this._charSizeService.width * dpr);
this.dimensions.device.char.height = Math.ceil(this._charSizeService.height * dpr);
this.dimensions.device.cell.height = Math.floor(this.dimensions.device.char.height * this._optionsService.rawOptions.lineHeight);
this.dimensions.device.char.top = this._optionsService.rawOptions.lineHeight === 1 ? 0 : Math.round((this.dimensions.device.cell.height - this.dimensions.device.char.height) / 2);
this.dimensions.device.cell.width = this.dimensions.device.char.width + Math.round(this._optionsService.rawOptions.letterSpacing);
this.dimensions.device.char.left = Math.floor(this._optionsService.rawOptions.letterSpacing / 2);
this.dimensions.device.canvas.height = this._bufferService.rows * this.dimensions.device.cell.height;
this.dimensions.device.canvas.width = this._bufferService.cols * this.dimensions.device.cell.width;
this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / dpr);
this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / dpr);
this.dimensions.css.cell.height = this.dimensions.css.canvas.height / this._bufferService.rows;
this.dimensions.css.cell.width = this.dimensions.css.canvas.width / this._bufferService.cols;
}
private _setCanvasDevicePixelDimensions(width: number, height: number): void {
this.dimensions.device.canvas.height = height;
this.dimensions.device.canvas.width = width;
// Resize all render layers
for (const l of this._renderLayers) {
l.resize(this.dimensions);
}
this._requestRedrawViewport();
}
private _requestRedrawViewport(): void {
this._onRequestRedraw.fire({ start: 0, end: this._bufferService.rows - 1 });
}
}
@@ -0,0 +1,233 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { CursorBlinkStateManager } from 'browser/renderer/shared/CursorBlinkStateManager';
import { IRenderDimensions, IRequestRedrawEvent } from 'browser/renderer/shared/Types';
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { IEventEmitter } from 'common/EventEmitter';
import { MutableDisposable } from 'common/Lifecycle';
import { isFirefox } from 'common/Platform';
import { ICellData } from 'common/Types';
import { CellData } from 'common/buffer/CellData';
import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
import { Terminal } from 'xterm';
import { BaseRenderLayer } from './BaseRenderLayer';
interface ICursorState {
x: number;
y: number;
isFocused: boolean;
style: string;
width: number;
}
export class CursorRenderLayer extends BaseRenderLayer {
private _state: ICursorState;
private _cursorRenderers: {[key: string]: (x: number, y: number, cell: ICellData) => void};
private _cursorBlinkStateManager: MutableDisposable<CursorBlinkStateManager> = this.register(new MutableDisposable());
private _cell: ICellData = new CellData();
constructor(
terminal: Terminal,
container: HTMLElement,
zIndex: number,
private readonly _onRequestRedraw: IEventEmitter<IRequestRedrawEvent>,
bufferService: IBufferService,
optionsService: IOptionsService,
private readonly _coreService: ICoreService,
coreBrowserService: ICoreBrowserService,
decorationService: IDecorationService,
themeService: IThemeService
) {
super(terminal, container, 'cursor', zIndex, true, themeService, bufferService, optionsService, decorationService, coreBrowserService);
this._state = {
x: 0,
y: 0,
isFocused: false,
style: '',
width: 0
};
this._cursorRenderers = {
'bar': this._renderBarCursor.bind(this),
'block': this._renderBlockCursor.bind(this),
'underline': this._renderUnderlineCursor.bind(this),
'outline': this._renderOutlineCursor.bind(this)
};
this.register(optionsService.onOptionChange(() => this._handleOptionsChanged()));
this._handleOptionsChanged();
}
public resize(dim: IRenderDimensions): void {
super.resize(dim);
// Resizing the canvas discards the contents of the canvas so clear state
this._state = {
x: 0,
y: 0,
isFocused: false,
style: '',
width: 0
};
}
public reset(): void {
this._clearCursor();
this._cursorBlinkStateManager.value?.restartBlinkAnimation();
this._handleOptionsChanged();
}
public handleBlur(): void {
this._cursorBlinkStateManager.value?.pause();
this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });
}
public handleFocus(): void {
this._cursorBlinkStateManager.value?.resume();
this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });
}
private _handleOptionsChanged(): void {
if (this._optionsService.rawOptions.cursorBlink) {
if (!this._cursorBlinkStateManager.value) {
this._cursorBlinkStateManager.value = new CursorBlinkStateManager(() => this._render(true), this._coreBrowserService);
}
} else {
this._cursorBlinkStateManager.clear();
}
// Request a refresh from the terminal as management of rendering is being
// moved back to the terminal
this._onRequestRedraw.fire({ start: this._bufferService.buffer.y, end: this._bufferService.buffer.y });
}
public handleCursorMove(): void {
this._cursorBlinkStateManager.value?.restartBlinkAnimation();
}
public handleGridChanged(startRow: number, endRow: number): void {
if (!this._cursorBlinkStateManager.value || this._cursorBlinkStateManager.value.isPaused) {
this._render(false);
} else {
this._cursorBlinkStateManager.value.restartBlinkAnimation();
}
}
private _render(triggeredByAnimationFrame: boolean): void {
// Don't draw the cursor if it's hidden
if (!this._coreService.isCursorInitialized || this._coreService.isCursorHidden) {
this._clearCursor();
return;
}
const cursorY = this._bufferService.buffer.ybase + this._bufferService.buffer.y;
const viewportRelativeCursorY = cursorY - this._bufferService.buffer.ydisp;
// Don't draw the cursor if it's off-screen
if (viewportRelativeCursorY < 0 || viewportRelativeCursorY >= this._bufferService.rows) {
this._clearCursor();
return;
}
// in case cursor.x == cols adjust visual cursor to cols - 1
const cursorX = Math.min(this._bufferService.buffer.x, this._bufferService.cols - 1);
this._bufferService.buffer.lines.get(cursorY)!.loadCell(cursorX, this._cell);
if (this._cell.content === undefined) {
return;
}
if (!this._coreBrowserService.isFocused) {
this._clearCursor();
this._ctx.save();
this._ctx.fillStyle = this._themeService.colors.cursor.css;
const cursorStyle = this._optionsService.rawOptions.cursorStyle;
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
if (cursorInactiveStyle && cursorInactiveStyle !== 'none') {
this._cursorRenderers[cursorInactiveStyle](cursorX, viewportRelativeCursorY, this._cell);
}
this._ctx.restore();
this._state.x = cursorX;
this._state.y = viewportRelativeCursorY;
this._state.isFocused = false;
this._state.style = cursorStyle;
this._state.width = this._cell.getWidth();
return;
}
// Don't draw the cursor if it's blinking
if (this._cursorBlinkStateManager.value && !this._cursorBlinkStateManager.value.isCursorVisible) {
this._clearCursor();
return;
}
if (this._state) {
// The cursor is already in the correct spot, don't redraw
if (this._state.x === cursorX &&
this._state.y === viewportRelativeCursorY &&
this._state.isFocused === this._coreBrowserService.isFocused &&
this._state.style === this._optionsService.rawOptions.cursorStyle &&
this._state.width === this._cell.getWidth()) {
return;
}
this._clearCursor();
}
this._ctx.save();
this._cursorRenderers[this._optionsService.rawOptions.cursorStyle || 'block'](cursorX, viewportRelativeCursorY, this._cell);
this._ctx.restore();
this._state.x = cursorX;
this._state.y = viewportRelativeCursorY;
this._state.isFocused = false;
this._state.style = this._optionsService.rawOptions.cursorStyle;
this._state.width = this._cell.getWidth();
}
private _clearCursor(): void {
if (this._state) {
// Avoid potential rounding errors when browser is Firefox (#4487) or device pixel ratio is
// less than 1
if (isFirefox || this._coreBrowserService.dpr < 1) {
this._clearAll();
} else {
this._clearCells(this._state.x, this._state.y, this._state.width, 1);
}
this._state = {
x: 0,
y: 0,
isFocused: false,
style: '',
width: 0
};
}
}
private _renderBarCursor(x: number, y: number, cell: ICellData): void {
this._ctx.save();
this._ctx.fillStyle = this._themeService.colors.cursor.css;
this._fillLeftLineAtCell(x, y, this._optionsService.rawOptions.cursorWidth);
this._ctx.restore();
}
private _renderBlockCursor(x: number, y: number, cell: ICellData): void {
this._ctx.save();
this._ctx.fillStyle = this._themeService.colors.cursor.css;
this._fillCells(x, y, cell.getWidth(), 1);
this._ctx.fillStyle = this._themeService.colors.cursorAccent.css;
this._fillCharTrueColor(cell, x, y);
this._ctx.restore();
}
private _renderUnderlineCursor(x: number, y: number, cell: ICellData): void {
this._ctx.save();
this._ctx.fillStyle = this._themeService.colors.cursor.css;
this._fillBottomLineAtCells(x, y);
this._ctx.restore();
}
private _renderOutlineCursor(x: number, y: number, cell: ICellData): void {
this._ctx.save();
this._ctx.strokeStyle = this._themeService.colors.cursor.css;
this._strokeRectAtCell(x, y, cell.getWidth(), 1);
this._ctx.restore();
}
}
@@ -4,7 +4,7 @@
*/
import { assert } from 'chai';
import { GridCache } from 'browser/renderer/GridCache';
import { GridCache } from './GridCache';
describe('GridCache', () => {
let grid: GridCache<number>;
@@ -3,33 +3,33 @@
* @license MIT
*/
import { IRenderDimensions } from 'browser/renderer/Types';
import { BaseRenderLayer } from './BaseRenderLayer';
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
import { is256Color } from 'browser/renderer/atlas/CharAtlasUtils';
import { IColorSet, ILinkifierEvent, ILinkifier, ILinkifier2 } from 'browser/Types';
import { ILinkifier2, ILinkifierEvent } from 'browser/Types';
import { is256Color } from 'browser/renderer/shared/CharAtlasUtils';
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/shared/Constants';
import { IRenderDimensions } from 'browser/renderer/shared/Types';
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
import { Terminal } from 'xterm';
import { BaseRenderLayer } from './BaseRenderLayer';
export class LinkRenderLayer extends BaseRenderLayer {
private _state: ILinkifierEvent | undefined;
constructor(
terminal: Terminal,
container: HTMLElement,
zIndex: number,
colors: IColorSet,
rendererId: number,
linkifier: ILinkifier,
linkifier2: ILinkifier2,
@IBufferService bufferService: IBufferService,
@IOptionsService optionsService: IOptionsService,
@IDecorationService decorationService: IDecorationService
bufferService: IBufferService,
optionsService: IOptionsService,
decorationService: IDecorationService,
coreBrowserService: ICoreBrowserService,
themeService: IThemeService
) {
super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService, decorationService);
linkifier.onShowLinkUnderline(e => this._onShowLinkUnderline(e));
linkifier.onHideLinkUnderline(e => this._onHideLinkUnderline(e));
super(terminal, container, 'link', zIndex, true, themeService, bufferService, optionsService, decorationService, coreBrowserService);
linkifier2.onShowLinkUnderline(e => this._onShowLinkUnderline(e));
linkifier2.onHideLinkUnderline(e => this._onHideLinkUnderline(e));
this.register(linkifier2.onShowLinkUnderline(e => this._handleShowLinkUnderline(e)));
this.register(linkifier2.onHideLinkUnderline(e => this._handleHideLinkUnderline(e)));
}
public resize(dim: IRenderDimensions): void {
@@ -54,14 +54,14 @@ export class LinkRenderLayer extends BaseRenderLayer {
}
}
private _onShowLinkUnderline(e: ILinkifierEvent): void {
private _handleShowLinkUnderline(e: ILinkifierEvent): void {
if (e.fg === INVERTED_DEFAULT_COLOR) {
this._ctx.fillStyle = this._colors.background.css;
this._ctx.fillStyle = this._themeService.colors.background.css;
} else if (e.fg && is256Color(e.fg)) {
// 256 color support
this._ctx.fillStyle = this._colors.ansi[e.fg].css;
this._ctx.fillStyle = this._themeService.colors.ansi[e.fg].css;
} else {
this._ctx.fillStyle = this._colors.foreground.css;
this._ctx.fillStyle = this._themeService.colors.foreground.css;
}
if (e.y1 === e.y2) {
@@ -78,7 +78,7 @@ export class LinkRenderLayer extends BaseRenderLayer {
this._state = e;
}
private _onHideLinkUnderline(e: ILinkifierEvent): void {
private _handleHideLinkUnderline(e: ILinkifierEvent): void {
this._clearCurrentLink();
}
}
@@ -3,10 +3,11 @@
* @license MIT
*/
import { IRenderDimensions } from 'browser/renderer/Types';
import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';
import { IColorSet } from 'browser/Types';
import { IRenderDimensions } from 'browser/renderer/shared/Types';
import { BaseRenderLayer } from './BaseRenderLayer';
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { Terminal } from 'xterm';
interface ISelectionState {
start?: [number, number];
@@ -19,15 +20,16 @@ export class SelectionRenderLayer extends BaseRenderLayer {
private _state!: ISelectionState;
constructor(
terminal: Terminal,
container: HTMLElement,
zIndex: number,
colors: IColorSet,
rendererId: number,
@IBufferService bufferService: IBufferService,
@IOptionsService optionsService: IOptionsService,
@IDecorationService decorationService: IDecorationService
bufferService: IBufferService,
coreBrowserService: ICoreBrowserService,
decorationService: IDecorationService,
optionsService: IOptionsService,
themeService: IThemeService
) {
super(container, 'selection', zIndex, true, colors, rendererId, bufferService, optionsService, decorationService);
super(terminal, container, 'selection', zIndex, true, themeService, bufferService, optionsService, decorationService, coreBrowserService);
this._clearState();
}
@@ -42,8 +44,12 @@ export class SelectionRenderLayer extends BaseRenderLayer {
public resize(dim: IRenderDimensions): void {
super.resize(dim);
// Resizing the canvas discards the contents of the canvas so clear state
this._clearState();
// On resize use the base render layer's cached selection values since resize clears _state
// inside reset.
if (this._selectionModel.selectionStart && this._selectionModel.selectionEnd) {
this._clearState();
this._redrawSelection(this._selectionModel.selectionStart, this._selectionModel.selectionEnd, this._selectionModel.columnSelectMode);
}
}
public reset(): void {
@@ -53,9 +59,22 @@ export class SelectionRenderLayer extends BaseRenderLayer {
}
}
public onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
super.onSelectionChanged(start, end, columnSelectMode);
public handleBlur(): void {
this.reset();
this._redrawSelection(this._selectionModel.selectionStart, this._selectionModel.selectionEnd, this._selectionModel.columnSelectMode);
}
public handleFocus(): void {
this.reset();
this._redrawSelection(this._selectionModel.selectionStart, this._selectionModel.selectionEnd, this._selectionModel.columnSelectMode);
}
public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
super.handleSelectionChanged(start, end, columnSelectMode);
this._redrawSelection(start, end, columnSelectMode);
}
private _redrawSelection(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
// Selection has not changed
if (!this._didStateChange(start, end, columnSelectMode, this._bufferService.buffer.ydisp)) {
return;
@@ -82,7 +101,9 @@ export class SelectionRenderLayer extends BaseRenderLayer {
return;
}
this._ctx.fillStyle = this._colors.selectionTransparent.css;
this._ctx.fillStyle = (this._coreBrowserService.isFocused
? this._themeService.colors.selectionBackgroundTransparent
: this._themeService.colors.selectionInactiveBackgroundTransparent).css;
if (columnSelectMode) {
const startCol = start[0];
@@ -3,17 +3,17 @@
* @license MIT
*/
import { IRenderDimensions } from 'browser/renderer/Types';
import { CharData, ICellData } from 'common/Types';
import { GridCache } from 'browser/renderer/GridCache';
import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';
import { AttributeData } from 'common/buffer/AttributeData';
import { NULL_CELL_CODE, Content } from 'common/buffer/Constants';
import { IColorSet } from 'browser/Types';
import { CellData } from 'common/buffer/CellData';
import { IOptionsService, IBufferService, IDecorationService } from 'common/services/Services';
import { ICharacterJoinerService } from 'browser/services/Services';
import { IRenderDimensions } from 'browser/renderer/shared/Types';
import { JoinedCellData } from 'browser/services/CharacterJoinerService';
import { ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { CharData, ICellData } from 'common/Types';
import { AttributeData } from 'common/buffer/AttributeData';
import { CellData } from 'common/buffer/CellData';
import { Content, NULL_CELL_CODE } from 'common/buffer/Constants';
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
import { Terminal } from 'xterm';
import { BaseRenderLayer } from './BaseRenderLayer';
import { GridCache } from './GridCache';
/**
* This CharData looks like a null character, which will forc a clear and render
@@ -30,18 +30,20 @@ export class TextRenderLayer extends BaseRenderLayer {
private _workCell = new CellData();
constructor(
terminal: Terminal,
container: HTMLElement,
zIndex: number,
colors: IColorSet,
alpha: boolean,
rendererId: number,
@IBufferService bufferService: IBufferService,
@IOptionsService optionsService: IOptionsService,
@ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService,
@IDecorationService decorationService: IDecorationService
bufferService: IBufferService,
optionsService: IOptionsService,
private readonly _characterJoinerService: ICharacterJoinerService,
decorationService: IDecorationService,
coreBrowserService: ICoreBrowserService,
themeService: IThemeService
) {
super(container, 'text', zIndex, alpha, colors, rendererId, bufferService, optionsService, decorationService);
super(terminal, container, 'text', zIndex, alpha, themeService, bufferService, optionsService, decorationService, coreBrowserService);
this._state = new GridCache<CharData>();
this.register(optionsService.onSpecificOptionChange('allowTransparency', value => this._setTransparency(value)));
}
public resize(dim: IRenderDimensions): void {
@@ -49,8 +51,8 @@ export class TextRenderLayer extends BaseRenderLayer {
// Clear the character width cache if the font or width has changed
const terminalFont = this._getFont(false, false);
if (this._characterWidth !== dim.scaledCharWidth || this._characterFont !== terminalFont) {
this._characterWidth = dim.scaledCharWidth;
if (this._characterWidth !== dim.device.char.width || this._characterFont !== terminalFont) {
this._characterWidth = dim.device.char.width;
this._characterFont = terminalFont;
this._characterOverlapCache = {};
}
@@ -91,6 +93,13 @@ export class TextRenderLayer extends BaseRenderLayer {
continue;
}
// exit early for NULL and SP
// NOTE: commented out due to #4120 (needs a more clever patch to keep things performant)
// const code = cell.getCode();
// if (code === 0 || code === 32) {
// continue;
// }
// Process any joined character ranges as needed. Because of how the
// ranges are produced, we know that they are valid for the characters
// and attributes of our input.
@@ -165,30 +174,30 @@ export class TextRenderLayer extends BaseRenderLayer {
if (cell.isInverse()) {
if (cell.isFgDefault()) {
nextFillStyle = this._colors.foreground.css;
nextFillStyle = this._themeService.colors.foreground.css;
} else if (cell.isFgRGB()) {
nextFillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
} else {
nextFillStyle = this._colors.ansi[cell.getFgColor()].css;
nextFillStyle = this._themeService.colors.ansi[cell.getFgColor()].css;
}
} else if (cell.isBgRGB()) {
nextFillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`;
} else if (cell.isBgPalette()) {
nextFillStyle = this._colors.ansi[cell.getBgColor()].css;
nextFillStyle = this._themeService.colors.ansi[cell.getBgColor()].css;
}
// Get any decoration foreground/background overrides, this must be fetched before the early
// exist but applied after inverse
let isTop = false;
for (const d of this._decorationService.getDecorationsAtCell(x, this._bufferService.buffer.ydisp + y)) {
this._decorationService.forEachDecorationAtCell(x, this._bufferService.buffer.ydisp + y, undefined, d => {
if (d.options.layer !== 'top' && isTop) {
continue;
return;
}
if (d.backgroundColorRGB) {
nextFillStyle = d.backgroundColorRGB.css;
}
isTop = d.options.layer === 'top';
}
});
if (prevFillStyle === null) {
// This is either the first iteration, or the default background was set. Either way, we
@@ -224,52 +233,10 @@ export class TextRenderLayer extends BaseRenderLayer {
}
private _drawForeground(firstRow: number, lastRow: number): void {
this._forEachCell(firstRow, lastRow, (cell, x, y) => {
if (cell.isInvisible()) {
return;
}
this._drawChars(cell, x, y);
if (cell.isUnderline() || cell.isStrikethrough()) {
this._ctx.save();
if (cell.isInverse()) {
if (cell.isBgDefault()) {
this._ctx.fillStyle = this._colors.background.css;
} else if (cell.isBgRGB()) {
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getBgColor()).join(',')})`;
} else {
let bg = cell.getBgColor();
if (this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && bg < 8) {
bg += 8;
}
this._ctx.fillStyle = this._colors.ansi[bg].css;
}
} else {
if (cell.isFgDefault()) {
this._ctx.fillStyle = this._colors.foreground.css;
} else if (cell.isFgRGB()) {
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getFgColor()).join(',')})`;
} else {
let fg = cell.getFgColor();
if (this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
fg += 8;
}
this._ctx.fillStyle = this._colors.ansi[fg].css;
}
}
if (cell.isStrikethrough()) {
this._fillMiddleLineAtCells(x, y, cell.getWidth());
}
if (cell.isUnderline()) {
this._fillBottomLineAtCells(x, y, cell.getWidth());
}
this._ctx.restore();
}
});
this._forEachCell(firstRow, lastRow, (cell, x, y) => this._drawChars(cell, x, y));
}
public onGridChanged(firstRow: number, lastRow: number): void {
public handleGridChanged(firstRow: number, lastRow: number): void {
// Resize has not been called yet
if (this._state.cache.length === 0) {
return;
@@ -284,10 +251,6 @@ export class TextRenderLayer extends BaseRenderLayer {
this._drawForeground(firstRow, lastRow);
}
public onOptionsChanged(): void {
this._setTransparency(this._optionsService.rawOptions.allowTransparency);
}
/**
* Whether a character is overlapping to the next cell.
*/
@@ -326,19 +289,4 @@ export class TextRenderLayer extends BaseRenderLayer {
this._characterOverlapCache[chars] = overlaps;
return overlaps;
}
/**
* Clear the charcater at the cell specified.
* @param x The column of the char.
* @param y The row of the char.
*/
// private _clearChar(x: number, y: number): void {
// let colsToClear = 1;
// // Clear the adjacent character if it was wide
// const state = this._state.cache[x][y];
// if (state && state[CHAR_DATA_WIDTH_INDEX] === 2) {
// colsToClear = 2;
// }
// this.clearCells(x, y, colsToClear, 1);
// }
}
@@ -4,23 +4,8 @@
*/
import { IDisposable } from 'common/Types';
import { IColorSet } from 'browser/Types';
import { IEvent } from 'common/EventEmitter';
export interface IRenderDimensions {
scaledCharWidth: number;
scaledCharHeight: number;
scaledCellWidth: number;
scaledCellHeight: number;
scaledCharLeft: number;
scaledCharTop: number;
scaledCanvasWidth: number;
scaledCanvasHeight: number;
canvasWidth: number;
canvasHeight: number;
actualCellWidth: number;
actualCellHeight: number;
}
import { IRenderDimensions } from 'browser/renderer/shared/Types';
export interface IRequestRedrawEvent {
start: number;
@@ -40,57 +25,49 @@ export interface IRenderer extends IDisposable {
*/
readonly onRequestRedraw: IEvent<IRequestRedrawEvent>;
dispose(): void;
setColors(colors: IColorSet): void;
onDevicePixelRatioChange(): void;
onResize(cols: number, rows: number): void;
onCharSizeChanged(): void;
onBlur(): void;
onFocus(): void;
onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void;
onCursorMove(): void;
onOptionsChanged(): void;
handleDevicePixelRatioChange(): void;
handleResize(cols: number, rows: number): void;
handleCharSizeChanged(): void;
handleBlur(): void;
handleFocus(): void;
handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void;
handleCursorMove(): void;
handleOptionsChanged(): void;
clear(): void;
renderRows(start: number, end: number): void;
clearTextureAtlas?(): void;
}
export interface IRenderLayer extends IDisposable {
readonly canvas: HTMLCanvasElement;
readonly cacheCanvas: HTMLCanvasElement;
readonly onAddTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
/**
* Called when the terminal loses focus.
*/
onBlur(): void;
handleBlur(): void;
/**
* * Called when the terminal gets focus.
* Called when the terminal gets focus.
*/
onFocus(): void;
handleFocus(): void;
/**
* Called when the cursor is moved.
*/
onCursorMove(): void;
/**
* Called when options change.
*/
onOptionsChanged(): void;
/**
* Called when the theme changes.
*/
setColors(colorSet: IColorSet): void;
handleCursorMove(): void;
/**
* Called when the data in the grid has changed (or needs to be rendered
* again).
*/
onGridChanged(startRow: number, endRow: number): void;
handleGridChanged(startRow: number, endRow: number): void;
/**
* Calls when the selection changes.
*/
onSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void;
handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void;
/**
* Resize the render layer.
@@ -0,0 +1,40 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2015",
"lib": [
"dom",
"es6"
],
"rootDir": ".",
"outDir": "../out",
"sourceMap": true,
"removeComments": true,
"baseUrl": ".",
"paths": {
"common/*": [
"../../../src/common/*"
],
"browser/*": [
"../../../src/browser/*"
]
},
"strict": true,
"downlevelIteration": true,
"types": [
"../../../node_modules/@types/mocha"
]
},
"include": [
"./**/*",
"../../../typings/xterm.d.ts"
],
"references": [
{
"path": "../../../src/common"
},
{
"path": "../../../src/browser"
}
]
}
+7
View File
@@ -0,0 +1,7 @@
{
"files": [],
"include": [],
"references": [
{ "path": "./src" }
]
}
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal, ITerminalAddon, IEvent } from 'xterm';
declare module 'xterm-addon-canvas' {
/**
* An xterm.js addon that provides search functionality.
*/
export class CanvasAddon implements ITerminalAddon {
public textureAtlas?: HTMLCanvasElement;
/**
* An event that is fired when the texture atlas of the renderer changes.
*/
public readonly onChangeTextureAtlas: IEvent<HTMLCanvasElement>;
/**
* An event that is fired when the a new page is added to the texture atlas.
*/
public readonly onAddTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
constructor();
/**
* Activates the addon.
* @param terminal The terminal the addon is being loaded in.
*/
public activate(terminal: Terminal): void;
/**
* Disposes the addon.
*/
public dispose(): void;
/**
* Clears the terminal's texture atlas and triggers a redraw.
*/
public clearTextureAtlas(): void;
}
}
@@ -0,0 +1,39 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
const path = require('path');
const addonName = 'CanvasAddon';
const mainFile = 'xterm-addon-canvas.js';
module.exports = {
entry: `./out/${addonName}.js`,
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre",
exclude: /node_modules/
}
]
},
resolve: {
modules: ['./node_modules'],
extensions: [ '.js' ],
alias: {
common: path.resolve('../../out/common'),
browser: path.resolve('../../out/browser')
}
},
output: {
filename: mainFile,
path: path.resolve('./lib'),
library: addonName,
libraryTarget: 'umd'
},
mode: 'production'
};
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-fit",
"version": "0.5.0",
"version": "0.8.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
@@ -21,6 +21,6 @@
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^4.0.0"
"xterm": "^5.0.0"
}
}
+5 -5
View File
@@ -4,6 +4,7 @@
*/
import { Terminal, ITerminalAddon } from 'xterm';
import { IRenderDimensions } from 'browser/renderer/shared/Types';
interface ITerminalDimensions {
/**
@@ -23,8 +24,6 @@ const MINIMUM_ROWS = 1;
export class FitAddon implements ITerminalAddon {
private _terminal: Terminal | undefined;
constructor() {}
public activate(terminal: Terminal): void {
this._terminal = terminal;
}
@@ -58,8 +57,9 @@ export class FitAddon implements ITerminalAddon {
// TODO: Remove reliance on private API
const core = (this._terminal as any)._core;
const dims: IRenderDimensions = core._renderService.dimensions;
if (core._renderService.dimensions.actualCellWidth === 0 || core._renderService.dimensions.actualCellHeight === 0) {
if (dims.css.cell.width === 0 || dims.css.cell.height === 0) {
return undefined;
}
@@ -81,8 +81,8 @@ export class FitAddon implements ITerminalAddon {
const availableHeight = parentElementHeight - elementPaddingVer;
const availableWidth = parentElementWidth - elementPaddingHor - scrollbarWidth;
const geometry = {
cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth)),
rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight))
cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / dims.css.cell.width)),
rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / dims.css.cell.height))
};
return geometry;
}
+12 -2
View File
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"target": "es2015",
"lib": [
"dom",
"es2015"
@@ -13,10 +13,20 @@
"strict": true,
"types": [
"../../../node_modules/@types/mocha"
]
],
"paths": {
"browser/*": [
"../../../src/browser/*"
]
}
},
"include": [
"./**/*",
"../../../typings/xterm.d.ts"
],
"references": [
{
"path": "../../../src/browser"
}
]
}
+11 -11
View File
@@ -5,7 +5,7 @@
import { assert } from 'chai';
import { openTerminal, launchBrowser } from '../../../out-test/api/TestUtils';
import { Browser, Page } from 'playwright';
import { Browser, Page } from '@playwright/test';
const APP = 'http://127.0.0.1:3001/test';
@@ -41,14 +41,13 @@ describe('FitAddon', () => {
});
describe('proposeDimensions', () => {
afterEach(async () => {
return await unloadFit();
});
afterEach(() => unloadFit());
it('default', async function(): Promise<any> {
await loadFit();
const dimensions: {cols: number, rows: number} = await page.evaluate(`window.fit.proposeDimensions()`);
assert.equal(dimensions.cols, 87);
assert.isAbove(dimensions.cols, 85);
assert.isBelow(dimensions.cols, 88);
assert.isAbove(dimensions.rows, 24);
assert.isBelow(dimensions.rows, 29);
});
@@ -56,7 +55,8 @@ describe('FitAddon', () => {
it('width', async function(): Promise<any> {
await loadFit(1008);
const dimensions: {cols: number, rows: number} = await page.evaluate(`window.fit.proposeDimensions()`);
assert.equal(dimensions.cols, 110);
assert.isAbove(dimensions.cols, 108);
assert.isBelow(dimensions.cols, 111);
assert.isAbove(dimensions.rows, 24);
assert.isBelow(dimensions.rows, 29);
});
@@ -80,16 +80,15 @@ describe('FitAddon', () => {
});
describe('fit', () => {
afterEach(async () => {
return await unloadFit();
});
afterEach(() => unloadFit());
it('default', async function(): Promise<any> {
await loadFit();
await page.evaluate(`window.fit.fit()`);
const cols: number = await page.evaluate(`window.term.cols`);
const rows: number = await page.evaluate(`window.term.rows`);
assert.equal(cols, 87);
assert.isAbove(cols, 85);
assert.isBelow(cols, 88);
assert.isAbove(rows, 24);
assert.isBelow(rows, 29);
});
@@ -99,7 +98,8 @@ describe('FitAddon', () => {
await page.evaluate(`window.fit.fit()`);
const cols: number = await page.evaluate(`window.term.cols`);
const rows: number = await page.evaluate(`window.term.rows`);
assert.equal(cols, 110);
assert.isAbove(cols, 108);
assert.isBelow(cols, 111);
assert.isAbove(rows, 24);
assert.isBelow(rows, 29);
});
+1 -1
View File
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"target": "es2015",
"lib": [
"es2015"
],
+1 -1
View File
@@ -35,7 +35,7 @@ declare module 'xterm-addon-fit' {
/**
* Gets the proposed dimensions that will be used for a fit.
*/
public proposeDimensions(): ITerminalDimensions;
public proposeDimensions(): ITerminalDimensions | undefined;
}
/**
+34
View File
@@ -0,0 +1,34 @@
node_modules/
*.swp
.lock-wscript
lib/
out/
out-test/
out-worker/
.nyc_output/
Makefile.gyp
*.Makefile
*.target.gyp.mk
*.node
example/*.log
docs/
npm-debug.log
/.idea/
.env
build/
.DS_Store
package-lock.json
yarn.lock
# Keep bundled code out of Git
dist/
demo/dist/
# skip benchmark & inwasm folders, except builds
.benchmark/
timeline/
inwasm-sdks/
inwasm-builds/
!inwasm-builds/**/final.wat
!inwasm-builds/**/final.wasm
!inwasm-builds/**/definition.json
+10
View File
@@ -0,0 +1,10 @@
.github
fixture
overwrite
bootstrap.sh
tsconfig.json
webpack.config.js
test
out-test
inwasm-sdks
inwasm-builds
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2019, 2020, 2021, 2022, 2023 The xterm.js authors (https://github.com/xtermjs/xterm.js)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+231
View File
@@ -0,0 +1,231 @@
## xterm-addon-image
Inline image output in xterm.js. Supports SIXEL and iTerm's inline image protocol (IIP).
![](fixture/example.png)
### Install from npm
```bash
npm install --save xterm-addon-image
```
### Usage
```ts
import { Terminal } from 'xterm';
import { ImageAddon, IImageAddonOptions } from 'xterm-addon-image';
// customize as needed (showing addon defaults)
const customSettings: IImageAddonOptions = {
enableSizeReports: true, // whether to enable CSI t reports (see below)
pixelLimit: 16777216, // max. pixel size of a single image
sixelSupport: true, // enable sixel support
sixelScrolling: true, // whether to scroll on image output
sixelPaletteLimit: 256, // initial sixel palette size
sixelSizeLimit: 25000000, // size limit of a single sixel sequence
storageLimit: 128, // FIFO storage limit in MB
showPlaceholder: true, // whether to show a placeholder for evicted images
iipSupport: true, // enable iTerm IIP support
iipSizeLimit: 20000000 // size limit of a single IIP sequence
}
// initialization
const terminal = new Terminal();
const imageAddon = new ImageAddon(customSettings);
terminal.loadAddon(imageAddon);
```
### General Notes
- *IMPORTANT:* The worker approach as done in previous releases got removed.
The addon contructor no longer expects a worker path as first argument.
- By default the addon will activate these `windowOptions` reports on the terminal:
- getWinSizePixels (CSI 14 t)
- getCellSizePixels (CSI 16 t)
- getWinSizeChars (CSI 18 t)
to help applications getting useful terminal metrics for their image preparations. Set `enableSizeReports` in the constructor options to `false`, if you dont want the addon to alter these terminal settings. This is especially useful, if you have very strict security needs not allowing any terminal reports, or deal with `windowOptions` by other means.
### Operation Modes
- **SIXEL Support**
Set by default, change it with `{sixelSupport: true}`.
- **Scrolling On | Off**
By default scrolling is on, thus an image will advance the cursor at the bottom if needed.
(see cursor positioning).
If scrolling is off, the image gets painted from the top left of the current viewport
and might be truncated if the image exceeds the viewport size.
The cursor position does not change.
You can customize this behavior with the constructor option `{sixelScrolling: false}`
or with `DECSET 80` (off, binary: `\x1b [ ? 80 h`) and
`DECRST 80` (on, binary: `\x1b [ ? 80 l`) during runtime.
- **Cursor Positioning**
If scrolling is set, the cursor will be placed at the first image column of the last image row (VT340 mode).
Other cursor positioning modes as used by xterm or mintty are not supported.
- **SIXEL Palette Handling**
By default the addon limits the palette size to 256 registers (as demanded by the DEC specification).
The limit can be increased to a maximum of 4096 registers (via `sixelPaletteLimit`).
The default palette is a mixture of VT340 colors (lower 16 registers), xterm colors (up to 256) and zeros (up to 4096).
There is no private/shared palette distinction, palette colors are always carried over from a previous sixel image.
Restoring the default palette size and colors is possible with `XTSMGRAPHICS 1 ; 2` (binary: `\x1b[?1;2S`).
It gets also restored automatically on RIS and DECSTR.
Other than on older terminals, the underlying SIXEL library applies colors immediately to individual pixels
(*printer mode*), thus it is technically possible to use more colors in one image than the palette has color slots.
This feature is called *high-color* in libsixel.
A terminal wide shared palette mode with late indexed coloring of the output is not supported,
therefore palette animations cannot be used.
- **SIXEL Raster Attributes Handling**
If raster attributes were found in the SIXEL data (level 2), the image will always be truncated to the given height/width extend. We deviate here from the specification on purpose, as it allows several processing optimizations. For level 1 SIXEL data without any raster attributes the image can freely grow in width and height up to the last data byte, which has a much higher processing penalty. In general encoding libraries should not create level 1 data anymore and should not produce pixel information beyond the announced height/width extend. Both is discouraged by the >30 years old specification.
Currently the SIXEL implementation of the addon does not take custom pixel sizes into account, a SIXEL pixel will map 1:1 to a screen pixel.
- **IIP Support (iTerm's Inline Image Protocol)**
Set by default, change it with `{iipSupport: true}`.
The IIP implementation has the following features / restrictions (sequence will silently fail for unmet conditions):
- Supported formats: PNG, JPEG and GIF
- No animation support.
- Image type hinting is not supported (always deducted from data header).
- File download is not supported.
- Filename gets parsed but not used.
- Strict base64 handling as of RFC4648 §4 (standard alphabet, optional padding, no separator bytes allowed).
- Payload size may not exceed CEIL(sizeParameter * 4 / 3).
- Image scaling beyond terminal viewport size is allowed (e.g. `width=200%`).
- Image pixel size is restricted by `pixelLimit` (pre- and post resizing).
- Size parameter is restricted by `iipSizeLimit`.
- Cursor positioning behaves the same as for sixel (see above).
### Storage and Drawing Settings
The internal storage holds images up to `storageLimit` (in MB, calculated as 4-channel RBGA unpacked, default 100 MB). Once hit images get evicted by FIFO rules. Furthermore images on the alternate buffer will always be erased on buffer changes.
The addon exposes two properties to interact with the storage limits at runtime:
- `storageLimit`
Change the value to your needs at runtime. This is especially useful, if you have multiple terminal
instances running, that all add to one upper memory limit.
- `storageUsage`
Inspect the current memory usage of the image storage.
By default the addon will show a placeholder pattern for evicted images that are still part
of the terminal (e.g. in the scrollback). The pattern can be deactivated by toggling `showPlaceholder`.
### Image Data Retrieval
The addon provides the following API endpoints to retrieve raw image data as canvas:
- `getImageAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined`
Returns the canvas containing the original image data (not resized) at the given buffer position.
The buffer position is the 0-based absolute index (including scrollback at top).
- `extractTileAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined`
Returns a canvas containing the actual single tile image data (maybe resized) at the given buffer position.
The buffer position is the 0-based absolute index (including scrollback at top).
Note that the canvas gets created and data copied over for every call, thus it is not suitable for performance critical actions.
### Memory Usage
The addon does most image processing in Javascript and therefore can occupy a rather big amount of memory. To get an idea where the memory gets eaten, lets look at the data flow and processing steps:
- Incomming image data chunk at `term.write` (terminal)
`term.write` might stock up incoming chunks. To circumvent this, you will need proper flow control (see xterm.js docs). Note that with image output it is more likely to run into this issue, as it can create lots of MBs in very short time.
- Sequence Parser (terminal)
The parser operates on a buffer containing up to 2^17 codepoints (~0.5 MB).
- Sequence Handler - Chunk Decoding (addon)
Image data chunks are processed immediately by the SIXEL decoder (streamlined). The decoder allocates memory for image
pixels as needed. The allowed image size is restricted by `pixelLimit` (default 16M pixels), the decoder holds 2 pixel buffers at maximum during decoding (RGBA, ~128 MB for 16M pixels).
- Sequence Handler - Image Finalization (addon)
After decoding the final pixel buffer is grabbed by the sequence handler and written to a canvas of the same size (~64 MB for 16M pixels) and added to the storage.
- Image Storage (addon)
The image storage implements a FIFO cache, that will remove old images, if a new one arrives and `storageLimit` is hit (default 128 MB). The storage holds a canvas with the original image, and may additionally hold resized versions of images after a font rescaling. Both are accounted in `storageUsage` as a rough estimation of _pixels x 4 channels_.
Following the steps above, a rough estimation of maximum memory usage by the addon can be calculated with these formulas (in bytes):
```typescript
// storage alone
const storageBytes = storageUsage * storageLimit * 1024 * 1024;
// decoding alone
const decodingBytes = sixelSizeLimit + 2 * (pixelLimit * 4);
// totals
// inactive decoding
const totalInactive = storageBytes;
// active decoding
const totalActive = storageBytes + decodingBytes;
```
Note that browsers have offloading tricks for rarely touched memory segments, esp. `storageBytes` might not directly translate into real memory usage. Usage peaks will happen during active decoding of multiple big images due to the need of 2 full pixel buffers at the same time, which cannot be offloaded. Thus you may want to keep an eye on `pixelLimit` under limited memory conditions.
Further note that the formulas above do not respect the Javascript object's overhead. Compared to the raw buffer needs the book keeping by these objects is rather small (<<5%).
_Why should I care about memory usage at all?_
Well you don't have to, and it probably will just work fine with the addon defaults. But for bigger integrations, where much more data is held in the Javascript context (like multiple terminals on one page), it is likely to hit the engine's memory limit sooner or later under decoding and/or storage pressure.
_How can I adjust the memory usage?_
- `pixelLimit`
A constructor setting, thus you would have to anticipate, whether (multiple) terminals in your page gonna do lots of concurrent decoding. Since this is normally not the case and the memory usage is only temporarily peaking, a rather high value should work even with multiple terminals in one page.
- `storageLimit`
A constructor and a runtime setting. In conjunction with `storageUsage` you can do runtime checks and adjust the limit to your needs. If you have to lower the limit below the current usage, images will be removed in FIFO order and may turn into a placeholder in the terminal's scrollback (if `showPlaceholder` is set). When adjusting keep in mind to leave enough room for memory peaking for decoding.
- `sixelSizeLimit`
A constructor setting. This has only a small impact on peaking memory during decoding. It is meant to avoid processing of overly big or broken SIXEL sequences at an earlier phase, thus may stop the decoder from entering its memory intensive task for potentially invalid data.
### Terminal Interaction
- Images already on the terminal screen will reshape on font-rescaling to keep the terminal cell coverage intact.
This behavior might diverge from other terminals, but is in general the more desired one.
- On terminal resize images may expand to the right automatically, if they were right-truncated before.
They never expand to the bottom, if they were bottom-truncated before (e.g. from scrolling-off).
- Text autowrapping from terminal resize may break and wrap images into multiple parts. This is unfortunate,
but cannot be avoided, while sticking to the stronger terminal text-grid mechanics.
(Yes images are a second class citizen on a mainly text-driven interface.)
- Characters written over an image will erase the image information for affected cells.
- Images are painted above BG/FG data not erasing it. More advanced "composition tricks" like painting images
below FG/BG data are not possible. (We currently dont hook into BG/FG rendering itself.)
- Previous image data at a cell will be erased on new image data. (We currently have no transparency composition.)
### Performance & Latency
- Performance should be good enough for occasional SIXEL output from REPLs, up to downscaled movies
from `mpv` with its SIXEL renderer (tested in the demo). For 3rd party xterm.js integrations this
furthermore depends highly on the overall incoming data throughput.
- Image processing has a high latency. Most of the latency though is inherited from xterm.js' incoming data route
(PTY -> server process -> websocket -> xterm.js async parsing), where every step creates more waiting time.
Since we cannot do much about that "long line", keep that in mind when you try to run more demanding applications with realtime drawing and interactive response needs.
### Status
- Sixel support and image handling in xterm.js is considered beta quality.
- IIP support is in alpha stage. Please file a bug for any awkwardities.
### Changelog
- 0.5.0 integrate with xtermjs base repo (at v0.4.3)
- 0.4.3 defer canvas creation
- 0.4.2 fix image canvas resize
- 0.4.1 compat release for xterm.js 5.2.0
- 0.4.0 IIP support
- 0.3.1 compat release for xterm.js 5.1.0
- 0.3.0 important change: worker removed from addon
- 0.2.0 compat release for xterm.js 5.0.0
- 0.1.3 bugfix: avoid striping
- 0.1.2 bugfix: reset clear flag
- 0.1.1 bugfixes:
- clear sticky image tiles on render
- create xterm-addon-image folder from bootstrap.sh
- fix peer dependency in package.json
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash
# sixel endless mode
# Should print an endless sine curve, abort with Ctrl-C.
period=200
amplitude=50
sixels=(@\$ A\$ C\$ G\$ O\$ _\$-)
pi=$(echo "scale=10; 4*a(1)" | bc -l)
run=true
trap run=false INT
echo -ne "\x1bP0;0;0q\"1;1#1;2;100;0;0#1"
y=0
while $run
do
x=$(echo "s(2*${pi}*${y}/${period})*${amplitude}+2*${amplitude}+0.5" | bc -l)
echo -ne "!${x%%.*}?${sixels[$((y%6))]}"
(( y++ ))
done
echo -e "\x1b\\"
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

+52
View File
@@ -0,0 +1,52 @@
#!/bin/bash
# GLF should move the text cursor downwards,
# even if no pixels were modified,
# when sixel scrolling is on.
echo -n $'\e[?80l' # Ensure sixel scrolling is on (disable DECSDM)
clear
echo "A test of Sixel GLF (Graphics Line Feed) when sixel scrolling is on"
echo
# Move cursor down three using GLF ("-")
echo -e '\x1bPq$-$-$-$-\x1b\\'
# Show a single sixel line that says, "Your terminal ->"
cat <<'EOF'
P0;0;0q"1;1;244;21#0;2;0;0;0#1;2;80;80;80#0~~~NFFFN~~~n!4FN!68~NFN!64~bbb!42~rbbbBB!41~$#1???owwwo???O!4wo!68?owo!64?[[[!42?K[[[{{-#0!5~{woBFB`o{}!4~NFBpp!4xpbBF~~~x@@@~~~xxp@@!5~xxp@@b`pxwxpz!17~zpp???!5px!5~NFBpxxwxxpBBN!4~xxp@@b`pxwxpz~x@@@xxp@@pxx@@F!4~xppp@@!8~x@@@pxxwxp@B!5~zxXXXWXX@BF!10~??!24~!10^FNN^~~~$#1!5?BFN{w{]NB@!4?ow{MM!4EM[{w???E}}}???EEM}}!5?EEM}}[]MEFEMC!17?CMM~~~!5ME!5?ow{MEEFEEM{{o!4?EEM}}[]MEFEMC?E}}}EEM}}MEE}}w!4?EMMM}}!8?E}}}MEEFEM}{!5?CEeeefee}{w!10?~~!24?!10_woo_-#0!5~nFFF??FFN!5~wo_FF!4NFb_o!4~{o_!4Ffb??Fn~~~NFF??!4FN!23~_??!5NFFn~~~w__C!7KCC!4~NFF??!4FN!4~N???N~~??Fn~???N~~N!4F??!4Fn~~~F???Fn~~nF??Fn~~p??KMMMEE???Fn~~~N!4F??!4Fn!29~{{}!4~$#1!5?Owww~~wwo!5?FN^ww!4ow[^N!4?BN^!4wW[~~wO???oww~~!4wo!23?^~~!5owwO???F^^z!7rzz!4?oww~~!4wo!4?o~~~o??~~wO?~~~o??o!4w~~!4wO???w~~~wO??Ow~~wO??M~~rpppxx~~~wO???o!4w~~!4wO!29?BB@-#0!25FE!63FE!15FE!77FE!60F$#1!25?@!63?@!15?@!77?@-\
EOF
sleep 1
tput cup 2 30
# Show four lines saying, "<- A genuine VT340 would end here"
cat <<'EOF'
P0;0;0q"1;1;144;80#0;2;0;0;0#1;2;80;80;80#0~~rpp@@`@BN!25~!11^!5~^^^N^^^!6~!7^N^^^!5~!4^~~~!5^!6~^^^WWW!8~!7^N^^^!8~^^^N^^^!5~$#1??KMM}}]}{o!25?!11_!5?___o___!6?!7_o___!5?!4_???!5_!6?___fff!8?!7_o___!8?___o___-#0~~NB??EFE??@F^!19~B??{!4}{W??}}~~B@?K!5MK??B~~~}???{!4}{??!4~}???~~~}}{??!6~}{{{??!8~}???{!4}{??!4~B@?K!5MK??B~~$#1??o{~~xwx~~}w_!19?{~~B!4@Bf~~@@??{}~r!5pr~~{???@~~~B!4@B~~!4?@~~~???@@B~~!6?@BBB~~!8?@~~~B!4@B~~!4?{}~r!5pr~~{-#0rpoopr~~~zpooopr!17~}{ww!4pwW??!4~}wwprrb!4rpp~~~pooopz~~zpoopz!4~{w!4pxwoopz~~~r!4poo!4pz~~~pooopz~~zpoopz~~}wwprrb!4rpp~~$#1KMNNMK???CMNNNMK!17?@BFF!4MFf~~!4?@FFMKK[!4KMM???MNNNMC??CMNNMC!4?BF!4MEFNNMC???K!4MNN!4MC???MNNNMC??CMNNMC??@FFMKK[!4KMM-#0!32~^NNNKWwww[[MNNN^~!13N!5~NN!5FNN^!10~N!4F!8~NN!4FN^!36~$#1!32?_ooorfFFFbbpooo_?!13o!5?oo!5woo_!10?o!4w!8?oo!4wo_-#0!32~}{o?AM!4~E?_w}}~o_o}}???}}}__!5~}}~fFFFAOw!6~^FB_w{???!6~B??}!4~{??!35~$#1!32?@BN~|p!4?x~^F@@?N^N@@~~~@@@^^!5?@@?Wwww|nF!6?_w{^FB~~~!6?{~~@!4?B~~-#0!36~{_?FB?o}!7~^NN???NN^!6~!8NE?_x!4~poooP@@???X!5~{o?F!4NB_o!35~$#1!36?B^~w{~N@!7?_oo~~~oo_!6?!8ox~^E!4?MNNNm}}~~~e!5?BN~w!4o{^N-#0!38~}}}!11~!8}!8~!6}!13~!6}!9~!4}!38~$#1!38?@@@!11?!8@!8?!6@!13?!6@!9?!4@-#0!5^!5~!5^!5~!7^!6~!4^~~~!5^!6~{www??!11~^^^N^\W???!22~^^^N^^^!6~!7^N^^^!8~^^^N^\W???~~$#1!5_!5?!5_!5?!7_!6?!4_???!5_!6?BFFF~~!11?___o_af~~~!22?___o___!6?!7_o___!8?___o_af~~~-#0}{??CMB@@FM??o{~~B@?{{!4}{w?@~~~}???~~~}}{??!10~??!8~B??{!5}{???!19~B@?K!5MK??B~~~}???{!4}{??!4~B??{!5}{???~~$#1@B~~zp{}}wp~~NB??{}~BB!4@BF~}???@~~~???@@B~~!10?~~!8?{~~B!5@B~~~!19?{}~r!5pr~~{???@~~~B!4@B~~!4?{~~B!5@B~~~-#0~~~woow~}woo{!4~}{wpprbrrpww{!5~{w!4pxwoopz~~~r!4poo!4pz~~~}{ww!5pwooopz!17~}wwprrb!4rpp~~~pooopz~~zpoopz~~}{ww!5pwooopz$#1???FNNF?@FNNB!4?@BFMMK[KKMFFB!5?BF!4MEFNNMC???K!4MNN!4MC???@BFF!5MFNNNMC!17?@FFMKK[!4KMM???MNNNMC??CMNNMC??@BFF!5MFNNNMC-#0!40~n!4F!99~$#1!40?O!4w-#0!10~^^n!29~???b!4rbBF!4~^NFbrrprrbFF^!4~rrbBBFBbrrrbv~~^NFbrrprrbFF^!42~$#1!10?__O!29?~~~[!4K[{w!4?_ow[KKMKK[ww_!4?KK[{{w{[KKK[G??_ow[KKMKK[ww_-#0!9~}{wq!10}!18~N???N^~~^N??N^~~o??H!6XWGG!4~^NN??!4N^!5~o??H!6XWGG!42~$#1!9?@BFL!10@!18?o~~~o_??_o~~o_??N~~u!6efvv!4?_oo~~!4o_!5?N~~u!6efvv-#0!41B!6ABB!6A!5BAAA?!6A!4B!10A!8BAAA?!6A!42B$#1!41?!6@??!6@!5?@@@B!6@!4?!10@!8?@@@B!6@$-\This text should be indented.
EOF
# For more details, please see:
# https://github.com/jerch/xterm-addon-image/issues/37
# By default, sixel is in a 2:1 aspect ratio, which means
# every sixel graphics linefeed (GLF) adds another 12 pixels.
# With four GLFs, we have five sixel lines = 5 * 12 = 60 pixels.
# Which text line that ends up on depends upon the height of your
# font. On the VT340, the font is 20 pixels high.
# On a VT340 sixel scrolling is on by default. DEC refers to this by
# two different names "sixel scrolling" and its negation, "DECSDM"
# (Sixel Display Mode). They control the same thing, so when DECSDM is
# on, sixel scrolling is off, and vice versa.
# When DECSDM is on, the graphic line feeds do not affect the text cursor.
# How hackerb9 created the two sixel test images:
#
# convert -family "Courier" -style normal -density 72 -pointsize 26 -interline-spacing -12 -gravity center -fill gray80 -background none label:$'A genuine\nVT340\nwould end\n← here ' +trim +dither -colors 2 sixel:- > reference.six
#
# convert -family "Courier" -style normal -density 72 -pointsize 26 -interline-spacing -12 -gravity center -fill gray80 -background none label:$'Your terminal →' +trim -bordercolor none -border 2 +dither -colors 2 sixel:- > yourterminal.six
@@ -0,0 +1,44 @@
const sixelEncode = require('../node_modules/sixel/lib/SixelEncoder').image2sixel;
const toRGBA8888 = require('../node_modules/sixel/lib/Colors').toRGBA8888;
function createRect(size, color) {
const pixels = new Uint32Array(size * size);
pixels.fill(toRGBA8888(...color));
return sixelEncode(new Uint8ClampedArray(pixels.buffer), size, size);
}
function createRectMinusOne(size, color) {
const pixels = new Uint32Array(size * size);
if (size - 1) {
const sub = new Uint32Array(size - 1);
sub.fill(toRGBA8888(...color));
const last = size * (size - 1);
for (let y = 0; y < last; y += size) {
pixels.set(sub, y);
}
}
return sixelEncode(new Uint8ClampedArray(pixels.buffer), size, size);
}
async function main() {
// clear + cursor and sixelScrolling off
process.stdout.write('\x1b[2J\x1b[?25;80h');
for (let i = 1; i < 300; ++i) {
await new Promise(res => setTimeout(() => {
process.stdout.write(createRect(i, [0, 255, 0]));
res();
}, 5));
}
for (let i = 299; i >= 1; --i) {
await new Promise(res => setTimeout(() => {
process.stdout.write(createRectMinusOne(i, [0, 255, 0]));
res();
}, 5));
}
// re-enable cursor and sixel scrolling
process.stdout.write('\x1b[2J\x1b[?25;80l');
}
main();
@@ -0,0 +1 @@
]1337;File=inline=1;size=525;name=Li4vcGFsZXR0ZS5wbmc=:iVBORw0KGgoAAAANSUhEUgAAAoAAAABQCAYAAACJbMQlAAAABmJLR0QA/wD/AP+gvaeTAAABwklEQVR4nO3dwQnEMAwAQedIIem/yVwLBj0C3pm3uAO/Fj2ia631rh3P1lRu7vnof0+Z836zOe83m/N+sznvN5vzfrM57zeb+23+HAAAhxCAAAAxAhAAIEYAAgDECEAAgBgBCAAQIwABAGIEIABAjAAEAIi59z8tDQDACWwAAQBiBCAAQIwABACIEYAAADECEAAgRgACAMQIQACAGAEIABAjAAEAYlwCAQCIsQEEAIgRgAAAMQIQACBGAAIAxAhAAIAYAQgAECMAAQBiBCAAQIwABACIcQkEACDGBhAAIEYAAgDECEAAgBgBCAAQIwABAGIEIABAjAAEAIgRgAAAMQIQACDGJRAAgBgbQACAGAEIABAjAAEAYgQgAECMAAQAiBGAAAAxAhAAIEYAAgDECEAAgBiXQAAAYmwAAQBiBCAAQIwABACIEYAAADECEAAgRgACAMQIQACAGAEIABAjAAEAYlwCAQCIsQEEAIgRgAAAMQIQACBGAAIAxAhAAIAYAQgAECMAAQBiBCAAQIwABACIcQkEACDGBhAAIEYAAgDECEAAgBgBCAAQIwABAGIEIABAjAAEAIgRgAAAMQIQACDmDz4lC02AaqMRAAAAAElFTkSuQmCC
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
]1337;File=inline=1;size=1667;name=Li4vdGVzdGltYWdlcy93M2NfaG9tZV8yNTYuZ2lm:R0lGODlhSAAwAPcAAAQCBIyKjMzOzERGRHSOvAxCnLy6vNzm9DRmrCQiJGxubNze3HSWxJSq1JyenCRarPz+/FR+vBweHLzG5FxeXGSKxBROpOzu7ERytDw+PHx+fLTC5AwODIyOjExOTAxGnLy+vDxqtCwuLHR2dNTa7ISezKS23KyurMTO5GyKxOzy/ACCXwAAN7AAF+IAjIEAw8DPArtsAOSupIHW2HBvAsUBAHIAbwAAARwAiEdgA/caAr+CAgCIALAjAOMcJIGCAAB8EADGAAByiAAArcQcSrtHBOT3zYG/AaQA7sVgE3Ib9wCCv88AZ2wAAaIAANUAAG+M4wEjogAc9wCCv8SwGbvGBeRyAIEAAF/PrBZsxwCucgDWAIRvpMUBaHIAAQAAAFCMfEIjj/kcUb+CAAC4sLAZKOL3AIG/AECQoAfGowBy9wAAvwBQAABCAAD5UAC/ALgALBlguPcaUb+CAMRwpNUFaAAAAQAAAOAAAPgAAHIAAAAAAG24DBgZAPf3UL+/AM/QAGzWAKIAUNUAAADgfAD4jwByUQAAAKJtQNUYAH/3AFe/AGTPABNsAACuAADWAAAAMgAABUYAAAIAAACuGQDWBQh/AABXAAJH1AABx8QAcgsAAAMAUAAApTdG9wYCv3oAADMAAKUBULAAAEcCZwEApQCM9wDDv0ACAAcAAABfUAAWAKUAQ7AAAEelAAGwAPhHQ8UBAHIAAAAAABhwFIkFxX8AUIEAABClAtKwADxHAIIBABAUopPXKKZm99Iov/gf+MUXx3IAcgAAAAxwZsYFtHIA9wAAvxgAAIlgAH8aUIGCAHiM1QrDtD0C94IAvxAAAJMASqYChdIAgQxw78YFGXIA+AAAvw4AM6EAGvcA+L8AvyYAALMASveNhb+tgQBKLGAEuDzNUYIBAADRmwABbgAA9wAAv8+kFmzYxWYCUNYAAG+k4wHYKAACQgAAAAAAHmAAxTsoUIIAADwcvcZ6bnIC9wAAvxgAm4kAbn8A94EAv0QABAoAAD0MAIIXACH5BAAAAAAALAAAAABIADAABwj/AAEIHEiwoMGDCBMqXGgQgsOHECNKnEixosWLGDNq3Mixo8ePIEOKHCkxgMkRBkiq7BhAgEmXAlbKxBgAwoALAyDUnMlzYs0EA1LuVIGiKAoVEg8YRXFxQoOnJpZKLYpUY0wII2wKGLBg54EPYD+YkEgi7AemFS2AVRuW7Qe3aB8K8ACAA4UFDy+IcGhg54URFx4+CFthIlsLDCqiKGD2bWOzcSE4MOjAoQYAck0GCPyQweGJFcI+qMiA8eO2azFAFIDQwAUOAEBcRNGWhEQTZg9QRFDg8NrTkSUglKBAYOWLbhNHVGG2wcSvYVNE3GC28MPJA0VoUJCAIAfOFiOE/0UwEYPjCBMbmK0quC17CBkGHodwgcJADRlxO9YdMXTY9w6Jx1gIEZXg2AclQHSBfBLZ911GzIU1VkRufTCBRL8595AKbo0G0QkCUUCRdhuFEBZ6EJFQoXUPTWCWbQ/5B9aFEF0GAF4TCQAeRuqFVeBvH3gYo2gQ0eaYahHR5cFKZTm2AUQIPAajQ4OBpZxD5oXFH0TxzUdSlR9c+VWFFiToUJNgPemQfmCxyCUAO44ko5BsukWeQz1aYMGGg7kVwpQOZSDBTC46BqN4bxn4n0MmgoUiBD2iJhaXGfDUnENuHSCeWmNFCNaEEJRQoVmgalDpTIh+oBp1YJEXKXpsfv+wJQol1IqAbxZU5cCpMuHGlgoMrJUYdG9BEJpaBFKEQoVmLsDrSsSKBSaMYC4blpkUoVmsZT1FCVaj20Lg2bcvXnQsWDDKxtO4jbFoZGNCVsTqWxr2BEGTvtGIqaRuZmsWtvaO+sF7Moalr0VuAdxTwR88uuaBYAH43G8pqItRnBkVGla9DnmqlsOK/btkRhnguFGEbG35UJZg1TtBBHdGFCxbKHSA30UBAHCCRywnG1GksjrkrYXLgTnwAgB4KZEAsN3MUaRXQkSszzJaAGiwJzpUnNJywZa0R9BZEBlED6glpltlokDCuW+h9RoAO0EUgNd7fdQnaWBFpqjAWT9gBCIAxJ1wggLCCcSByR0F22+R4a7cWIUIAIjdQRxc9RFtoBq2uAopnPZBCBJL5jVBElgOUq4WVZB5kZt+u3pECygggkAZdICxvbjnrvvuvPfu++/ABy88Q8QXb/zxAgUEADs=
@@ -0,0 +1 @@
]1337;File=inline=1;size=2159;name=Li4vdGVzdGltYWdlcy93M2NfaG9tZV8yNTYuanBn:/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAwAEgDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD33/hXPgn/AKE7w3/4K4P/AImj/hXPgn/oTvDf/grg/wDia8y/aLuNX0PWtB1nS7+8ghcGJ4o5mWPejbhlQcHIYj6LXr07J4l8IO1jM8K6lZboZUbayb0yrAjoRkH8K6J4flpwqX0l+BhGrzSlC2qM7/hXPgn/AKE7w3/4K4P/AImj/hXPgn/oTvDf/grg/wDia81/Zt8S313da3ousXk9xcR7biLz5S7DB2OMknvsrov2ifEEujeB0trSd4bq/uFiDRsVYIvzMQR9FH/AquWDlHELD3+ZMcQnS9qdR/wrnwT/ANCd4b/8FcH/AMTR/wAK58E/9Cd4b/8ABXB/8TXhFr4F8dL4SXX5fFCWOntbfays1/OrqhGRkKpGSMcZzyB1q7+z9D4g8QeKzqF3q2pSaZpwzIsty7LJIwIVME4Pcn0wPUVvPL4xhKaqJqJnHFNyUXFq57V/wrnwT/0J3hv/AMFcH/xNH/CufBP/AEJ3hv8A8FcH/wATXgnxP8YePPiB8Sr/AMI/DKe8tbLSG8u4ntZ/ILSKcOzy5BVQ2VCg87ScHoF0/wAcfE3T9Z8O/D7xJZSNr0mq20635YYuLJG3upZeGHycsOdoYEZ6+Ydh6Pd2fgW38Qvpn/CA+G2EaNKzfYoPMEStIrPs8voDDJ37D+JlUld9ceFNNnvzcyISGfzGjKoQW+Yn5iu8Al3yoYA73yPmbJQBzvx30X+2fhtqRRN01jtvE9tmdx/74L1ynw18Z/Y/gVqN20n+laMktuhJ5y3MX4ZdV/CvZ7qCO6tpredQ8UqGN1PdSMEfrXxPqb6hoD674WDExveKkygHLmJnC4Hvuz74Fexl8FiaToy6NP5dTz8VJ0Zqouqa/wAjb+Ed/P4c8f8Ah++uEeO0v2MIc8K6Oxjzn0DgH/gNdt8bpH8V/FnQ/DFu5KQeXC+P4GlIZz+CbD+FT/Grwf8A2L8NvCk1uu2fSQtvMyccuNzN/wB/F/8AH6p/AeO48V/E/VfE2ohWkgRpiQOFkk+VQPYLvH4V3SqQmnjV0TXzvoc6jKP+zPq0/wDM0P2k/FRt47Twnp+Y4QizXOOAQPuR/QY3H/gNdx8BtQ8PS+CoLDQJibm3G+9jlULIZW6uR3BxgEE8ADqK7nUdG0zUlcajp1pdb12t50KuSPqRXzl4bgTwP+0C2m2EhSwMxgKs/HlSR7wpP+ySp5/u1wUnDE4Z0Y6OKv6nTNSpVlUeqenoej/s26IuneAZdSmX/iY6xf3N3cuw+YkSsgB+gXOOxY16bNY2s17b3k1vE91bh1hlZQWjDY3BT2zgZ+lQ6JpcGj2H2O0GIfOllA9PMkaQgewLEVeJx1ryDvFooooAK8K8X+C/tf7QWjTCPNneKt9KSPl3QjDD8dsf/fdel/8ACxvBP/Q4+G//AAaQf/FVG3j/AMCNcJO3i3wwZ0VkSQ6lBuVWILAHdkA7VyO+B6V0YevKg249U0ZVaSqJJ9Hcu/EDRf8AhIfBesaYF3yT27eUP+mi/Mn/AI8BXm3wa8Panb/CDUbjRZ/sms6mzy28xUEgJ8qDDDAztbntuzXoH/CxvBP/AEOPhv8A8GkH/wAVUdv4/wDAltCkNv4t8MRRIMKialAqj6ANTp4mUKTpLun939IUqKlPn8rHkXg340XnhuwuNJ8Y2N9eX9s77ZWbEuSSdkm70PQjtgY4ql8KdCu/iD4+1LxNrVsTppMpkyPkd3QoI1Pfarfhgetet6n4l+F+qzCbVNa8F3soG0PcXVrIwHpliavW3j7wHawJBa+LPC8MKDCRx6jAqqPQANgV1SxtNRl7KFpS3/4BjHDTuueV0tjg0+LX/CvL5/DfxPivRNCT9i1iKHzI7+AfddgORJjAYAEZ54yM8xd/ETV/jN4v0zw/4Btrux8PWV3DealqU67WZY3DKMAkAZXhc5YgdADXrereMPhxrNr9m1fxF4Qv7bOfKur22lTPrhmIpdK8ZfDnSLRbXSfEfhGxtV5ENtfW0SD/AICrAV5h2HbUVyv/AAsbwT/0OPhv/wAGkH/xVFAH/9k=
@@ -0,0 +1 @@
]1337;File=inline=1;size=1658;name=Li4vdGVzdGltYWdlcy93M2NfaG9tZV8yNTYucG5n:iVBORw0KGgoAAAANSUhEUgAAAEgAAAAwCAMAAACFQszZAAADAFBMVEUEAgSMiozMzsxERkR0jrwMQpy8urzc5vQ0ZqwkIiRsbmzc3tx0lsSUqtScnpwkWqz8/vxUfrwcHhy8xuRcXlxkisQUTqTs7uxEcrQ8Pjx8fny0wuQMDgyMjoxMTkwMRpy8vrw8arQsLix0dnTU2uyEnsykttysrqzEzuRsisTs8vwAgl8AADewABfiAACBAAlwzwPVbADkrqSB1thwbwLFAQByAG8AAAEcADBHYAf3GgK/ggIA/ACwaADjHByBggAAfA4AxgAAchQAACB0HEbVRwTk98WBvwGkAO7FYBNyG/cAgr/PAGdsAAGiAADVAABvAOMBaaIAHPcAgr90sBXVxgXkcgCBAABfz6wWbMcArnIA1gCEb1TFARNyAAAAAABQANRCaU35HFG/ggAAuLCwGSji9wCBvwD4kKAIxqMAcvcAAL8AUAAAQgAA+VAAvwC4AIQZYHb3GlG/ggDElFTVDxMAAAAAAADgAAD4AAByAAAAAABtuAwYGQD391C/vwDP0ABs1gCiAFDVAAAA4NQA+E0AclEAAACibUDVGAB/9wBXvwBkzwATbAAArgAA1gAAABgAAAVGAAACAAAArhUA1gUIfwAAVwADR9QAAcd0AHIlAAADAFAAAKU3RvcGAr96AAAzAAClAVCwAABHA2cBAKUAAPcACb/4AwAIAAAAX1AAFgClAEOwAABHpQABsAD4R0PFAQByAAAAAAAYlByJD8V/AFCBAAAQpQLSsAA8RwCCAQAQFKKT1yimZvfSKL/4H/jFF8dyAHIAAAAMlGbGD7RyAPcAAL8YAACJYAB/GlCBggB4ANUKCbQ9A/eCAL8QAACTAEqmA4XSAIEMlO/GDxlyAPgAAL8OADOhABr3APi/AL8mAACzAEr3GoW/IIEARoRgBHY8xVGCAQAAz5sAAW4AAPcAAL/PpB5s2MVmAlDWAABvpOMB2CgAAkIAAAAAABZgAMU7KFCCAAA8HL3Gem5yAvcAAL8YAJuJAG5/APeBAL9EAAQKAAA9DACCFwCk+0jYAAAACXBIWXMAAAAAAAAAAACdYiYyAAADIElEQVR4nO2WW3OqQAyAAUehArJWXcDSUiwg3f//A8/msheU0/bBhzNnutMOEZKPJJtkCYJHLfGg9V+CwjD82DwAFEah/oseABKrciXCR4CeVhsEqUkvRXdjkD2tQ57n4+SWuqFEQnyIVbRKARS3eo30oAfZI8m2lXBL8j89it6C5phqobwIsQn19aOE0AqtcfIsM8uZ1q3B0ELQjkp5J8QZzHWywxJ3LQNrNj1p7cKCsrVFAE1W6I9pik3ZBO9GFUATqPX0ewSb2DxN1hSb9B16NqDnLbrlQBBPy/Eo0M/5IWbvE8WX1sSPgV3O2ye4NuUMtNdKCd+o9Ov3LOdgTRsFeZQo1pQeUR61cBYz0AjOxy5JbKxfsG47lAaIbgCpDGw8R88hAimvADAdB5KBj2EqaffgGgRHY3xxDnGvdVqP4umlTYY4gNhbN4l+DoLUGEflLQiTwTFAEIWNEqUJblZk8Ba8iaVFoB40X0BKaJ/Rj8JsZuXVRO12fAFkbWLuiEFwuwB9dNECqPwKZKIYuSESildCwasCb3VUsvXzIseADmDfY0XJgQvA7EBuOgT3ta6/BAkuaHh1vNdWo6uJwfYs/Dp/A4LirrAREnRh77puGoYhwYaD0t59AxrBd5XhEIEWkzptkssa1oRu6T1IvwHRdCto5+GCloNTxD2E3J+XMN5xBAXUsS4MqM6WEy/wEG6830NmoIxTCvUycXILXxPyJ/M7wB2o5wmGHcUj+uRr9u081r+BBHukKIzWGwKewk9AZEwzYKROmR0+MTj5eZOhm3mEC4cGT1mFoe1nNpi44ab1aztSHEhhWrjHKwM97M0MzuDx9Drb/TC43oPQ2FRgzmWd2EypAoNN/SkSNW5oe6DcnSVYnwCFxNFJlXEGt46kOTfHkTWW9rQuJM0nLIRh6rEc4bE+FEOOS3MuYgGkO0NaOePTcPAPbMz+FU7G63UL52STLoIyrwInA63MNNJzgcphZz9km0gsgiZzJMGSDFWfxp/OlNWu4SPb48w//aRXgScLnWDQtZ33knR7CYL6dTa8/6Wv2l/Qj0GPWn8AP2jx/kucqREAAAAASUVORK5CYII=
+67
View File
@@ -0,0 +1,67 @@
#!/bin/bash
function print_palette() {
L=$(( LOWER / 256))
U=$(( (UPPER-1) / 256))
for ((p = $L; p <= $U; p++))
do
echo "slot $((p*256))..$((p*256+255)):"
echo -ne "\x1bP;1q"
for i in {0..15}
do
a=$((i * 16 + p * 256))
for j in {0..15}
do
echo -ne "#$((a+j))!6~"
done
echo -ne "\$-"
done
echo -e "\x1b\\"
done
}
colors=undefined
max_colors=undefined
echo "Terminal Reports (XTSMGRAPHICS):"
IFS=";" read -a REPLY -s -t 1 -d "S" -p $'\e[?1;1;0S'
[[ ${REPLY[1]} == "0" ]] && colors=${REPLY[2]}
echo "active colors: ${colors}"
IFS=";" read -a REPLY -s -t 1 -d "S" -p $'\e[?1;4;0S'
[[ ${REPLY[1]} == "0" ]] && max_colors=${REPLY[2]}
echo "max colors : ${max_colors}"
echo
# query up to colors by default
# if colors is undefined (no XTSMGRAPHICS), assume 256
ARG1=${1:-${colors}}
if [[ $colors == "undefined" ]]
then
ARG1=${1:-256}
fi
LOWER=0
UPPER=$ARG1
ARG2=${2:-undefined}
if [[ $ARG2 != "undefined" ]]
then
LOWER=ARG1
UPPER=ARG2
fi
if [[ $colors != "undefined" ]]
then
if [[ $colors -lt $UPPER ]] || [[ $colors -lt 256 ]]
then
echo -e "\x1b[33mNote: Active colors is smaller than test range."
echo -e "A spec-conform terminal may repeat colors in 'slot mod ${colors}'.\x1b[m"
echo
fi
else
echo -e "\x1b[33mNote: Cannot query active colors."
echo -e "The terminal may repeat colors beyond it max slot (e.g. slot mod 16).\x1b[m"
echo
fi
print_palette
+141
View File
@@ -0,0 +1,141 @@
#!/bin/bash
function smiling_smiley() {
echo -ne '\x1bP;2q"1;1;60;60
#6!60~$-
!60~$-
!60~$
!15?#1!4]!22?!4]$-
#6!60~$-
!60~$-
!60~$-
!60~$
!15?#1!4~!22?!4~$-
#6!60~$
!15?#1!30N$-
#6!60~$-
!60~$-
'
echo -ne '\x1b\\'
}
function indifferent_smiley() {
echo -ne '\x1bP;2q"1;1;60;60
#6!60~$-
!60~$-
!60~$
!15?#1!4]!22?!4]$-
#6!60~$-
!60~$-
!60~$-
!60~$-
!60~$
!15?#1!30N$-
#6!60~$-
!60~$-
'
echo -ne '\x1b\\'
}
function sad_smiley() {
echo -ne '\x1bP;2q"1;1;60;60
#6!60~$-
!60~$-
!60~$
!15?#1!4]!22?!4]$-
#6!60~$-
!60~$-
!60~$-
!60~$-
!60~$
!15?#1!30N$
!15?#1!4o!22?!4o$-
#6!60~$
!15?#1!4B!22?!4B$-
#6!60~$-
'
echo -ne '\x1b\\'
}
function smiling_smiley_slim() {
echo -ne '\x1bP;1q"1;1;60;60
$-
$-
$-
$-
$-
$-
!15?#1!4~!22?!4~$-
!15?#1!30N
'
echo -ne '\x1b\\'
}
function indifferent_smiley_slim() {
echo -ne '\x1bP;1q"1;1;60;60
$-
$-
$-
$-
$-
$-
!15?#6!4~!22?!4~$-
!15?!4o!22?!4o$ !15?#1!30N$-
!15?#6!4B!22?!4B
'
echo -ne '\x1b\\'
}
function sad_smiley_slim() {
echo -ne '\x1bP;1q"1;1;60;60
$-
$-
$-
$-
$-
$-
$-
!15?#1!30N$
!15?#1!4o!22?!4o$-
!15?#1!4B!22?!4B$-
'
echo -ne '\x1b\\'
}
function full() {
smiling_smiley
sleep .5
indifferent_smiley
sleep .5
sad_smiley
sleep .5
indifferent_smiley
sleep .5
smiling_smiley
}
function slim() {
smiling_smiley
sleep .5
indifferent_smiley_slim
sleep .5
sad_smiley_slim
sleep .5
indifferent_smiley_slim
sleep .5
smiling_smiley_slim
}
# clear screen and place cursor to 1;10
echo -ne '\x1b[2J\x1b[10;1H'
# switch sixel scrolling off
echo -ne '\x1b[?80h'
case "$1" in
full ) full ;;
slim ) slim ;;
esac
# re-enable sixel scrolling
echo -ne '\x1b[?80l'
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

+288
View File
@@ -0,0 +1,288 @@
#!/bin/bash
# Test cursor row, column placement after sixel image is sent.
# After a sixel image is displayed, the text cursor is moved to the
# row of the last sixel cursor position, but the column stays the same
# as it was before the sixel image was sent.
#
# This can be thought of as sixel images always ending with an
# implicit Graphics Carriage Return (`$`).
# ADDENDUM: It is not as simple as I thought. When a row of sixels
# straddles two rows of text, the text cursor can be left on the upper row.
# It seems up to three lines of pixels may be beneath any words printed.
#
# The rule for when this happens is not obvious to me, but can be seen
# with images of height: 21, 22, 23, 24, 41, 42, 81, 82, 83, 84...
#
# My guess:
# for a sixel image of height h, let a=(h-1)%6 and b=(h-1)%20,
# then, the text will overlap the image when a>b.
#
# If that is the case, then the entire list of heights for which this
# will happen on the VT340's 480 pixel high screen is:
#
# 21 22 23 24 41 42 81 82 83 84
# 101 102 141 142 143 144 161 162
# 201 202 203 204 221 222 261 262 263 264 281 282
# 321 322 323 324 341 342 381 382 383 384
# 401 402 441 442 443 444 461 462
#
# Note that there are 48 entries, so that means there's a 10% chance
# if heights are chosen randomly from 1 to 480. However, if one were
# to always pick heights which are a multiple of the character cell
# height (20px), then the chances are 0% as there are no problematic
# heights divisible by 20.
# Sixel images often do *not* end with a `-` (Graphics New Line = GNL)
# which sends the sixel cursor down 6 pixels. Any text printed next
# will potentially overlap the last row of sixels!
# I am not yet positive, but I believe that, in general, applications
# should send sixel images without a GNL but then send `^J`, a text
# newline (NL), before displaying more text or graphics.
# IMPORTANT: sometimes neither a graphics nor a text newline is wanted.
# For example, if an image is full screen, either newline would cause
# the top line to scroll off the screen.
# | Text cursor column | Text cursor row
# --------|--------------------|-------------------------------------
# !GNL !NL| Unchanged | Overlapping last line of graphics
# !GNL NL| Column=1 | First line immediately after graphic (usually)
# GNL !NL| Unchanged | _Sometimes_ overlapping graphics
# GNL NL| Column=1 | First *or* second line after graphic
CSI=$'\e[' # Control Sequence Introducer
DCS=$'\eP' # Device Control String
ST=$'\e\\' # String Terminator
set_cursor_pos() {
# Home, top left is row 1, col 1.
local row=$1 col=$2
echo -n ${CSI}${row}';'${col}'H'
}
reset_palette() {
# Send DECRSTS to load colors from a Color Table Report
echo -n ${DCS}'2$p'
echo -n "0;2;0;0;0/" # VT color #0 is black and BG text color
echo -n "1;2;20;20;79/" # VT color #1 is blue
echo -n "2;2;79;13;13/" # VT color #2 is red
echo -n "3;2;20;79;20/" # VT color #3 is green
echo -n "4;2;79;20;79/" # VT color #4 is magenta
echo -n "5;2;20;79;79/" # VT color #5 is cyan
echo -n "6;2;79;79;20/" # VT color #6 is yellow
echo -n "7;2;46;46;46/" # VT color #7 is gray 50% and FG text color
echo -n "8;2;26;26;26/" # VT color #8 is gray 25%
echo -n "9;2;33;33;59/" # VT color #9 is pastel blue
echo -n "10;2;59;26;26/" # VT color #10 is pastel red
echo -n "11;2;33;59;33/" # VT color #11 is pastel green
echo -n "12;2;59;33;59/" # VT color #12 is pastel magenta
echo -n "13;2;33;59;59/" # VT color #13 is pastel cyan
echo -n "14;2;59;59;33/" # VT color #14 is pastel yellow
echo -n "15;2;79;79;79" # VT color #15 is gray 75% and BOLD text color
echo -n ${ST} # String Terminator
}
# Generate square of size w with final graphics new line removed
square() {
# Given a color index number and (optionally) a size, row, and column,
# draw a square with top left corner at (row, column) and of size×size px.
# Default size 100×100px (10cols, 5 rows)
local -i color=${1:-1} # Default is color index 1 (blue)
local -i size=${2:-100} # Size in pixels (defaults to 100)
local -i row=$3 column=$4 # If set to 0, cursor is not moved
if [[ row -ne 0 && column -ne 0 ]]; then
set_cursor_pos $row $column
fi
# Draw a square of the right color & size
squaresize $color $size
}
squaresize() {
# Helper for square() that uses convert to return a sixel square of
# the right color ($1) and size ($2).
# Similar to this but with variable size squares:
# echo -n ${DCS}'0;0;0q"1;1;100;100#'${color}'!100~-!100~-!100~-!100~-!100~-!100~-!100~-!100~-!100~-!100~-!100~-!100~-!100~-!100~-!100~-!100~-!100N'${ST}
local color=${1:-1} # Default color index is 1 (blue)
local size=${2:-100} # Default size is 100x100
# Get a sixel string
local sq=$(convert -geometry ${size}x${size} xc:black sixel:-)
# Remove ImageMagick's extraneous Graphic New Line at end of image.
sq=${sq%-??}$'\e\\'
# VT340s always used the same color register for the first sixel
# color defined no matter what number it was assigned. That means,
# each time we send a new sixel image, the previous one's color
# palette gets changed. We don't want squares of all the same
# color, so remove the color definition and just use the defaults.
sq=${sq/\#0;2;0;0;0/}
# And finally, switch to the proper index for the color we want.
echo -n ${sq//\#0/#${color}}
}
squaregnl() {
# Same as square(), but sends a graphics newline at the end of the sixels.
# (Sticks a `-` before the String Terminator, "Esc \")
sq=$(square "${@}")
echo -n ${sq%??}$'-\e\\'
}
main() {
clear
reset_palette
show_labels
neither_graphic_nor_text 96 4 4 31 # size, color, row, column
text_newline_only 96 9 4 1
text_newline_only 84 9 4 14
graphics_new_line_only 100 1 4 51
graphics_new_line_only 96 1 4 64
set_cursor_pos 1000 1
}
neither_graphic_nor_text() {
# Typically sixel images should not end with a Graphics New Line (GNL)
# However, if a text newline isn't sent, there will be overlap.
local -i size color row column
read size color row column <<<"$@"
set_cursor_pos $((row++)) $column
echo -n "Height $size"
set_cursor_pos $((row++)) $column
# Three squares sent as separate sixel images, indented +1
for i in {1..3}; do
square $((color++)) $size
tput cuf 1
done
echo -n "overlap?"
}
text_newline_only() {
# USING A TEXT NEWLINE (NL) after an sixel image that does NOT
# have GNL is probably the best way to be on the text line
# immediately below the image. However, the text will still
# occasionally overlap the last four rows of pixels.
# Also, if multiple images are intended to be shown, there will
# usually be a gap between them when using a text newline.
# Overlap happens because the height of a text cell is 20 pixels
# and the height of a sixel is 6.
# for (h=0; h<480; h++) if ((h-1)%6 > (h-1)%20 ) { h }
# Let the pixel position of the top of the graphics cursor be 'Yg'
# and let the pixel position of the top of the corresponding cell
# of text which the text cursor will be placed on be 'Yt'. Note
# that Yg is evenly divisible by 6 and Yt, by 20. Taking the
# remainder, r, after dividing Yg by 20 tells us how many pixels
# down into a row of text the last line of sixels started. When
# r==0, the sixels started at the top of the text row.
# When r = 14, the sixels covered the bottom six pixels on the row
# of text. When 14 < r < 20, the sixel line impinged by r - 14
# pixels into the text row below and there is a chance the next
# text printed will overlap.
local -i size color row column
read size color row column <<<"$@"
local -i offset
offset=$((column-1))
set_cursor_pos $row 1
if ((offset)); then tput cuf $((offset)); fi
echo "Height $size"
# Three squares, separated by text new lines and indented +1
for i in {1..3}; do
if ((offset)); then tput cuf $offset; fi
square $((color++)) $size
offset=offset+1
echo
done
tput cuf $((offset))
echo -n "overlap?"
}
graphics_new_line_only() {
# However, some sixel images end with a `-`, a Graphics New Line.
# This can be useful for writing another image starting at the same
# column without having to reposition the cursor.
#
# However, this runs the risk of having occasional overlap.
local -i size color row column
read size color row column <<<"$@"
set_cursor_pos $((row++)) $column
echo -n "Height $size"
set_cursor_pos $((row++)) $column
# Three squares, separated by graphics new lines and indented +1
for i in {1..3}; do
squaregnl $((color++)) $size
tput cuf 1
done
echo -n "overlap?"
}
show_labels() {
set_cursor_pos 1 10
echo -n "Should sixel images include a GNL ('-') at the end?"
set_cursor_pos 3 29
echo -n "Neither NL nor GNL"
set_cursor_pos 3 1
echo -n "Text New Line only"
set_cursor_pos 3 51
echo -n "Graphics New Line only"
set_cursor_pos 22 29
echo -n "Always overlaps" # Neither NL nor GNL
set_cursor_pos 22 3
echo -n "Overlaps a little" # NL only
set_cursor_pos 23 3
echo -n " Gaps a little" # NL only
set_cursor_pos 22 54
echo -n "Overlaps badly" # GNL only
set_cursor_pos 23 54
echo -n "Never gaps" # GNL only
}
main
+31
View File
@@ -0,0 +1,31 @@
{
"name": "xterm-addon-image",
"version": "0.6.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
},
"main": "lib/xterm-addon-image.js",
"types": "typings/xterm-addon-image.d.ts",
"repository": "https://github.com/xtermjs/xterm.js",
"license": "MIT",
"keywords": [
"terminal",
"image",
"sixel",
"xterm",
"xterm.js"
],
"scripts": {
"prepackage": "../../node_modules/.bin/tsc -p .",
"package": "../../node_modules/.bin/webpack",
"prepublishOnly": "npm run package"
},
"peerDependencies": {
"xterm": "^5.2.0"
},
"devDependencies": {
"sixel": "^0.16.0",
"xterm-wasm-parts": "^0.1.0"
}
}
+161
View File
@@ -0,0 +1,161 @@
/**
* Copyright (c) 2023 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IImageAddonOptions, IOscHandler, IResetHandler, ITerminalExt } from './Types';
import { ImageRenderer } from './ImageRenderer';
import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage';
import Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm';
import { HeaderParser, IHeaderFields, HeaderState } from './IIPHeaderParser';
import { imageType, UNSUPPORTED_TYPE } from './IIPMetrics';
// eslint-disable-next-line
declare const Buffer: any;
// limit hold memory in base64 decoder
const KEEP_DATA = 4194304;
// default IIP header values
const DEFAULT_HEADER: IHeaderFields = {
name: 'Unnamed file',
size: 0,
width: 'auto',
height: 'auto',
preserveAspectRatio: 1,
inline: 0
};
export class IIPHandler implements IOscHandler, IResetHandler {
private _aborted = false;
private _hp = new HeaderParser();
private _header: IHeaderFields = DEFAULT_HEADER;
private _dec = new Base64Decoder(KEEP_DATA);
private _metrics = UNSUPPORTED_TYPE;
constructor(
private readonly _opts: IImageAddonOptions,
private readonly _renderer: ImageRenderer,
private readonly _storage: ImageStorage,
private readonly _coreTerminal: ITerminalExt
) {}
public reset(): void {}
public start(): void {
this._aborted = false;
this._header = DEFAULT_HEADER;
this._metrics = UNSUPPORTED_TYPE;
this._hp.reset();
}
public put(data: Uint32Array, start: number, end: number): void {
if (this._aborted) return;
if (this._hp.state === HeaderState.END) {
if (this._dec.put(data, start, end)) {
this._dec.release();
this._aborted = true;
}
} else {
const dataPos = this._hp.parse(data, start, end);
if (dataPos === -1) {
this._aborted = true;
return;
}
if (dataPos > 0) {
this._header = Object.assign({}, DEFAULT_HEADER, this._hp.fields);
if (!this._header.inline || !this._header.size || this._header.size > this._opts.iipSizeLimit) {
this._aborted = true;
return;
}
this._dec.init(this._header.size);
if (this._dec.put(data, dataPos, end)) {
this._dec.release();
this._aborted = true;
}
}
}
}
public end(success: boolean): boolean | Promise<boolean> {
if (this._aborted) return true;
let w = 0;
let h = 0;
// early exit condition chain
let cond: number | boolean = true;
if (cond = success) {
if (cond = !this._dec.end()) {
this._metrics = imageType(this._dec.data8);
if (cond = this._metrics.mime !== 'unsupported') {
w = this._metrics.width;
h = this._metrics.height;
if (cond = w && h && w * h < this._opts.pixelLimit) {
[w, h] = this._resize(w, h).map(Math.floor);
cond = w && h && w * h < this._opts.pixelLimit;
}
}
}
}
if (!cond) {
this._dec.release();
return true;
}
const blob = new Blob([this._dec.data8], { type: this._metrics.mime });
this._dec.release();
if (!window.createImageBitmap) {
const url = URL.createObjectURL(blob);
const img = new Image();
return new Promise<boolean>(r => {
img.addEventListener('load', () => {
URL.revokeObjectURL(url);
const canvas = ImageRenderer.createCanvas(window.document, w, h);
canvas.getContext('2d')?.drawImage(img, 0, 0, w, h);
this._storage.addImage(canvas);
r(true);
});
img.src = url;
// sanity measure to avoid terminal blocking from dangling promise
// happens from corrupt data (onload never gets fired)
setTimeout(() => r(true), 1000);
});
}
return createImageBitmap(blob, { resizeWidth: w, resizeHeight: h })
.then(bm => {
this._storage.addImage(bm);
return true;
});
}
private _resize(w: number, h: number): [number, number] {
const cw = this._renderer.dimensions?.css.cell.width || CELL_SIZE_DEFAULT.width;
const ch = this._renderer.dimensions?.css.cell.height || CELL_SIZE_DEFAULT.height;
const width = this._renderer.dimensions?.css.canvas.width || cw * this._coreTerminal.cols;
const height = this._renderer.dimensions?.css.canvas.height || ch * this._coreTerminal.rows;
const rw = this._dim(this._header.width!, width, cw);
const rh = this._dim(this._header.height!, height, ch);
if (!rw && !rh) {
const wf = width / w; // TODO: should this respect initial cursor offset?
const hf = (height - ch) / h; // TODO: fix offset issues from float cell height
const f = Math.min(wf, hf);
return f < 1 ? [w * f, h * f] : [w, h];
}
return !rw
? [w * rh / h, rh]
: this._header.preserveAspectRatio || !rw || !rh
? [rw, h * rw / w] : [rw, rh];
}
private _dim(s: string, total: number, cdim: number): number {
if (s === 'auto') return 0;
if (s.endsWith('%')) return parseInt(s.slice(0, -1)) * total / 100;
if (s.endsWith('px')) return parseInt(s.slice(0, -2));
return parseInt(s) * cdim;
}
}
@@ -0,0 +1,138 @@
/**
* Copyright (c) 2023 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { HeaderParser, HeaderState, IHeaderFields } from './IIPHeaderParser';
const CASES: [string, IHeaderFields][] = [
['File=size=123456;name=dGVzdA==:', {name: 'test', size: 123456}],
['File=size=123456;name=dGVzdA:', {name: 'test', size: 123456}],
// utf-8 encoding in name
['File=size=123456;name=w7xtbMOkdXTDnw==:', {name: 'ümläutß', size: 123456}],
['File=size=123456;name=w7xtbMOkdXTDnw:', {name: 'ümläutß', size: 123456}],
// full header spec
[
'File=inline=1;width=10px;height=20%;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw:',
{
inline: 1,
width: '10px',
height: '20%',
preserveAspectRatio: 1,
size: 123456,
name: 'ümläutß'
}
],
[
'File=inline=1;width=auto;height=20;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw:',
{
inline: 1,
width: 'auto',
height: '20',
preserveAspectRatio: 1,
size: 123456,
name: 'ümläutß'
}
]
];
function fromBs(bs: string): Uint32Array {
const r = new Uint32Array(bs.length);
for (let i = 0; i < r.length; ++i) r[i] = bs.charCodeAt(i);
return r;
}
describe('IIPHeaderParser', () => {
it('at once', () => {
const hp = new HeaderParser();
for (const example of CASES) {
hp.reset();
const inp = fromBs(example[0]);
const res = hp.parse(inp, 0, inp.length);
assert.strictEqual(res, inp.length);
assert.strictEqual(hp.state, HeaderState.END);
assert.deepEqual(hp.fields, example[1]);
}
});
it('bytewise', () => {
const hp = new HeaderParser();
for (const example of CASES) {
hp.reset();
const inp = fromBs(example[0]);
let pos = 0;
let res = -2;
while (res === -2 && pos < inp.length) {
res = hp.parse(new Uint32Array([inp[pos++]]), 0, 1);
}
assert.strictEqual(res, 1);
assert.strictEqual(hp.state, HeaderState.END);
assert.deepEqual(hp.fields, example[1]);
}
});
it('no File= starter', () => {
const hp = new HeaderParser();
let inp = fromBs('size=123456;name=dGVzdA==:');
let res = hp.parse(inp, 0, inp.length);
assert.strictEqual(res, -1);
hp.reset();
inp = fromBs(CASES[0][0]);
res = hp.parse(inp, 0, inp.length);
assert.strictEqual(res, inp.length);
assert.strictEqual(hp.state, HeaderState.END);
assert.deepEqual(hp.fields, CASES[0][1]);
});
it('empty key - error', () => {
const hp = new HeaderParser();
let inp = fromBs('File=size=123456;=dGVzdA==:');
let res = hp.parse(inp, 0, inp.length);
assert.strictEqual(res, -1);
hp.reset();
inp = fromBs(CASES[0][0]);
res = hp.parse(inp, 0, inp.length);
assert.strictEqual(res, inp.length);
assert.strictEqual(hp.state, HeaderState.END);
assert.deepEqual(hp.fields, CASES[0][1]);
});
it('empty size value - set to 0', () => {
const hp = new HeaderParser();
let inp = fromBs('File=size=;name=dGVzdA==:');
let res = hp.parse(inp, 0, inp.length);
assert.strictEqual(res, inp.length);
assert.strictEqual(hp.state, HeaderState.END);
assert.deepEqual(hp.fields, {name: 'test', size: 0});
hp.reset();
inp = fromBs(CASES[0][0]);
res = hp.parse(inp, 0, inp.length);
assert.strictEqual(res, inp.length);
assert.strictEqual(hp.state, HeaderState.END);
assert.deepEqual(hp.fields, CASES[0][1]);
});
it('empty name value - set to empty string', () => {
const hp = new HeaderParser();
let inp = fromBs('File=size=123456;name=:');
let res = hp.parse(inp, 0, inp.length);
assert.strictEqual(res, inp.length);
assert.strictEqual(hp.state, HeaderState.END);
assert.deepEqual(hp.fields, {name: '', size: 123456});
hp.reset();
inp = fromBs(CASES[0][0]);
res = hp.parse(inp, 0, inp.length);
assert.strictEqual(res, inp.length);
assert.strictEqual(hp.state, HeaderState.END);
assert.deepEqual(hp.fields, CASES[0][1]);
});
it('empty size value - error', () => {
const hp = new HeaderParser();
let inp = fromBs('File=inline=1;width=;height=20%;preserveAspectRatio=1;size=123456;name=w7xtbMOkdXTDnw:');
let res = hp.parse(inp, 0, inp.length);
assert.strictEqual(res, -1);
hp.reset();
inp = fromBs(CASES[0][0]);
res = hp.parse(inp, 0, inp.length);
assert.strictEqual(res, inp.length);
assert.strictEqual(hp.state, HeaderState.END);
assert.deepEqual(hp.fields, CASES[0][1]);
});
});
@@ -0,0 +1,186 @@
/**
* Copyright (c) 2023 The xterm.js authors. All rights reserved.
* @license MIT
*/
// eslint-disable-next-line
declare const Buffer: any;
export interface IHeaderFields {
// base-64 encoded filename. Defaults to "Unnamed file".
name: string;
// File size in bytes. The file transfer will be canceled if this size is exceeded.
size: number;
/**
* Optional width and height to render:
* - N: N character cells.
* - Npx: N pixels.
* - N%: N percent of the session's width or height.
* - auto: The image's inherent size will be used to determine an appropriate dimension.
*/
width?: string;
height?: string;
// Optional, defaults to 1 respecting aspect ratio (width takes precedence).
preserveAspectRatio?: number;
// Optional, defaults to 0. If set to 1, the file will be displayed inline, else downloaded
// (download not supported).
inline?: number;
}
export const enum HeaderState {
START = 0,
ABORT = 1,
KEY = 2,
VALUE = 3,
END = 4
}
// field value decoders
// ASCII bytes to string
function toStr(data: Uint32Array): string {
let s = '';
for (let i = 0; i < data.length; ++i) {
s += String.fromCharCode(data[i]);
}
return s;
}
// digits to integer
function toInt(data: Uint32Array): number {
let v = 0;
for (let i = 0; i < data.length; ++i) {
if (data[i] < 48 || data[i] > 57) {
throw new Error('illegal char');
}
v = v * 10 + data[i] - 48;
}
return v;
}
// check for correct size entry
function toSize(data: Uint32Array): string {
const v = toStr(data);
if (!v.match(/^((auto)|(\d+?((px)|(%)){0,1}))$/)) {
throw new Error('illegal size');
}
return v;
}
// name is base64 encoded utf-8
function toName(data: Uint32Array): string {
if (typeof Buffer !== 'undefined') {
return Buffer.from(toStr(data), 'base64').toString();
}
const bs = atob(toStr(data));
const b = new Uint8Array(bs.length);
for (let i = 0; i < b.length; ++i) {
b[i] = bs.charCodeAt(i);
}
return new TextDecoder().decode(b);
}
const DECODERS: {[key: string]: (v: Uint32Array) => any} = {
inline: toInt,
size: toInt,
name: toName,
width: toSize,
height: toSize,
preserveAspectRatio: toInt
};
const FILE_MARKER = [70, 105, 108, 101];
const MAX_FIELDCHARS = 1024;
export class HeaderParser {
public state: HeaderState = HeaderState.START;
private _buffer = new Uint32Array(MAX_FIELDCHARS);
private _position = 0;
private _key = '';
public fields: {[key: string]: any} = {};
public reset(): void {
this._buffer.fill(0);
this.state = HeaderState.START;
this._position = 0;
this.fields = {};
this._key = '';
}
public parse(data: Uint32Array, start: number, end: number): number {
let state = this.state;
let pos = this._position;
const buffer = this._buffer;
if (state === HeaderState.ABORT || state === HeaderState.END) return -1;
if (state === HeaderState.START && pos > 6) return -1;
for (let i = start; i < end; ++i) {
const c = data[i];
switch (c) {
case 59: // ;
if (!this._storeValue(pos)) return this._a();
state = HeaderState.KEY;
pos = 0;
break;
case 61: // =
if (state === HeaderState.START) {
for (let k = 0; k < FILE_MARKER.length; ++k) {
if (buffer[k] !== FILE_MARKER[k]) return this._a();
}
state = HeaderState.KEY;
pos = 0;
} else if (state === HeaderState.KEY) {
if (!this._storeKey(pos)) return this._a();
state = HeaderState.VALUE;
pos = 0;
} else if (state === HeaderState.VALUE) {
if (pos >= MAX_FIELDCHARS) return this._a();
buffer[pos++] = c;
}
break;
case 58: // :
if (state === HeaderState.VALUE) {
if (!this._storeValue(pos)) return this._a();
}
this.state = HeaderState.END;
return i + 1;
default:
if (pos >= MAX_FIELDCHARS) return this._a();
buffer[pos++] = c;
}
}
this.state = state;
this._position = pos;
return -2;
}
private _a(): number {
this.state = HeaderState.ABORT;
return -1;
}
private _storeKey(pos: number): boolean {
const k = toStr(this._buffer.subarray(0, pos));
if (k) {
this._key = k;
this.fields[k] = null;
return true;
}
return false;
}
private _storeValue(pos: number): boolean {
if (this._key) {
try {
const v = this._buffer.slice(0, pos);
this.fields[this._key] = DECODERS[this._key] ? DECODERS[this._key](v) : v;
} catch (e) {
return false;
}
return true;
}
return false;
}
}
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2023 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { imageType, IMetrics } from './IIPMetrics';
// fix missing nodejs decl
declare const require: (s: string) => any;
const fs = require('fs');
const TEST_IMAGES: [string, IMetrics][] = [
['w3c_home_256.gif', { mime: 'image/gif', width: 72, height: 48 }],
['w3c_home_256.jpg', { mime: 'image/jpeg', width: 72, height: 48 }],
['w3c_home_256.png', { mime: 'image/png', width: 72, height: 48 }],
['w3c_home_2.gif', { mime: 'image/gif', width: 72, height: 48 }],
['w3c_home_2.jpg', { mime: 'image/jpeg', width: 72, height: 48 }],
['w3c_home_2.png', { mime: 'image/png', width: 72, height: 48 }],
['w3c_home_animation.gif', { mime: 'image/gif', width: 72, height: 48 }],
['w3c_home.gif', { mime: 'image/gif', width: 72, height: 48 }],
['w3c_home_gray.gif', { mime: 'image/gif', width: 72, height: 48 }],
['w3c_home_gray.jpg', { mime: 'image/jpeg', width: 72, height: 48 }],
['w3c_home_gray.png', { mime: 'image/png', width: 72, height: 48 }],
['w3c_home.jpg', { mime: 'image/jpeg', width: 72, height: 48 }],
['w3c_home.png', { mime: 'image/png', width: 72, height: 48 }],
['spinfox.png', { mime: 'image/png', width: 148, height: 148 }],
['iphone_hdr_YES.jpg', { mime: 'image/jpeg', width: 3264, height: 2448 }],
['nikon-e950.jpg', { mime: 'image/jpeg', width: 800, height: 600 }],
['agfa-makernotes.jpg', { mime: 'image/jpeg', width: 8, height: 8 }],
['sony-alpha-6000.jpg', { mime: 'image/jpeg', width: 6000, height: 4000 }]
];
describe('IIPMetrics', () => {
it('bunch of testimages', () => {
for (let i = 0; i < TEST_IMAGES.length; ++i) {
const imageData = fs.readFileSync('./addons/xterm-addon-image/fixture/testimages/' + TEST_IMAGES[i][0]);
assert.deepStrictEqual(imageType(imageData), TEST_IMAGES[i][1]);
}
});
});
@@ -0,0 +1,81 @@
/**
* Copyright (c) 2023 The xterm.js authors. All rights reserved.
* @license MIT
*/
export type ImageType = 'image/png' | 'image/jpeg' | 'image/gif' | 'unsupported' | '';
export interface IMetrics {
mime: ImageType;
width: number;
height: number;
}
export const UNSUPPORTED_TYPE: IMetrics = {
mime: 'unsupported',
width: 0,
height: 0
};
export function imageType(d: Uint8Array): IMetrics {
if (d.length < 24) {
return UNSUPPORTED_TYPE;
}
const d32 = new Uint32Array(d.buffer, d.byteOffset, 6);
// PNG: 89 50 4E 47 0D 0A 1A 0A (8 first bytes == magic number for PNG)
// + first chunk must be IHDR
if (d32[0] === 0x474E5089 && d32[1] === 0x0A1A0A0D && d32[3] === 0x52444849) {
return {
mime: 'image/png',
width: d[16] << 24 | d[17] << 16 | d[18] << 8 | d[19],
height: d[20] << 24 | d[21] << 16 | d[22] << 8 | d[23]
};
}
// JPEG: FF D8 FF E0 xx xx JFIF or FF D8 FF E1 xx xx Exif 00 00
if ((d32[0] === 0xE0FFD8FF || d32[0] === 0xE1FFD8FF)
&& (
(d[6] === 0x4a && d[7] === 0x46 && d[8] === 0x49 && d[9] === 0x46)
|| (d[6] === 0x45 && d[7] === 0x78 && d[8] === 0x69 && d[9] === 0x66)
)
) {
const [width, height] = jpgSize(d);
return { mime: 'image/jpeg', width, height };
}
// GIF: GIF87a or GIF89a
if (d32[0] === 0x38464947 && (d[4] === 0x37 || d[4] === 0x39) && d[5] === 0x61) {
return {
mime: 'image/gif',
width: d[7] << 8 | d[6],
height: d[9] << 8 | d[8]
};
}
return UNSUPPORTED_TYPE;
}
function jpgSize(d: Uint8Array): [number, number] {
const len = d.length;
let i = 4;
let blockLength = d[i] << 8 | d[i + 1];
while (true) {
i += blockLength;
if (i >= len) {
// exhausted without size info
return [0, 0];
}
if (d[i] !== 0xFF) {
return [0, 0];
}
if (d[i + 1] === 0xC0 || d[i + 1] === 0xC2) {
if (i + 8 < len) {
return [
d[i + 7] << 8 | d[i + 8],
d[i + 5] << 8 | d[i + 6]
];
}
return [0, 0];
}
i += 2;
blockLength = d[i] << 8 | d[i + 1];
}
}
+317
View File
@@ -0,0 +1,317 @@
/**
* Copyright (c) 2020 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IIPHandler } from './IIPHandler';
import { ITerminalAddon, IDisposable } from 'xterm';
import { ImageRenderer } from './ImageRenderer';
import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage';
import { SixelHandler } from './SixelHandler';
import { ITerminalExt, IImageAddonOptions, IResetHandler } from './Types';
// default values of addon ctor options
const DEFAULT_OPTIONS: IImageAddonOptions = {
enableSizeReports: true,
pixelLimit: 16777216, // limit to 4096 * 4096 pixels
sixelSupport: true,
sixelScrolling: true,
sixelPaletteLimit: 256,
sixelSizeLimit: 25000000,
storageLimit: 128,
showPlaceholder: true,
iipSupport: true,
iipSizeLimit: 20000000
};
// max palette size supported by the sixel lib (compile time setting)
const MAX_SIXEL_PALETTE_SIZE = 4096;
// definitions for _xtermGraphicsAttributes sequence
const enum GaItem {
COLORS = 1,
SIXEL_GEO = 2,
REGIS_GEO = 3
}
const enum GaAction {
READ = 1,
SET_DEFAULT = 2,
SET = 3,
READ_MAX = 4
}
const enum GaStatus {
SUCCESS = 0,
ITEM_ERROR = 1,
ACTION_ERROR = 2,
FAILURE = 3
}
export class ImageAddon implements ITerminalAddon {
private _opts: IImageAddonOptions;
private _defaultOpts: IImageAddonOptions;
private _storage: ImageStorage | undefined;
private _renderer: ImageRenderer | undefined;
private _disposables: IDisposable[] = [];
private _terminal: ITerminalExt | undefined;
private _handlers: Map<String, IResetHandler> = new Map();
constructor(opts?: Partial<IImageAddonOptions>) {
this._opts = Object.assign({}, DEFAULT_OPTIONS, opts);
this._defaultOpts = Object.assign({}, DEFAULT_OPTIONS, opts);
}
public dispose(): void {
for (const obj of this._disposables) {
obj.dispose();
}
this._disposables.length = 0;
this._handlers.clear();
}
private _disposeLater(...args: IDisposable[]): void {
for (const obj of args) {
this._disposables.push(obj);
}
}
public activate(terminal: ITerminalExt): void {
this._terminal = terminal;
// internal data structures
this._renderer = new ImageRenderer(terminal);
this._storage = new ImageStorage(terminal, this._renderer, this._opts);
// enable size reports
if (this._opts.enableSizeReports) {
// const windowOptions = terminal.getOption('windowOptions');
// windowOptions.getWinSizePixels = true;
// windowOptions.getCellSizePixels = true;
// windowOptions.getWinSizeChars = true;
// terminal.setOption('windowOptions', windowOptions);
const windowOps = terminal.options.windowOptions || {};
windowOps.getWinSizePixels = true;
windowOps.getCellSizePixels = true;
windowOps.getWinSizeChars = true;
terminal.options.windowOptions = windowOps;
}
this._disposeLater(
this._renderer,
this._storage,
// DECSET/DECRST/DA1/XTSMGRAPHICS handlers
terminal.parser.registerCsiHandler({ prefix: '?', final: 'h' }, params => this._decset(params)),
terminal.parser.registerCsiHandler({ prefix: '?', final: 'l' }, params => this._decrst(params)),
terminal.parser.registerCsiHandler({ final: 'c' }, params => this._da1(params)),
terminal.parser.registerCsiHandler({ prefix: '?', final: 'S' }, params => this._xtermGraphicsAttributes(params)),
// render hook
terminal.onRender(range => this._storage?.render(range)),
/**
* reset handlers covered:
* - DECSTR
* - RIS
* - Terminal.reset()
*/
terminal.parser.registerCsiHandler({ intermediates: '!', final: 'p' }, () => this.reset()),
terminal.parser.registerEscHandler({ final: 'c' }, () => this.reset()),
terminal._core._inputHandler.onRequestReset(() => this.reset()),
// wipe canvas and delete alternate images on buffer switch
terminal.buffer.onBufferChange(() => this._storage?.wipeAlternate()),
// extend images to the right on resize
terminal.onResize(metrics => this._storage?.viewportResize(metrics))
);
// SIXEL handler
if (this._opts.sixelSupport) {
const sixelHandler = new SixelHandler(this._opts, this._storage!, terminal);
this._handlers.set('sixel', sixelHandler);
this._disposeLater(
terminal._core._inputHandler._parser.registerDcsHandler({ final: 'q' }, sixelHandler)
);
}
// iTerm IIP handler
if (this._opts.iipSupport) {
const iipHandler = new IIPHandler(this._opts, this._renderer!, this._storage!, terminal);
this._handlers.set('iip', iipHandler);
this._disposeLater(
terminal._core._inputHandler._parser.registerOscHandler(1337, iipHandler)
);
}
}
// Note: storageLimit is skipped here to not intoduce a surprising side effect.
public reset(): boolean {
// reset options customizable by sequences to defaults
this._opts.sixelScrolling = this._defaultOpts.sixelScrolling;
this._opts.sixelPaletteLimit = this._defaultOpts.sixelPaletteLimit;
// also clear image storage
this._storage?.reset();
// reset protocol handlers
for (const handler of this._handlers.values()) {
handler.reset();
}
return false;
}
public get storageLimit(): number {
return this._storage?.getLimit() || -1;
}
public set storageLimit(limit: number) {
this._storage?.setLimit(limit);
this._opts.storageLimit = limit;
}
public get storageUsage(): number {
if (this._storage) {
return this._storage.getUsage();
}
return -1;
}
public get showPlaceholder(): boolean {
return this._opts.showPlaceholder;
}
public set showPlaceholder(value: boolean) {
this._opts.showPlaceholder = value;
this._renderer?.showPlaceholder(value);
}
public getImageAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined {
return this._storage?.getImageAtBufferCell(x, y);
}
public extractTileAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined {
return this._storage?.extractTileAtBufferCell(x, y);
}
private _report(s: string): void {
this._terminal?._core.coreService.triggerDataEvent(s);
}
private _decset(params: (number | number[])[]): boolean {
for (let i = 0; i < params.length; ++i) {
switch (params[i]) {
case 80:
this._opts.sixelScrolling = false;
break;
}
}
return false;
}
private _decrst(params: (number | number[])[]): boolean {
for (let i = 0; i < params.length; ++i) {
switch (params[i]) {
case 80:
this._opts.sixelScrolling = true;
break;
}
}
return false;
}
// overload DA to return something more appropriate
private _da1(params: (number | number[])[]): boolean {
if (params[0]) {
return true;
}
// reported features:
// 62 - VT220
// 4 - SIXEL support
// 9 - charsets
// 22 - ANSI colors
if (this._opts.sixelSupport) {
this._report(`\x1b[?62;4;9;22c`);
return true;
}
return false;
}
/**
* Implementation of xterm's graphics attribute sequence.
*
* Supported features:
* - read/change palette limits (max 4096 by sixel lib)
* - read SIXEL canvas geometry (reports current window canvas or
* squared pixelLimit if canvas > pixel limit)
*
* Everything else is deactivated.
*/
private _xtermGraphicsAttributes(params: (number | number[])[]): boolean {
if (params.length < 2) {
return true;
}
if (params[0] === GaItem.COLORS) {
switch (params[1]) {
case GaAction.READ:
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${this._opts.sixelPaletteLimit}S`);
return true;
case GaAction.SET_DEFAULT:
this._opts.sixelPaletteLimit = this._defaultOpts.sixelPaletteLimit;
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${this._opts.sixelPaletteLimit}S`);
// also reset protocol handlers for now
for (const handler of this._handlers.values()) {
handler.reset();
}
return true;
case GaAction.SET:
if (params.length > 2 && !(params[2] instanceof Array) && params[2] <= MAX_SIXEL_PALETTE_SIZE) {
this._opts.sixelPaletteLimit = params[2];
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${this._opts.sixelPaletteLimit}S`);
} else {
this._report(`\x1b[?${params[0]};${GaStatus.ACTION_ERROR}S`);
}
return true;
case GaAction.READ_MAX:
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${MAX_SIXEL_PALETTE_SIZE}S`);
return true;
default:
this._report(`\x1b[?${params[0]};${GaStatus.ACTION_ERROR}S`);
return true;
}
}
if (params[0] === GaItem.SIXEL_GEO) {
switch (params[1]) {
// we only implement read and read_max here
case GaAction.READ:
let width = this._renderer?.dimensions?.css.canvas.width;
let height = this._renderer?.dimensions?.css.canvas.height;
if (!width || !height) {
// for some reason we have no working image renderer
// --> fallback to default cell size
const cellSize = CELL_SIZE_DEFAULT;
width = (this._terminal?.cols || 80) * cellSize.width;
height = (this._terminal?.rows || 24) * cellSize.height;
}
if (width * height < this._opts.pixelLimit) {
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${width.toFixed(0)};${height.toFixed(0)}S`);
} else {
// if we overflow pixelLimit report that squared instead
const x = Math.floor(Math.sqrt(this._opts.pixelLimit));
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${x};${x}S`);
}
return true;
case GaAction.READ_MAX:
// read_max returns pixelLimit as square area
const x = Math.floor(Math.sqrt(this._opts.pixelLimit));
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${x};${x}S`);
return true;
default:
this._report(`\x1b[?${params[0]};${GaStatus.ACTION_ERROR}S`);
return true;
}
}
// exit with error on ReGIS or any other requests
this._report(`\x1b[?${params[0]};${GaStatus.ITEM_ERROR}S`);
return true;
}
}
@@ -0,0 +1,379 @@
/**
* Copyright (c) 2020 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { toRGBA8888 } from 'sixel/lib/Colors';
import { IDisposable } from 'xterm';
import { ICellSize, ITerminalExt, IImageSpec, IRenderDimensions, IRenderService } from './Types';
import { Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle';
const PLACEHOLDER_LENGTH = 4096;
const PLACEHOLDER_HEIGHT = 24;
/**
* ImageRenderer - terminal frontend extension:
* - provide primitives for canvas, ImageData, Bitmap (static)
* - add canvas layer to DOM (browser only for now)
* - draw image tiles onRender
*/
export class ImageRenderer extends Disposable implements IDisposable {
public canvas: HTMLCanvasElement | undefined;
private _ctx: CanvasRenderingContext2D | null | undefined;
private _placeholder: HTMLCanvasElement | undefined;
private _placeholderBitmap: ImageBitmap | undefined;
private _optionsRefresh = this.register(new MutableDisposable());
private _oldOpen: ((parent: HTMLElement) => void) | undefined;
private _renderService: IRenderService | undefined;
private _oldSetRenderer: ((renderer: any) => void) | undefined;
// drawing primitive - canvas
public static createCanvas(localDocument: Document | undefined, width: number, height: number): HTMLCanvasElement {
/**
* NOTE: We normally dont care, from which document the canvas
* gets created, so we can fall back to global document,
* if the terminal has no document associated yet.
* This way early image loads before calling .open keep working
* (still discouraged though, as the metrics will be screwed up).
* Only the DOM output canvas should be on the terminal's document,
* which gets explicitly checked in `insertLayerToDom`.
*/
const canvas = (localDocument || document).createElement('canvas');
canvas.width = width | 0;
canvas.height = height | 0;
return canvas;
}
// drawing primitive - ImageData with optional buffer
public static createImageData(ctx: CanvasRenderingContext2D, width: number, height: number, buffer?: ArrayBuffer): ImageData {
if (typeof ImageData !== 'function') {
const imgData = ctx.createImageData(width, height);
if (buffer) {
imgData.data.set(new Uint8ClampedArray(buffer, 0, width * height * 4));
}
return imgData;
}
return buffer
? new ImageData(new Uint8ClampedArray(buffer, 0, width * height * 4), width, height)
: new ImageData(width, height);
}
// drawing primitive - ImageBitmap
public static createImageBitmap(img: ImageBitmapSource): Promise<ImageBitmap | undefined> {
if (typeof createImageBitmap !== 'function') {
return Promise.resolve(undefined);
}
return createImageBitmap(img);
}
constructor(private _terminal: ITerminalExt) {
super();
this._oldOpen = this._terminal._core.open;
this._terminal._core.open = (parent: HTMLElement): void => {
this._oldOpen?.call(this._terminal._core, parent);
this._open();
};
if (this._terminal._core.screenElement) {
this._open();
}
// hack to spot fontSize changes
this._optionsRefresh.value = this._terminal._core.optionsService.onOptionChange(option => {
if (option === 'fontSize') {
this.rescaleCanvas();
this._renderService?.refreshRows(0, this._terminal.rows);
}
});
this.register(toDisposable(() => {
this.removeLayerFromDom();
if (this._terminal._core && this._oldOpen) {
this._terminal._core.open = this._oldOpen;
this._oldOpen = undefined;
}
if (this._renderService && this._oldSetRenderer) {
this._renderService.setRenderer = this._oldSetRenderer;
this._oldSetRenderer = undefined;
}
this._renderService = undefined;
this.canvas = undefined;
this._ctx = undefined;
this._placeholderBitmap?.close();
this._placeholderBitmap = undefined;
this._placeholder = undefined;
}));
}
/**
* Enable the placeholder.
*/
public showPlaceholder(value: boolean): void {
if (value) {
if (!this._placeholder && this.cellSize.height !== -1) {
this._createPlaceHolder(Math.max(this.cellSize.height + 1, PLACEHOLDER_HEIGHT));
}
} else {
this._placeholderBitmap?.close();
this._placeholderBitmap = undefined;
this._placeholder = undefined;
}
this._renderService?.refreshRows(0, this._terminal.rows);
}
/**
* Dimensions of the terminal.
* Forwarded from internal render service.
*/
public get dimensions(): IRenderDimensions | undefined {
return this._renderService?.dimensions;
}
/**
* Current cell size (float).
*/
public get cellSize(): ICellSize {
return {
width: this.dimensions?.css.cell.width || -1,
height: this.dimensions?.css.cell.height || -1
};
}
/**
* Clear a region of the image layer canvas.
*/
public clearLines(start: number, end: number): void {
this._ctx?.clearRect(
0,
start * (this.dimensions?.css.cell.height || 0),
this.dimensions?.css.canvas.width || 0,
(++end - start) * (this.dimensions?.css.cell.height || 0)
);
}
/**
* Clear whole image canvas.
*/
public clearAll(): void {
this._ctx?.clearRect(0, 0, this.canvas?.width || 0, this.canvas?.height || 0);
}
/**
* Draw neighboring tiles on the image layer canvas.
*/
public draw(imgSpec: IImageSpec, tileId: number, col: number, row: number, count: number = 1): void {
if (!this._ctx) {
return;
}
const { width, height } = this.cellSize;
// Don't try to draw anything, if we cannot get valid renderer metrics.
if (width === -1 || height === -1) {
return;
}
this._rescaleImage(imgSpec, width, height);
const img = imgSpec.actual!;
const cols = Math.ceil(img.width / width);
const sx = (tileId % cols) * width;
const sy = Math.floor(tileId / cols) * height;
const dx = col * width;
const dy = row * height;
// safari bug: never access image source out of bounds
const finalWidth = count * width + sx > img.width ? img.width - sx : count * width;
const finalHeight = sy + height > img.height ? img.height - sy : height;
// Floor all pixel offsets to get stable tile mapping without any overflows.
// Note: For not pixel perfect aligned cells like in the DOM renderer
// this will move a tile slightly to the top/left (subpixel range, thus ignore it).
// FIX #34: avoid striping on displays with pixelDeviceRatio != 1 by ceiling height and width
this._ctx.drawImage(
img,
Math.floor(sx), Math.floor(sy), Math.ceil(finalWidth), Math.ceil(finalHeight),
Math.floor(dx), Math.floor(dy), Math.ceil(finalWidth), Math.ceil(finalHeight)
);
}
/**
* Extract a single tile from an image.
*/
public extractTile(imgSpec: IImageSpec, tileId: number): HTMLCanvasElement | undefined {
const { width, height } = this.cellSize;
// Don't try to draw anything, if we cannot get valid renderer metrics.
if (width === -1 || height === -1) {
return;
}
this._rescaleImage(imgSpec, width, height);
const img = imgSpec.actual!;
const cols = Math.ceil(img.width / width);
const sx = (tileId % cols) * width;
const sy = Math.floor(tileId / cols) * height;
const finalWidth = width + sx > img.width ? img.width - sx : width;
const finalHeight = sy + height > img.height ? img.height - sy : height;
const canvas = ImageRenderer.createCanvas(this.document, finalWidth, finalHeight);
const ctx = canvas.getContext('2d');
if (ctx) {
ctx.drawImage(
img,
Math.floor(sx), Math.floor(sy), Math.floor(finalWidth), Math.floor(finalHeight),
0, 0, Math.floor(finalWidth), Math.floor(finalHeight)
);
return canvas;
}
}
/**
* Draw a line with placeholder on the image layer canvas.
*/
public drawPlaceholder(col: number, row: number, count: number = 1): void {
if (this._ctx) {
const { width, height } = this.cellSize;
// Don't try to draw anything, if we cannot get valid renderer metrics.
if (width === -1 || height === -1) {
return;
}
if (!this._placeholder) {
this._createPlaceHolder(Math.max(height + 1, PLACEHOLDER_HEIGHT));
} else if (height >= this._placeholder!.height) {
this._createPlaceHolder(height + 1);
}
if (!this._placeholder) return;
this._ctx.drawImage(
this._placeholderBitmap || this._placeholder!,
col * width,
(row * height) % 2 ? 0 : 1, // needs %2 offset correction
width * count,
height,
col * width,
row * height,
width * count,
height
);
}
}
/**
* Rescale image layer canvas if needed.
* Checked once from `ImageStorage.render`.
*/
public rescaleCanvas(): void {
if (!this.canvas) {
return;
}
if (this.canvas.width !== this.dimensions!.css.canvas.width || this.canvas.height !== this.dimensions!.css.canvas.height) {
this.canvas.width = this.dimensions!.css.canvas.width || 0;
this.canvas.height = this.dimensions!.css.canvas.height || 0;
}
}
/**
* Rescale image in storage if needed.
*/
private _rescaleImage(spec: IImageSpec, currentWidth: number, currentHeight: number): void {
if (currentWidth === spec.actualCellSize.width && currentHeight === spec.actualCellSize.height) {
return;
}
const { width: originalWidth, height: originalHeight } = spec.origCellSize;
if (currentWidth === originalWidth && currentHeight === originalHeight) {
spec.actual = spec.orig;
spec.actualCellSize.width = originalWidth;
spec.actualCellSize.height = originalHeight;
return;
}
const canvas = ImageRenderer.createCanvas(
this.document,
Math.ceil(spec.orig!.width * currentWidth / originalWidth),
Math.ceil(spec.orig!.height * currentHeight / originalHeight)
);
const ctx = canvas.getContext('2d');
if (ctx) {
ctx.drawImage(spec.orig!, 0, 0, canvas.width, canvas.height);
spec.actual = canvas;
spec.actualCellSize.width = currentWidth;
spec.actualCellSize.height = currentHeight;
}
}
/**
* Lazy init for the renderer.
*/
private _open(): void {
this._renderService = this._terminal._core._renderService;
this._oldSetRenderer = this._renderService.setRenderer.bind(this._renderService);
this._renderService.setRenderer = (renderer: any) => {
this.removeLayerFromDom();
this._oldSetRenderer?.call(this._renderService, renderer);
};
}
public insertLayerToDom(): void {
// make sure that the terminal is attached to a document and to DOM
if (this.document && this._terminal._core.screenElement) {
if (!this.canvas) {
this.canvas = ImageRenderer.createCanvas(
this.document, this.dimensions?.css.canvas.width || 0,
this.dimensions?.css.canvas.height || 0
);
this.canvas.classList.add('xterm-image-layer');
this._terminal._core.screenElement.appendChild(this.canvas);
this._ctx = this.canvas.getContext('2d', { alpha: true, desynchronized: true });
this.clearAll();
}
} else {
console.warn('image addon: cannot insert output canvas to DOM, missing document or screenElement');
}
}
public removeLayerFromDom(): void {
if (this.canvas) {
this._ctx = undefined;
this.canvas.remove();
this.canvas = undefined;
}
}
private _createPlaceHolder(height: number = PLACEHOLDER_HEIGHT): void {
this._placeholderBitmap?.close();
this._placeholderBitmap = undefined;
// create blueprint to fill placeholder with
const bWidth = 32; // must be 2^n
const blueprint = ImageRenderer.createCanvas(this.document, bWidth, height);
const ctx = blueprint.getContext('2d', { alpha: false });
if (!ctx) return;
const imgData = ImageRenderer.createImageData(ctx, bWidth, height);
const d32 = new Uint32Array(imgData.data.buffer);
const black = toRGBA8888(0, 0, 0);
const white = toRGBA8888(255, 255, 255);
d32.fill(black);
for (let y = 0; y < height; ++y) {
const shift = y % 2;
const offset = y * bWidth;
for (let x = 0; x < bWidth; x += 2) {
d32[offset + x + shift] = white;
}
}
ctx.putImageData(imgData, 0, 0);
// create placeholder line, width aligned to blueprint width
const width = (screen.width + bWidth - 1) & ~(bWidth - 1) || PLACEHOLDER_LENGTH;
this._placeholder = ImageRenderer.createCanvas(this.document, width, height);
const ctx2 = this._placeholder.getContext('2d', { alpha: false });
if (!ctx2) {
this._placeholder = undefined;
return;
}
for (let i = 0; i < width; i += bWidth) {
ctx2.drawImage(blueprint, i, 0);
}
ImageRenderer.createImageBitmap(this._placeholder).then(bitmap => this._placeholderBitmap = bitmap);
}
public get document(): Document | undefined {
return this._terminal._core._coreBrowserService?.window.document;
}
}
@@ -0,0 +1,592 @@
/**
* Copyright (c) 2020 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IDisposable } from 'xterm';
import { ImageRenderer } from './ImageRenderer';
import { ITerminalExt, IExtendedAttrsImage, IImageAddonOptions, IImageSpec, IBufferLineExt, BgFlags, Cell, Content, ICellSize, ExtFlags, Attributes, UnderlineStyle } from './Types';
// fallback default cell size
export const CELL_SIZE_DEFAULT: ICellSize = {
width: 7,
height: 14
};
/**
* Extend extended attribute to also hold image tile information.
*
* Object definition is copied from base repo to fully mimick its behavior.
* Image data is added as additional public properties `imageId` and `tileId`.
*/
class ExtendedAttrsImage implements IExtendedAttrsImage {
private _ext: number = 0;
public get ext(): number {
if (this._urlId) {
return (
(this._ext & ~ExtFlags.UNDERLINE_STYLE) |
(this.underlineStyle << 26)
);
}
return this._ext;
}
public set ext(value: number) { this._ext = value; }
public get underlineStyle(): UnderlineStyle {
// Always return the URL style if it has one
if (this._urlId) {
return UnderlineStyle.DASHED;
}
return (this._ext & ExtFlags.UNDERLINE_STYLE) >> 26;
}
public set underlineStyle(value: UnderlineStyle) {
this._ext &= ~ExtFlags.UNDERLINE_STYLE;
this._ext |= (value << 26) & ExtFlags.UNDERLINE_STYLE;
}
public get underlineColor(): number {
return this._ext & (Attributes.CM_MASK | Attributes.RGB_MASK);
}
public set underlineColor(value: number) {
this._ext &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
this._ext |= value & (Attributes.CM_MASK | Attributes.RGB_MASK);
}
private _urlId: number = 0;
public get urlId(): number {
return this._urlId;
}
public set urlId(value: number) {
this._urlId = value;
}
constructor(
ext: number = 0,
urlId: number = 0,
public imageId = -1,
public tileId = -1
) {
this._ext = ext;
this._urlId = urlId;
}
public clone(): IExtendedAttrsImage {
/**
* Technically we dont need a clone variant of ExtendedAttrsImage,
* as we never clone a cell holding image data.
* Note: Clone is only meant to be used by the InputHandler for
* sticky attributes, which is never the case for image data.
* We still provide a proper clone method to reflect the full ext attr
* state in case there are future use cases for clone.
*/
return new ExtendedAttrsImage(this._ext, this._urlId, this.imageId, this.tileId);
}
public isEmpty(): boolean {
return this.underlineStyle === UnderlineStyle.NONE && this._urlId === 0 && this.imageId === -1;
}
}
const EMPTY_ATTRS = new ExtendedAttrsImage();
/**
* ImageStorage - extension of CoreTerminal:
* - hold image data
* - write/read image data to/from buffer
*
* TODO: image composition for overwrites
*/
export class ImageStorage implements IDisposable {
// storage
private _images: Map<number, IImageSpec> = new Map();
// last used id
private _lastId = 0;
// last evicted id
private _lowestId = 0;
// whether a full clear happened before
private _fullyCleared = false;
// whether render should do a full clear
private _needsFullClear = false;
// hard limit of stored pixels (fallback limit of 10 MB)
private _pixelLimit: number = 2500000;
private _viewportMetrics: { cols: number, rows: number };
constructor(
private _terminal: ITerminalExt,
private _renderer: ImageRenderer,
private _opts: IImageAddonOptions
) {
try {
this.setLimit(this._opts.storageLimit);
} catch (e: any) {
console.error(e.message);
console.warn(`storageLimit is set to ${this.getLimit()} MB`);
}
this._viewportMetrics = {
cols: this._terminal.cols,
rows: this._terminal.rows
};
}
public dispose(): void {
this.reset();
}
public reset(): void {
for (const spec of this._images.values()) {
spec.marker?.dispose();
}
// NOTE: marker.dispose above already calls ImageBitmap.close
// therefore we can just wipe the map here
this._images.clear();
this._renderer.clearAll();
}
public getLimit(): number {
return this._pixelLimit * 4 / 1000000;
}
public setLimit(value: number): void {
if (value < 0.5 || value > 1000) {
throw RangeError('invalid storageLimit, should be at least 0.5 MB and not exceed 1G');
}
this._pixelLimit = (value / 4 * 1000000) >>> 0;
this._evictOldest(0);
}
public getUsage(): number {
return this._getStoredPixels() * 4 / 1000000;
}
private _getStoredPixels(): number {
let storedPixels = 0;
for (const spec of this._images.values()) {
if (spec.orig) {
storedPixels += spec.orig.width * spec.orig.height;
if (spec.actual && spec.actual !== spec.orig) {
storedPixels += spec.actual.width * spec.actual.height;
}
}
}
return storedPixels;
}
private _delImg(id: number): void {
const spec = this._images.get(id);
this._images.delete(id);
// FIXME: really ugly workaround to get bitmaps deallocated :(
if (spec && window.ImageBitmap && spec.orig instanceof ImageBitmap) {
spec.orig.close();
}
}
/**
* Wipe canvas and images on alternate buffer.
*/
public wipeAlternate(): void {
// remove all alternate tagged images
const zero = [];
for (const [id, spec] of this._images.entries()) {
if (spec.bufferType === 'alternate') {
spec.marker?.dispose();
zero.push(id);
}
}
for (const id of zero) {
this._delImg(id);
}
// mark canvas to be wiped on next render
this._needsFullClear = true;
this._fullyCleared = false;
}
/**
* Only advance text cursor.
* This is an edge case from empty sixels carrying only a height but no pixels.
* Partially fixes https://github.com/jerch/xterm-addon-image/issues/37.
*/
public advanceCursor(height: number): void {
if (this._opts.sixelScrolling) {
let cellSize = this._renderer.cellSize;
if (cellSize.width === -1 || cellSize.height === -1) {
cellSize = CELL_SIZE_DEFAULT;
}
const rows = Math.ceil(height / cellSize.height);
for (let i = 1; i < rows; ++i) {
this._terminal._core._inputHandler.lineFeed();
}
}
}
/**
* Method to add an image to the storage.
*/
public addImage(img: HTMLCanvasElement | ImageBitmap): void {
// never allow storage to exceed memory limit
this._evictOldest(img.width * img.height);
// calc rows x cols needed to display the image
let cellSize = this._renderer.cellSize;
if (cellSize.width === -1 || cellSize.height === -1) {
cellSize = CELL_SIZE_DEFAULT;
}
const cols = Math.ceil(img.width / cellSize.width);
const rows = Math.ceil(img.height / cellSize.height);
const imageId = ++this._lastId;
const buffer = this._terminal._core.buffer;
const termCols = this._terminal.cols;
const termRows = this._terminal.rows;
const originX = buffer.x;
const originY = buffer.y;
let offset = originX;
let tileCount = 0;
if (!this._opts.sixelScrolling) {
buffer.x = 0;
buffer.y = 0;
offset = 0;
}
this._terminal._core._inputHandler._dirtyRowTracker.markDirty(buffer.y);
for (let row = 0; row < rows; ++row) {
const line = buffer.lines.get(buffer.y + buffer.ybase);
for (let col = 0; col < cols; ++col) {
if (offset + col >= termCols) break;
this._writeToCell(line as IBufferLineExt, offset + col, imageId, row * cols + col);
tileCount++;
}
if (this._opts.sixelScrolling) {
if (row < rows - 1) this._terminal._core._inputHandler.lineFeed();
} else {
if (++buffer.y >= termRows) break;
}
buffer.x = offset;
}
this._terminal._core._inputHandler._dirtyRowTracker.markDirty(buffer.y);
// cursor positioning modes
if (this._opts.sixelScrolling) {
buffer.x = offset;
} else {
buffer.x = originX;
buffer.y = originY;
}
// deleted images with zero tile count
const zero = [];
for (const [id, spec] of this._images.entries()) {
if (spec.tileCount < 1) {
spec.marker?.dispose();
zero.push(id);
}
}
for (const id of zero) {
this._delImg(id);
}
// eviction marker:
// delete the image when the marker gets disposed
const endMarker = this._terminal.registerMarker(0);
endMarker?.onDispose(() => {
const spec = this._images.get(imageId);
if (spec) {
this._delImg(imageId);
}
});
// since markers do not work on alternate for some reason,
// we evict images here manually
if (this._terminal.buffer.active.type === 'alternate') {
this._evictOnAlternate();
}
// create storage entry
const imgSpec: IImageSpec = {
orig: img,
origCellSize: cellSize,
actual: img,
actualCellSize: { ...cellSize }, // clone needed, since later modified
marker: endMarker || undefined,
tileCount,
bufferType: this._terminal.buffer.active.type
};
// finally add the image
this._images.set(imageId, imgSpec);
}
/**
* Render method. Collects buffer information and triggers
* canvas updates.
*/
// TODO: Should we move this to the ImageRenderer?
public render(range: { start: number, end: number }): void {
// setup image canvas in case we have none yet, but have images in store
if (!this._renderer.canvas && this._images.size) {
this._renderer.insertLayerToDom();
// safety measure - in case we cannot spawn a canvas at all, just exit
if (!this._renderer.canvas) {
return;
}
}
// rescale if needed
this._renderer.rescaleCanvas();
// exit early if we dont have any images to test for
if (!this._images.size) {
if (!this._fullyCleared) {
this._renderer.clearAll();
this._fullyCleared = true;
this._needsFullClear = false;
}
if (this._renderer.canvas) {
this._renderer.removeLayerFromDom();
}
return;
}
// buffer switches force a full clear
if (this._needsFullClear) {
this._renderer.clearAll();
this._fullyCleared = true;
this._needsFullClear = false;
}
const { start, end } = range;
const buffer = this._terminal._core.buffer;
const cols = this._terminal._core.cols;
// clear drawing area
this._renderer.clearLines(start, end);
// walk all cells in viewport and draw tiles found
for (let row = start; row <= end; ++row) {
const line = buffer.lines.get(row + buffer.ydisp) as IBufferLineExt;
if (!line) return;
for (let col = 0; col < cols; ++col) {
if (line.getBg(col) & BgFlags.HAS_EXTENDED) {
let e: IExtendedAttrsImage = line._extendedAttrs[col] || EMPTY_ATTRS;
const imageId = e.imageId;
if (imageId === undefined || imageId === -1) {
continue;
}
const imgSpec = this._images.get(imageId);
if (e.tileId !== -1) {
const startTile = e.tileId;
const startCol = col;
let count = 1;
/**
* merge tiles to the right into a single draw call, if:
* - not at end of line
* - cell has same image id
* - cell has consecutive tile id
*/
while (
++col < cols
&& (line.getBg(col) & BgFlags.HAS_EXTENDED)
&& (e = line._extendedAttrs[col] || EMPTY_ATTRS)
&& (e.imageId === imageId)
&& (e.tileId === startTile + count)
) {
count++;
}
col--;
if (imgSpec) {
if (imgSpec.actual) {
this._renderer.draw(imgSpec, startTile, startCol, row, count);
}
} else if (this._opts.showPlaceholder) {
this._renderer.drawPlaceholder(startCol, row, count);
}
this._fullyCleared = false;
}
}
}
}
}
public viewportResize(metrics: { cols: number, rows: number }): void {
// exit early if we have nothing in storage
if (!this._images.size) {
this._viewportMetrics = metrics;
return;
}
// handle only viewport width enlargements, exit all other cases
// TODO: needs patch for tile counter
if (this._viewportMetrics.cols >= metrics.cols) {
this._viewportMetrics = metrics;
return;
}
// walk scrollbuffer at old col width to find all possible expansion matches
const buffer = this._terminal._core.buffer;
const rows = buffer.lines.length;
const oldCol = this._viewportMetrics.cols - 1;
for (let row = 0; row < rows; ++row) {
const line = buffer.lines.get(row) as IBufferLineExt;
if (line.getBg(oldCol) & BgFlags.HAS_EXTENDED) {
const e: IExtendedAttrsImage = line._extendedAttrs[oldCol] || EMPTY_ATTRS;
const imageId = e.imageId;
if (imageId === undefined || imageId === -1) {
continue;
}
const imgSpec = this._images.get(imageId);
if (!imgSpec) {
continue;
}
// found an image tile at oldCol, check if it qualifies for right exapansion
const tilesPerRow = Math.ceil((imgSpec.actual?.width || 0) / imgSpec.actualCellSize.width);
if ((e.tileId % tilesPerRow) + 1 >= tilesPerRow) {
continue;
}
// expand only if right side is empty (nothing got wrapped from below)
let hasData = false;
for (let rightCol = oldCol + 1; rightCol > metrics.cols; ++rightCol) {
if (line._data[rightCol * Cell.SIZE + Cell.CONTENT] & Content.HAS_CONTENT_MASK) {
hasData = true;
break;
}
}
if (hasData) {
continue;
}
// do right expansion on terminal buffer
const end = Math.min(metrics.cols, tilesPerRow - (e.tileId % tilesPerRow) + oldCol);
let lastTile = e.tileId;
for (let expandCol = oldCol + 1; expandCol < end; ++expandCol) {
this._writeToCell(line as IBufferLineExt, expandCol, imageId, ++lastTile);
imgSpec.tileCount++;
}
}
}
// store new viewport metrics
this._viewportMetrics = metrics;
}
/**
* Retrieve original canvas at buffer position.
*/
public getImageAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined {
const buffer = this._terminal._core.buffer;
const line = buffer.lines.get(y) as IBufferLineExt;
if (line && line.getBg(x) & BgFlags.HAS_EXTENDED) {
const e: IExtendedAttrsImage = line._extendedAttrs[x] || EMPTY_ATTRS;
if (e.imageId && e.imageId !== -1) {
const orig = this._images.get(e.imageId)?.orig;
if (window.ImageBitmap && orig instanceof ImageBitmap) {
const canvas = ImageRenderer.createCanvas(window.document, orig.width, orig.height);
canvas.getContext('2d')?.drawImage(orig, 0, 0, orig.width, orig.height);
return canvas;
}
return orig as HTMLCanvasElement;
}
}
}
/**
* Extract active single tile at buffer position.
*/
public extractTileAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined {
const buffer = this._terminal._core.buffer;
const line = buffer.lines.get(y) as IBufferLineExt;
if (line && line.getBg(x) & BgFlags.HAS_EXTENDED) {
const e: IExtendedAttrsImage = line._extendedAttrs[x] || EMPTY_ATTRS;
if (e.imageId && e.imageId !== -1 && e.tileId !== -1) {
const spec = this._images.get(e.imageId);
if (spec) {
return this._renderer.extractTile(spec, e.tileId);
}
}
}
}
// TODO: Do we need some blob offloading tricks here to avoid early eviction?
// also see https://stackoverflow.com/questions/28307789/is-there-any-limitation-on-javascript-max-blob-size
private _evictOldest(room: number): number {
const used = this._getStoredPixels();
let current = used;
while (this._pixelLimit < current + room && this._images.size) {
const spec = this._images.get(++this._lowestId);
if (spec && spec.orig) {
current -= spec.orig.width * spec.orig.height;
if (spec.actual && spec.orig !== spec.actual) {
current -= spec.actual.width * spec.actual.height;
}
spec.marker?.dispose();
this._delImg(this._lowestId);
}
}
return used - current;
}
private _writeToCell(line: IBufferLineExt, x: number, imageId: number, tileId: number): void {
if (line._data[x * Cell.SIZE + Cell.BG] & BgFlags.HAS_EXTENDED) {
const old = line._extendedAttrs[x];
if (old) {
if (old.imageId !== undefined) {
// found an old ExtendedAttrsImage, since we know that
// they are always isolated instances (single cell usage),
// we can re-use it and just update their id entries
const oldSpec = this._images.get(old.imageId);
if (oldSpec) {
// early eviction for in-viewport overwrites
oldSpec.tileCount--;
}
old.imageId = imageId;
old.tileId = tileId;
return;
}
// found a plain ExtendedAttrs instance, clone it to new entry
line._extendedAttrs[x] = new ExtendedAttrsImage(old.ext, old.urlId, imageId, tileId);
return;
}
}
// fall-through: always create new ExtendedAttrsImage entry
line._data[x * Cell.SIZE + Cell.BG] |= BgFlags.HAS_EXTENDED;
line._extendedAttrs[x] = new ExtendedAttrsImage(0, 0, imageId, tileId);
}
private _evictOnAlternate(): void {
// nullify tile count of all images on alternate buffer
for (const spec of this._images.values()) {
if (spec.bufferType === 'alternate') {
spec.tileCount = 0;
}
}
// re-count tiles on whole buffer
const buffer = this._terminal._core.buffer;
for (let y = 0; y < this._terminal.rows; ++y) {
const line = buffer.lines.get(y) as IBufferLineExt;
if (!line) {
continue;
}
for (let x = 0; x < this._terminal.cols; ++x) {
if (line._data[x * Cell.SIZE + Cell.BG] & BgFlags.HAS_EXTENDED) {
const imgId = line._extendedAttrs[x]?.imageId;
if (imgId) {
const spec = this._images.get(imgId);
if (spec) {
spec.tileCount++;
}
}
}
}
}
// deleted images with zero tile count
const zero = [];
for (const [id, spec] of this._images.entries()) {
if (spec.bufferType === 'alternate' && !spec.tileCount) {
spec.marker?.dispose();
zero.push(id);
}
}
for (const id of zero) {
this._delImg(id);
}
}
}
@@ -0,0 +1,151 @@
/**
* Copyright (c) 2020, 2023 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ImageStorage } from './ImageStorage';
import { IDcsHandler, IParams, IImageAddonOptions, ITerminalExt, AttributeData, IResetHandler, ReadonlyColorSet } from './Types';
import { toRGBA8888, BIG_ENDIAN, PALETTE_ANSI_256, PALETTE_VT340_COLOR } from 'sixel/lib/Colors';
import { RGBA8888 } from 'sixel/lib/Types';
import { ImageRenderer } from './ImageRenderer';
import { DecoderAsync, Decoder } from 'sixel/lib/Decoder';
// always free decoder ressources after decoding if it exceeds this limit
const MEM_PERMA_LIMIT = 4194304; // 1024 pixels * 1024 pixels * 4 channels = 4MB
// custom default palette: VT340 (lower 16 colors) + ANSI256 (up to 256) + zeroed (up to 4096)
const DEFAULT_PALETTE = PALETTE_ANSI_256;
DEFAULT_PALETTE.set(PALETTE_VT340_COLOR);
export class SixelHandler implements IDcsHandler, IResetHandler {
private _size = 0;
private _aborted = false;
private _dec: Decoder | undefined;
constructor(
private readonly _opts: IImageAddonOptions,
private readonly _storage: ImageStorage,
private readonly _coreTerminal: ITerminalExt
) {
DecoderAsync({
memoryLimit: this._opts.pixelLimit * 4,
palette: DEFAULT_PALETTE,
paletteLimit: this._opts.sixelPaletteLimit
}).then(d => this._dec = d);
}
public reset(): void {
/**
* reset sixel decoder to defaults:
* - release all memory
* - nullify palette (4096)
* - apply default palette (256)
*/
if (this._dec) {
this._dec.release();
// FIXME: missing interface on decoder to nullify full palette
(this._dec as any)._palette.fill(0);
this._dec.init(0, DEFAULT_PALETTE, this._opts.sixelPaletteLimit);
}
}
public hook(params: IParams): void {
this._size = 0;
this._aborted = false;
if (this._dec) {
const fillColor = params.params[1] === 1 ? 0 : extractActiveBg(
this._coreTerminal._core._inputHandler._curAttrData,
this._coreTerminal._core._themeService?.colors);
this._dec.init(fillColor, null, this._opts.sixelPaletteLimit);
}
}
public put(data: Uint32Array, start: number, end: number): void {
if (this._aborted || !this._dec) {
return;
}
this._size += end - start;
if (this._size > this._opts.sixelSizeLimit) {
console.warn(`SIXEL: too much data, aborting`);
this._aborted = true;
this._dec.release();
return;
}
try {
this._dec.decode(data, start, end);
} catch (e) {
console.warn(`SIXEL: error while decoding image - ${e}`);
this._aborted = true;
this._dec.release();
}
}
public unhook(success: boolean): boolean | Promise<boolean> {
if (this._aborted || !success || !this._dec) {
return true;
}
const width = this._dec.width;
const height = this._dec.height;
// partial fix for https://github.com/jerch/xterm-addon-image/issues/37
if (!width || ! height) {
if (height) {
this._storage.advanceCursor(height);
}
return true;
}
const canvas = ImageRenderer.createCanvas(undefined, width, height);
canvas.getContext('2d')?.putImageData(new ImageData(this._dec.data8, width, height), 0, 0);
if (this._dec.memoryUsage > MEM_PERMA_LIMIT) {
this._dec.release();
}
this._storage.addImage(canvas);
return true;
}
}
/**
* Some helpers to extract current terminal colors.
*/
// get currently active background color from terminal
// also respect INVERSE setting
function extractActiveBg(attr: AttributeData, colors: ReadonlyColorSet | undefined): RGBA8888 {
let bg = 0;
if (!colors) {
// FIXME: theme service is prolly not available yet,
// happens if .open() was not called yet (bug in core?)
return bg;
}
if (attr.isInverse()) {
if (attr.isFgDefault()) {
bg = convertLe(colors.foreground.rgba);
} else if (attr.isFgRGB()) {
const t = (attr.constructor as typeof AttributeData).toColorRGB(attr.getFgColor());
bg = toRGBA8888(...t);
} else {
bg = convertLe(colors.ansi[attr.getFgColor()].rgba);
}
} else {
if (attr.isBgDefault()) {
bg = convertLe(colors.background.rgba);
} else if (attr.isBgRGB()) {
const t = (attr.constructor as typeof AttributeData).toColorRGB(attr.getBgColor());
bg = toRGBA8888(...t);
} else {
bg = convertLe(colors.ansi[attr.getBgColor()].rgba);
}
}
return bg;
}
// rgba values on the color managers are always in BE, thus convert to LE
function convertLe(color: number): RGBA8888 {
if (BIG_ENDIAN) return color;
return (color & 0xFF) << 24 | (color >>> 8 & 0xFF) << 16 | (color >>> 16 & 0xFF) << 8 | color >>> 24 & 0xFF;
}
+108
View File
@@ -0,0 +1,108 @@
/**
* Copyright (c) 2020 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IDisposable, IMarker, Terminal } from 'xterm';
// private imports from base repo we build against
import { Attributes, BgFlags, Content, ExtFlags, UnderlineStyle } from 'common/buffer/Constants';
import type { AttributeData } from 'common/buffer/AttributeData';
import type { IParams, IDcsHandler, IOscHandler, IEscapeSequenceParser } from 'common/parser/Types';
import type { IBufferLine, IExtendedAttrs, IInputHandler } from 'common/Types';
import type { ITerminal, ReadonlyColorSet } from 'browser/Types';
import type { IRenderDimensions } from 'browser/renderer/shared/Types';
import type { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
export const enum Cell {
CONTENT = 0, // codepoint and wcwidth information (enum Content)
FG = 1, // foreground color in lower 3 bytes (rgb), attrs in 4th byte (enum FgFlags)
BG = 2, // background color in lower 3 bytes (rgb), attrs in 4th byte (enum BgFlags)
SIZE = 3 // size of single cell on buffer array
}
// export some privates for local usage
export { AttributeData, IParams, IDcsHandler, IOscHandler, BgFlags, IRenderDimensions, IRenderService, Content, ExtFlags, Attributes, UnderlineStyle, ReadonlyColorSet };
/**
* Plugin ctor options.
*/
export interface IImageAddonOptions {
enableSizeReports: boolean;
pixelLimit: number;
storageLimit: number;
showPlaceholder: boolean;
sixelSupport: boolean;
sixelScrolling: boolean;
sixelPaletteLimit: number;
sixelSizeLimit: number;
iipSupport: boolean;
iipSizeLimit: number;
}
export interface IResetHandler {
// attached to RIS and DECSTR
reset(): void;
}
/**
* Stub into private interfaces.
* This should be kept in line with common libs.
* Any change made here should be replayed in the accessors test case to
* have a somewhat reliable testing against code changes in the core repo.
*/
// overloaded IExtendedAttrs to hold image refs
export interface IExtendedAttrsImage extends IExtendedAttrs {
imageId: number;
tileId: number;
clone(): IExtendedAttrsImage;
}
/* eslint-disable */
export interface IBufferLineExt extends IBufferLine {
_extendedAttrs: {[index: number]: IExtendedAttrsImage | undefined};
_data: Uint32Array;
}
interface IInputHandlerExt extends IInputHandler {
_parser: IEscapeSequenceParser;
_curAttrData: AttributeData;
_dirtyRowTracker: {
markRangeDirty(y1: number, y2: number): void;
markAllDirty(): void;
markDirty(y: number): void;
};
onRequestReset(handler: () => void): IDisposable;
}
export interface ICoreTerminalExt extends ITerminal {
_themeService: IThemeService | undefined;
_inputHandler: IInputHandlerExt;
_renderService: IRenderService;
_coreBrowserService: ICoreBrowserService | undefined;
}
export interface ITerminalExt extends Terminal {
_core: ICoreTerminalExt;
}
/* eslint-enable */
/**
* Some storage definitions.
*/
export interface ICellSize {
width: number;
height: number;
}
export interface IImageSpec {
orig: HTMLCanvasElement | ImageBitmap | undefined;
origCellSize: ICellSize;
actual: HTMLCanvasElement | ImageBitmap | undefined;
actualCellSize: ICellSize;
marker: IMarker | undefined;
tileCount: number;
bufferType: 'alternate' | 'normal';
}
@@ -0,0 +1,28 @@
{
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"sourceMap": true,
"outDir": "../out",
"rootDir": ".",
"strict": true,
"noUnusedLocals": true,
"preserveWatchOutput": true,
"types": [
"../../../node_modules/@types/mocha"
],
"baseUrl": ".",
"paths": {
"browser/*": [ "../../../src/browser/*" ],
"common/*": [ "../../../src/common/*" ]
}
},
"include": [
"./**/*",
"../../../typings/xterm.d.ts"
],
"references": [
{ "path": "../../../src/browser" },
{ "path": "../../../src/common" }
]
}
@@ -0,0 +1,321 @@
/**
* Copyright (c) 2020 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { openTerminal, launchBrowser, pollFor } from '../../../out-test/api/TestUtils';
import { Browser, Page } from '@playwright/test';
import { IImageAddonOptions } from '../src/Types';
import { FINALIZER, introducer, sixelEncode } from 'sixel';
import { readFileSync } from 'fs';
const APP = 'http://127.0.0.1:3001/test';
let browser: Browser;
let page: Page;
const width = 800;
const height = 600;
// eslint-disable-next-line
declare const ImageAddon: {
new(options?: Partial<IImageAddonOptions>): any;
};
interface ITestData {
width: number;
height: number;
bytes: Uint8Array;
palette: number[];
sixel: string;
}
interface IDimensions {
cellWidth: number;
cellHeight: number;
width: number;
height: number;
}
// image: 640 x 80, 512 color
const TESTDATA: ITestData = (() => {
const data8 = readFileSync('./addons/xterm-addon-image/fixture/palette.blob');
const data32 = new Uint32Array(data8.buffer);
const palette = new Set<number>();
for (let i = 0; i < data32.length; ++i) palette.add(data32[i]);
const sixel = sixelEncode(data8, 640, 80, [...palette]);
return {
width: 640,
height: 80,
bytes: data8,
palette: [...palette],
sixel
};
})();
const SIXEL_SEQ_0 = introducer(0) + TESTDATA.sixel + FINALIZER;
// const SIXEL_SEQ_1 = introducer(1) + TESTDATA.sixel + FINALIZER;
// const SIXEL_SEQ_2 = introducer(2) + TESTDATA.sixel + FINALIZER;
// NOTE: the data is loaded as string for easier transport through playwright
const TESTDATA_IIP: [string, [number, number]][] = [
[readFileSync('./addons/xterm-addon-image/fixture/iip/palette.iip', { encoding: 'utf-8' }), [640, 80]],
[readFileSync('./addons/xterm-addon-image/fixture/iip/spinfox.iip', { encoding: 'utf-8' }), [148, 148]],
[readFileSync('./addons/xterm-addon-image/fixture/iip/w3c_gif.iip', { encoding: 'utf-8' }), [72, 48]],
[readFileSync('./addons/xterm-addon-image/fixture/iip/w3c_jpg.iip', { encoding: 'utf-8' }), [72, 48]],
[readFileSync('./addons/xterm-addon-image/fixture/iip/w3c_png.iip', { encoding: 'utf-8' }), [72, 48]]
];
describe('ImageAddon', () => {
before(async () => {
browser = await launchBrowser();
page = await (await browser.newContext()).newPage();
await page.setViewportSize({ width, height });
});
after(async () => {
await browser.close();
});
beforeEach(async () => {
await page.goto(APP);
await openTerminal(page);
await page.evaluate(opts => {
(window as any).imageAddon = new ImageAddon(opts.opts);
(window as any).term.loadAddon((window as any).imageAddon);
}, { opts: { sixelPaletteLimit: 512 } });
});
it('test for private accessors', async () => {
// terminal privates
const accessors = [
'_core',
'_core._renderService',
'_core._inputHandler',
'_core._inputHandler._parser',
'_core._inputHandler._curAttrData',
'_core._inputHandler._dirtyRowTracker',
'_core._themeService.colors',
'_core._coreBrowserService'
];
for (const prop of accessors) {
assert.equal(
await page.evaluate('(() => { const v = window.term.' + prop + '; return v !== undefined && v !== null; })()'),
true, `problem at ${prop}`
);
}
// bufferline privates
assert.equal(await page.evaluate('window.term._core.buffer.lines.get(0)._data instanceof Uint32Array'), true);
assert.equal(await page.evaluate('window.term._core.buffer.lines.get(0)._extendedAttrs instanceof Object'), true);
// inputhandler privates
assert.equal(await page.evaluate('window.term._core._inputHandler._curAttrData.constructor.name'), 'AttributeData');
assert.equal(await page.evaluate('window.term._core._inputHandler._parser.constructor.name'), 'EscapeSequenceParser');
});
describe('ctor options', () => {
it('empty settings should load defaults', async () => {
const DEFAULT_OPTIONS: IImageAddonOptions = {
enableSizeReports: true,
pixelLimit: 16777216,
sixelSupport: true,
sixelScrolling: true,
sixelPaletteLimit: 512, // set to 512 to get example image working
sixelSizeLimit: 25000000,
storageLimit: 128,
showPlaceholder: true,
iipSupport: true,
iipSizeLimit: 20000000
};
assert.deepEqual(await page.evaluate(`window.imageAddon._opts`), DEFAULT_OPTIONS);
});
it('custom settings should overload defaults', async () => {
const customSettings: IImageAddonOptions = {
enableSizeReports: false,
pixelLimit: 5,
sixelSupport: false,
sixelScrolling: false,
sixelPaletteLimit: 1024,
sixelSizeLimit: 1000,
storageLimit: 10,
showPlaceholder: false,
iipSupport: false,
iipSizeLimit: 1000
};
await page.evaluate(opts => {
(window as any).imageAddonCustom = new ImageAddon(opts.opts);
(window as any).term.loadAddon((window as any).imageAddonCustom);
}, { opts: customSettings });
assert.deepEqual(await page.evaluate(`window.imageAddonCustom._opts`), customSettings);
});
});
describe('scrolling & cursor modes', () => {
it('testdata default (scrolling with VT240 cursor pos)', async () => {
const dim = await getDimensions();
await writeToTerminal(SIXEL_SEQ_0);
assert.deepEqual(await getCursor(), [0, Math.floor(TESTDATA.height/dim.cellHeight)]);
// moved to right by 10 cells
await writeToTerminal('#'.repeat(10) + SIXEL_SEQ_0);
assert.deepEqual(await getCursor(), [10, Math.floor(TESTDATA.height/dim.cellHeight) * 2]);
});
it('write testdata noScrolling', async () => {
await writeToTerminal('\x1b[?80h' + SIXEL_SEQ_0);
assert.deepEqual(await getCursor(), [0, 0]);
// second draw does not change anything
await writeToTerminal(SIXEL_SEQ_0);
assert.deepEqual(await getCursor(), [0, 0]);
});
it('testdata cursor always at VT240 pos', async () => {
const dim = await getDimensions();
// offset 0
await writeToTerminal(SIXEL_SEQ_0);
assert.deepEqual(await getCursor(), [0, Math.floor(TESTDATA.height/dim.cellHeight)]);
// moved to right by 10 cells
await writeToTerminal('#'.repeat(10) + SIXEL_SEQ_0);
assert.deepEqual(await getCursor(), [10, Math.floor(TESTDATA.height/dim.cellHeight) * 2]);
// moved by 30 cells (+10 prev)
await writeToTerminal('#'.repeat(30) + SIXEL_SEQ_0);
assert.deepEqual(await getCursor(), [10 + 30, Math.floor(TESTDATA.height/dim.cellHeight) * 3]);
});
});
describe('image lifecycle & eviction', () => {
it('delete image once scrolled off', async () => {
await writeToTerminal(SIXEL_SEQ_0);
pollFor(page, 'window.imageAddon._storage._images.size', 1);
// scroll to scrollback + rows - 1
await page.evaluate(
scrollback => new Promise(res => (window as any).term.write('\n'.repeat(scrollback), res)),
(await getScrollbackPlusRows() - 1)
);
// wait here, as we have to make sure, that eviction did not yet occur
await new Promise(r => setTimeout(r, 100));
pollFor(page, 'window.imageAddon._storage._images.size', 1);
// scroll one further should delete the image
await page.evaluate(() => new Promise(res => (window as any).term.write('\n', res)));
pollFor(page, 'window.imageAddon._storage._images.size', 0);
});
it('get storageUsage', async () => {
assert.equal(await page.evaluate('imageAddon.storageUsage'), 0);
await writeToTerminal(SIXEL_SEQ_0);
assert.closeTo(await page.evaluate('imageAddon.storageUsage'), 640 * 80 * 4 / 1000000, 0.05);
});
it('get/set storageLimit', async () => {
assert.equal(await page.evaluate('imageAddon.storageLimit'), 128);
assert.equal(await page.evaluate('imageAddon.storageLimit = 1'), 1);
assert.equal(await page.evaluate('imageAddon.storageLimit'), 1);
});
it('remove images by storage limit pressure', async () => {
assert.equal(await page.evaluate('imageAddon.storageLimit = 1'), 1);
// never go beyond storage limit
await writeToTerminal(SIXEL_SEQ_0);
await writeToTerminal(SIXEL_SEQ_0);
await writeToTerminal(SIXEL_SEQ_0);
await writeToTerminal(SIXEL_SEQ_0);
await new Promise(r => setTimeout(r, 50));
const usage = await page.evaluate('imageAddon.storageUsage');
await writeToTerminal(SIXEL_SEQ_0);
await writeToTerminal(SIXEL_SEQ_0);
await writeToTerminal(SIXEL_SEQ_0);
await writeToTerminal(SIXEL_SEQ_0);
await new Promise(r => setTimeout(r, 50));
assert.equal(await page.evaluate('imageAddon.storageUsage'), usage);
assert.equal(usage as number < 1, true);
});
it('set storageLimit removes images synchronously', async () => {
await writeToTerminal(SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0);
const usage: number = await page.evaluate('imageAddon.storageUsage');
const newUsage: number = await page.evaluate('imageAddon.storageLimit = 0.5; imageAddon.storageUsage');
assert.equal(newUsage < usage, true);
assert.equal(newUsage < 0.5, true);
});
it('clear alternate images on buffer change', async () => {
assert.equal(await page.evaluate('imageAddon.storageUsage'), 0);
await writeToTerminal('\x1b[?1049h' + SIXEL_SEQ_0);
assert.closeTo(await page.evaluate('imageAddon.storageUsage'), 640 * 80 * 4 / 1000000, 0.05);
await writeToTerminal('\x1b[?1049l');
assert.equal(await page.evaluate('imageAddon.storageUsage'), 0);
});
it('evict tiles by in-place overwrites (only full overwrite tested)', async () => {
await new Promise(r => setTimeout(r, 50));
await writeToTerminal('\x1b[H' + SIXEL_SEQ_0 + '\x1b[100;100H');
let usage = await page.evaluate('imageAddon.storageUsage');
while (usage === 0) {
await new Promise(r => setTimeout(r, 50));
usage = await page.evaluate('imageAddon.storageUsage');
}
await writeToTerminal('\x1b[H' + SIXEL_SEQ_0 + '\x1b[100;100H');
await new Promise(r => setTimeout(r, 200)); // wait some time and re-check
assert.equal(await page.evaluate('imageAddon.storageUsage'), usage);
});
it('manual eviction on alternate buffer must not miss images', async () => {
await writeToTerminal('\x1b[?1049h');
await writeToTerminal(SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0);
await new Promise(r => setTimeout(r, 50));
const usage: number = await page.evaluate('imageAddon.storageUsage');
await writeToTerminal(SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0);
await writeToTerminal(SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0);
await new Promise(r => setTimeout(r, 50));
const newUsage: number = await page.evaluate('imageAddon.storageUsage');
assert.equal(newUsage, usage);
});
});
describe('IIP support - testimages', () => {
it('palette.png', async () => {
await writeToTerminal(TESTDATA_IIP[0][0]);
assert.deepEqual(await getOrigSize(1), TESTDATA_IIP[0][1]);
});
it('spinfox.png', async () => {
await writeToTerminal(TESTDATA_IIP[1][0]);
assert.deepEqual(await getOrigSize(1), TESTDATA_IIP[1][1]);
});
it('w3c gif', async () => {
await writeToTerminal(TESTDATA_IIP[2][0]);
assert.deepEqual(await getOrigSize(1), TESTDATA_IIP[2][1]);
});
it('w3c jpeg', async () => {
await writeToTerminal(TESTDATA_IIP[3][0]);
assert.deepEqual(await getOrigSize(1), TESTDATA_IIP[3][1]);
});
it('w3c png', async () => {
await writeToTerminal(TESTDATA_IIP[4][0]);
assert.deepEqual(await getOrigSize(1), TESTDATA_IIP[4][1]);
});
});
});
/**
* terminal access helpers.
*/
async function getDimensions(): Promise<IDimensions> {
const dimensions: any = await page.evaluate(`term._core._renderService.dimensions`);
return {
cellWidth: Math.round(dimensions.css.cell.width),
cellHeight: Math.round(dimensions.css.cell.height),
width: Math.round(dimensions.css.canvas.width),
height: Math.round(dimensions.css.canvas.height)
};
}
async function getCursor(): Promise<[number, number]> {
return page.evaluate('[window.term.buffer.active.cursorX, window.term.buffer.active.cursorY]');
}
async function getImageStorageLength(): Promise<number> {
return page.evaluate('window.imageAddon._storage._images.size');
}
async function getScrollbackPlusRows(): Promise<number> {
return page.evaluate('window.term.options.scrollback + window.term.rows');
}
async function writeToTerminal(d: string): Promise<any> {
return page.evaluate(data => new Promise(res => (window as any).term.write(data, res)), d);
}
async function getOrigSize(id: number): Promise<[number, number]> {
return page.evaluate<any>(`[
window.imageAddon._storage._images.get(${id}).orig.width,
window.imageAddon._storage._images.get(${id}).orig.height
]`);
}
@@ -0,0 +1,29 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"rootDir": ".",
"outDir": "../out-test",
"sourceMap": true,
"removeComments": true,
"strict": true,
"baseUrl": ".",
"paths": {
"browser/*": [ "../../../src/browser/*" ],
"common/*": [ "../../../src/common/*" ]
},
"types": [
"../../../node_modules/@types/mocha",
"../../../node_modules/@types/node",
"../../../out-test/api/TestUtils"
]
},
"include": [
"./**/*",
"../../../typings/xterm.d.ts"
],
"references": [
{ "path": "../../../src/browser" },
{ "path": "../../../src/common" }
]
}
+8
View File
@@ -0,0 +1,8 @@
{
"files": [],
"include": [],
"references": [
{ "path": "./src" },
{ "path": "./test" }
]
}
+120
View File
@@ -0,0 +1,120 @@
/**
* Copyright (c) 2022 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal, ITerminalAddon } from 'xterm';
declare module 'xterm-addon-image' {
export interface IImageAddonOptions {
/**
* Enable size reports in windowOptions:
* - getWinSizePixels (CSI 14 t)
* - getCellSizePixels (CSI 16 t)
* - getWinSizeChars (CSI 18 t)
*
* If `true` (default), the reports will be activated during addon loading.
* If `false`, no settings will be touched. Use `false`, if you have high
* security constraints and/or deal with windowOptions by other means.
* On addon disposal, the settings will not change.
*/
enableSizeReports?: boolean;
/**
* Maximum pixels a single image may hold. Images exceeding this number will
* be discarded during processing with no changes to the terminal buffer
* (no cursor advance, no placeholder).
* This setting is mainly used to restrict images sizes during initial decoding
* including the final canvas creation.
*
* Note: The image worker decoder may hold additional memory up to
* `pixelLimit` * 4 bytes permanently, plus the same amount on top temporarily
* for pixel transfers, which should be taken into account under memory pressure conditions.
*
* Note: Browsers restrict allowed canvas dimensions further. We dont reflect those
* limits here, thus the construction of an oddly shaped image having most pixels
* in one dimension still can fail.
*
* Note: `storageLimit` bytes are calculated from images by multiplying the pixels with 4
* (4 channels with one byte, images are stored as RGBA8888).
*
* Default is 2^16 (4096 x 4096 pixels).
*/
pixelLimit?: number;
/**
* Storage limit in MB.
* The storage implements a FIFO cache removing old images, when the limit gets hit.
* Also exposed as addon property for runtime adjustments.
* Default is 128 MB.
*/
storageLimit?: number;
/**
* Whether to show a placeholder for images removed from cache, default is true.
*/
showPlaceholder?: boolean;
/**
* SIXEL settings
*/
/** Whether SIXEL is enabled (default is true). */
sixelSupport?: boolean;
/** Whether SIXEL scrolling is enabled (default is true). Same as DECSET 80. */
sixelScrolling?: boolean;
/** Palette color limit (default 256). */
sixelPaletteLimit?: number;
/** SIXEL image size limit in bytes (default 25000000 bytes). */
sixelSizeLimit?: number;
/**
* IIP settings (iTerm image protocol)
*/
/** Whether iTerm image protocol style is enabled (default is true). */
iipSupport?: boolean;
/** IIP sequence size limit (default 20000000 bytes). */
iipSizeLimit?: number;
}
export class ImageAddon implements ITerminalAddon {
constructor(options?: IImageAddonOptions);
public activate(terminal: Terminal): void;
public dispose(): void;
/**
* Reset the image addon.
*
* This resets all runtime options to default values (as given to the ctor)
* and resets the image storage.
*/
public reset(): void;
/**
* Getter/Setter for the storageLimit in MB.
* Synchronously deletes images if the stored data exceeds the new value.
*/
public storageLimit: number;
/**
* Current memory usage of the stored images in MB.
*/
public readonly storageUsage: number;
/**
* Getter/Setter whether the placeholder should be shown.
*/
public showPlaceholder: boolean;
/**
* Get original image canvas at buffer position.
*/
public getImageAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined;
/**
* Extract single tile canvas at buffer position.
*/
public extractTileAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined;
}
}
@@ -0,0 +1,33 @@
/**
* Copyright (c) 2020 The xterm.js authors. All rights reserved.
* @license MIT
*/
const path = require('path');
const addonName = 'ImageAddon';
const mainFile = 'xterm-addon-image.js';
const addon = {
entry: `./out/${addonName}.js`,
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre",
exclude: /node_modules/
}
]
},
output: {
filename: mainFile,
path: path.resolve('./lib'),
library: addonName,
libraryTarget: 'umd'
},
mode: 'production'
};
module.exports = [addon];
+6 -5
View File
@@ -33,15 +33,16 @@ This package locates the font file on disk for the font currently in use by the
Since this package depends on being able to find and resolve a system font from disk, it has to have system access that isn't available in the web browser. As a result, this package is mainly useful in environments that combine browser and Node.js runtimes (such as [Electron]).
### Fallback Ligatures
When ligatures cannot be fetched from the environment, a set of "fallback" ligatures is used to get the most common ligatures working. These fallback ligatures can be customized with options passed to `LigatureAddon.constructor`.
### Fonts
This package makes use of the following fonts for testing:
* [Fira Code][Fira Code] - [Licensed under the OFL][Fira Code License] by Nikita
Prokopov, Mozilla Foundation with reserved names Fira Code, Fira Mono, and
Fira Sans
* [Iosevka] - [Licensed under the OFL][Iosevka License] by Belleve Invis with
reserved name Iosevka
* [Fira Code][Fira Code] - [Licensed under the OFL][Fira Code License] by Nikita Prokopov, Mozilla Foundation with reserved names Fira Code, Fira Mono, and Fira Sans
* [Iosevka] - [Licensed under the OFL][Iosevka License] by Belleve Invis with reserved name Iosevka
[xterm.js]: https://github.com/xtermjs/xterm.js
[Electron]: https://electronjs.org/

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