Repository: xtermjs/xterm.js Branch: master Commit: 1432e2008284 Files: 696 Total size: 4.5 MB Directory structure: gitextract_4c2m1yy1/ ├── .devcontainer/ │ └── devcontainer.json ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── config.yml │ │ └── feature_request.md │ ├── copilot-instructions.md │ ├── hooks/ │ │ └── setupRepo.json │ ├── instructions/ │ │ ├── benchmark.instructions.md │ │ └── unit-test.instructions.md │ └── workflows/ │ ├── ci.yml │ ├── codeql-analysis.yml │ ├── copilot-setup-steps.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── .npmignore ├── .nvmrc ├── .vscode/ │ ├── extensions.json │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── addons/ │ ├── addon-attach/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── AttachAddon.ts │ │ │ └── tsconfig.json │ │ ├── test/ │ │ │ ├── AttachAddon.test.ts │ │ │ ├── playwright.config.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ ├── typings/ │ │ │ └── addon-attach.d.ts │ │ └── webpack.config.js │ ├── addon-clipboard/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── ClipboardAddon.ts │ │ │ └── tsconfig.json │ │ ├── test/ │ │ │ ├── ClipboardAddon.test.ts │ │ │ ├── playwright.config.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ ├── typings/ │ │ │ └── addon-clipboard.d.ts │ │ └── webpack.config.js │ ├── addon-fit/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── FitAddon.ts │ │ │ └── tsconfig.json │ │ ├── test/ │ │ │ ├── FitAddon.test.ts │ │ │ ├── playwright.config.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ ├── typings/ │ │ │ └── addon-fit.d.ts │ │ └── webpack.config.js │ ├── addon-image/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── fixture/ │ │ │ ├── endless.sh │ │ │ ├── gcrglf.sh │ │ │ ├── growing_rect.js │ │ │ ├── iip/ │ │ │ │ ├── palette.iip │ │ │ │ ├── spinfox.iip │ │ │ │ ├── w3c_gif.iip │ │ │ │ ├── w3c_jpg.iip │ │ │ │ └── w3c_png.iip │ │ │ ├── inspect_palette.sh │ │ │ ├── overdraw.sh │ │ │ ├── palette.blob │ │ │ ├── palette.sixel │ │ │ └── textcursor.sh │ │ ├── package.json │ │ ├── src/ │ │ │ ├── IIPHandler.ts │ │ │ ├── IIPHeaderParser.test.ts │ │ │ ├── IIPHeaderParser.ts │ │ │ ├── IIPImageStorage.ts │ │ │ ├── IIPMetrics.test.ts │ │ │ ├── IIPMetrics.ts │ │ │ ├── ImageAddon.ts │ │ │ ├── ImageRenderer.ts │ │ │ ├── ImageStorage.ts │ │ │ ├── SixelHandler.ts │ │ │ ├── SixelImageStorage.ts │ │ │ ├── Types.ts │ │ │ ├── kitty/ │ │ │ │ ├── KittyGraphicsHandler.ts │ │ │ │ ├── KittyGraphicsTypes.test.ts │ │ │ │ ├── KittyGraphicsTypes.ts │ │ │ │ └── KittyImageStorage.ts │ │ │ └── tsconfig.json │ │ ├── test/ │ │ │ ├── ImageAddon.test.ts │ │ │ ├── KittyGraphics.test.ts │ │ │ ├── playwright.config.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ ├── typings/ │ │ │ └── addon-image.d.ts │ │ └── webpack.config.js │ ├── addon-ligatures/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── fonts/ │ │ │ └── FiraCode-Regular.otf │ │ ├── package.json │ │ ├── src/ │ │ │ ├── LigaturesAddon.ts │ │ │ ├── Types.ts │ │ │ ├── font.ts │ │ │ ├── fontLigatures/ │ │ │ │ ├── flatten.ts │ │ │ │ ├── index.test.ts │ │ │ │ ├── index.ts │ │ │ │ ├── merge.test.ts │ │ │ │ ├── merge.ts │ │ │ │ ├── mergeRange.test.ts │ │ │ │ ├── mergeRange.ts │ │ │ │ ├── processors/ │ │ │ │ │ ├── 6-1.ts │ │ │ │ │ ├── 6-2.ts │ │ │ │ │ ├── 6-3.ts │ │ │ │ │ ├── 8-1.ts │ │ │ │ │ ├── classDef.ts │ │ │ │ │ ├── coverage.ts │ │ │ │ │ ├── helper.ts │ │ │ │ │ └── substitution.ts │ │ │ │ ├── tables.ts │ │ │ │ ├── types.ts │ │ │ │ └── walk.ts │ │ │ ├── index.ts │ │ │ ├── parse.test.ts │ │ │ ├── parse.ts │ │ │ └── tsconfig.json │ │ ├── test/ │ │ │ ├── LigaturesAddon.test.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ ├── typings/ │ │ │ └── addon-ligatures.d.ts │ │ └── webpack.config.js │ ├── addon-progress/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── ProgressAddon.ts │ │ │ └── tsconfig.json │ │ ├── test/ │ │ │ ├── ProgressAddon.test.ts │ │ │ ├── playwright.config.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ ├── typings/ │ │ │ └── addon-progress.d.ts │ │ └── webpack.config.js │ ├── addon-search/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── fixtures/ │ │ │ └── issue-2444 │ │ ├── package.json │ │ ├── src/ │ │ │ ├── DecorationManager.ts │ │ │ ├── SearchAddon.ts │ │ │ ├── SearchEngine.test.ts │ │ │ ├── SearchEngine.ts │ │ │ ├── SearchLineCache.test.ts │ │ │ ├── SearchLineCache.ts │ │ │ ├── SearchResultTracker.ts │ │ │ ├── SearchState.ts │ │ │ └── tsconfig.json │ │ ├── test/ │ │ │ ├── SearchAddon.test.ts │ │ │ ├── playwright.config.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ ├── typings/ │ │ │ └── addon-search.d.ts │ │ └── webpack.config.js │ ├── addon-serialize/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── README.md │ │ ├── benchmark/ │ │ │ ├── SerializeAddon.benchmark.ts │ │ │ ├── benchmark.json │ │ │ └── tsconfig.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── SerializeAddon.test.ts │ │ │ ├── SerializeAddon.ts │ │ │ └── tsconfig.json │ │ ├── test/ │ │ │ ├── SerializeAddon.test.ts │ │ │ ├── playwright.config.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ ├── typings/ │ │ │ └── addon-serialize.d.ts │ │ └── webpack.config.js │ ├── addon-unicode-graphemes/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── benchmark/ │ │ │ ├── UnicodeGraphemeAddon.benchmark.ts │ │ │ ├── benchmark.json │ │ │ └── tsconfig.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── UnicodeGraphemeProvider.ts │ │ │ ├── UnicodeGraphemesAddon.ts │ │ │ ├── third-party/ │ │ │ │ ├── UnicodeProperties.ts │ │ │ │ ├── tiny-inflate.ts │ │ │ │ └── unicode-trie.ts │ │ │ └── tsconfig.json │ │ ├── test/ │ │ │ ├── UnicodeGraphemesAddon.test.ts │ │ │ ├── playwright.config.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ ├── typings/ │ │ │ └── addon-unicode-graphemes.d.ts │ │ └── webpack.config.js │ ├── addon-unicode11/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── Unicode11Addon.ts │ │ │ ├── UnicodeV11.ts │ │ │ └── tsconfig.json │ │ ├── test/ │ │ │ ├── Unicode11Addon.test.ts │ │ │ ├── playwright.config.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ ├── typings/ │ │ │ └── addon-unicode11.d.ts │ │ └── webpack.config.js │ ├── addon-web-fonts/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── WebFontsAddon.ts │ │ │ └── tsconfig.json │ │ ├── test/ │ │ │ ├── WebFontsAddon.test.ts │ │ │ ├── playwright.config.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ ├── typings/ │ │ │ └── addon-web-fonts.d.ts │ │ └── webpack.config.js │ ├── addon-web-links/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── WebLinkProvider.ts │ │ │ ├── WebLinksAddon.ts │ │ │ └── tsconfig.json │ │ ├── test/ │ │ │ ├── WebLinksAddon.test.ts │ │ │ ├── playwright.config.ts │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ ├── typings/ │ │ │ └── addon-web-links.d.ts │ │ └── webpack.config.js │ └── addon-webgl/ │ ├── .gitignore │ ├── .npmignore │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── src/ │ │ ├── CellColorResolver.ts │ │ ├── CharAtlasCache.ts │ │ ├── CharAtlasUtils.ts │ │ ├── Constants.ts │ │ ├── CursorBlinkStateManager.ts │ │ ├── DevicePixelObserver.ts │ │ ├── GlyphRenderer.ts │ │ ├── RectangleRenderer.ts │ │ ├── RenderModel.ts │ │ ├── TextureAtlas.ts │ │ ├── TypedArray.test.ts │ │ ├── TypedArray.ts │ │ ├── Types.ts │ │ ├── WebglAddon.ts │ │ ├── WebglRenderer.ts │ │ ├── WebglUtils.ts │ │ ├── customGlyphs/ │ │ │ ├── CustomGlyphDefinitions.ts │ │ │ ├── CustomGlyphRasterizer.ts │ │ │ └── Types.ts │ │ ├── renderLayer/ │ │ │ ├── BaseRenderLayer.ts │ │ │ ├── LinkRenderLayer.ts │ │ │ └── Types.ts │ │ └── tsconfig.json │ ├── test/ │ │ ├── WebglRenderer.test.ts │ │ ├── playwright.config.ts │ │ └── tsconfig.json │ ├── tsconfig.json │ ├── typings/ │ │ └── addon-webgl.d.ts │ └── webpack.config.js ├── bin/ │ ├── agent/ │ │ └── setup-repo.mjs │ ├── convert_svg_to_custom_glyph.js │ ├── esbuild.mjs │ ├── esbuild_all.mjs │ ├── extract_vtfeatures.js │ ├── lint_changes.js │ ├── package_headless.js │ ├── publish.js │ ├── test_integration.js │ ├── test_mousemodes.js │ ├── test_unit.js │ ├── update-website.sh │ ├── vs_base_find_unused.js │ └── vs_base_update.ps1 ├── css/ │ └── xterm.css ├── demo/ │ ├── client/ │ │ ├── client.ts │ │ ├── components/ │ │ │ ├── controlBar.ts │ │ │ └── window/ │ │ │ ├── addonImageWindow.ts │ │ │ ├── addonLigaturesWindow.ts │ │ │ ├── addonProgressWindow.ts │ │ │ ├── addonSearchWindow.ts │ │ │ ├── addonSerializeWindow.ts │ │ │ ├── addonWebFontsWindow.ts │ │ │ ├── addonWebLinksWindow.ts │ │ │ ├── addonsWindow.ts │ │ │ ├── baseWindow.ts │ │ │ ├── cellInspectorWindow.ts │ │ │ ├── gpuWindow.ts │ │ │ ├── optionsWindow.ts │ │ │ ├── styleWindow.ts │ │ │ ├── testWindow.ts │ │ │ ├── vtWindow.ts │ │ │ └── webglWindow.ts │ │ ├── tsconfig.json │ │ ├── types.ts │ │ └── unicodeTable.ts │ ├── fonts/ │ │ ├── bpdots.regular.otf │ │ └── font-licenses.txt │ ├── index.css │ ├── index.html │ ├── server/ │ │ ├── server.ts │ │ └── tsconfig.json │ ├── start.js │ ├── test.html │ └── tsconfig.json ├── eslint.config.mjs ├── eslint.config.typings.mjs ├── fixtures/ │ └── escape_sequence_files/ │ ├── NOTES │ ├── run_tests.py │ ├── t0001-all_printable.in │ ├── t0001-all_printable.text │ ├── t0002-history.in │ ├── t0002-history.text │ ├── t0002j-simple_string.in │ ├── t0002j-simple_string.text │ ├── t0003-line_wrap.in │ ├── t0003-line_wrap.text │ ├── t0003j-LF.in │ ├── t0003j-LF.text │ ├── t0004-LF.in │ ├── t0004-LF.text │ ├── t0004j-CR.in │ ├── t0004j-CR.text │ ├── t0005-CR.in │ ├── t0005-CR.text │ ├── t0006-IND.in │ ├── t0006-IND.text │ ├── t0007-space_at_end.in │ ├── t0007-space_at_end.text │ ├── t0008-BS.in │ ├── t0008-BS.text │ ├── t0009-NEL.in │ ├── t0009-NEL.text │ ├── t0010-RI.in │ ├── t0010-RI.text │ ├── t0011-RI_scroll.in │ ├── t0011-RI_scroll.text │ ├── t0012-VT.in │ ├── t0012-VT.text │ ├── t0013-FF.in │ ├── t0013-FF.text │ ├── t0014-CAN.in │ ├── t0014-CAN.text │ ├── t0015-SUB.in │ ├── t0015-SUB.text │ ├── t0016-SU.in │ ├── t0016-SU.text │ ├── t0017-SD.in │ ├── t0017-SD.text │ ├── t0020-CUF.in │ ├── t0020-CUF.text │ ├── t0021-CUB.in │ ├── t0021-CUB.text │ ├── t0022-CUU.in │ ├── t0022-CUU.text │ ├── t0023-CUU_scroll.in │ ├── t0023-CUU_scroll.text │ ├── t0024-CUD.in │ ├── t0024-CUD.text │ ├── t0025-CUP.in │ ├── t0025-CUP.text │ ├── t0026-CNL.in │ ├── t0026-CNL.text │ ├── t0027-CPL.in │ ├── t0027-CPL.text │ ├── t0030-HPR.in │ ├── t0030-HPR.text │ ├── t0031-HPB.in_ │ ├── t0031-HPB.text │ ├── t0032-VPB.in │ ├── t0032-VPB.text │ ├── t0033-VPB_scroll.in │ ├── t0033-VPB_scroll.text │ ├── t0034-VPR.in │ ├── t0034-VPR.text │ ├── t0035-HVP.in │ ├── t0035-HVP.text │ ├── t0040-REP.in │ ├── t0040-REP.text │ ├── t0050-ICH.in │ ├── t0050-ICH.text │ ├── t0051-IL.in │ ├── t0051-IL.text │ ├── t0052-DL.in │ ├── t0052-DL.text │ ├── t0053-DCH.in │ ├── t0053-DCH.text │ ├── t0054-ECH.in │ ├── t0054-ECH.text │ ├── t0055-EL.in │ ├── t0055-EL.text │ ├── t0056-ED.in │ ├── t0056-ED.text │ ├── t0057-ED3.in │ ├── t0057-ED3.note │ ├── t0057-ED3.text │ ├── t0060-DECSC.in │ ├── t0060-DECSC.text │ ├── t0061-CSI_s.in │ ├── t0061-CSI_s.text │ ├── t0070-DECSTBM_LF.in │ ├── t0070-DECSTBM_LF.text │ ├── t0071-DECSTBM_IND.in │ ├── t0071-DECSTBM_IND.text │ ├── t0072-DECSTBM_NEL.in │ ├── t0072-DECSTBM_NEL.text │ ├── t0073-DECSTBM_RI.in │ ├── t0073-DECSTBM_RI.text │ ├── t0074-DECSTBM_SU_SD.in │ ├── t0074-DECSTBM_SU_SD.text │ ├── t0075-DECSTBM_CUU_CUD.in │ ├── t0075-DECSTBM_CUU_CUD.text │ ├── t0076-DECSTBM_IL_DL.in │ ├── t0076-DECSTBM_IL_DL.text │ ├── t0077-DECSTBM_quirks.in │ ├── t0077-DECSTBM_quirks.text │ ├── t0078-DECSTBM_CPL_CNL.in │ ├── t0078-DECSTBM_CPL_CNL.text │ ├── t0079-DECSTBM_VPR.in │ ├── t0079-DECSTBM_VPR.text │ ├── t0080-HT.in │ ├── t0080-HT.text │ ├── t0081-TBC.in │ ├── t0081-TBC.text │ ├── t0082-HTS.in │ ├── t0082-HTS.text │ ├── t0083-CHT.in │ ├── t0083-CHT.text │ ├── t0084-CBT.in │ ├── t0084-CBT.text │ ├── t0084-CBT.text-xterm │ ├── t0090-alt_screen.in │ ├── t0090-alt_screen.text │ ├── t0091-alt_screen_ED3.in │ ├── t0091-alt_screen_ED3.text │ ├── t0092-alt_screen_DECSC.in │ ├── t0092-alt_screen_DECSC.text │ ├── t0100-IRM.in │ ├── t0100-IRM.text │ ├── t0101-NLM.in │ ├── t0101-NLM.text │ ├── t0102-DECAWM.in │ ├── t0102-DECAWM.text │ ├── t0103-reverse_wrap.in │ ├── t0103-reverse_wrap.text │ ├── t0200-SGR.html │ ├── t0200-SGR.in_ │ ├── t0220-SGR_inverse.html │ ├── t0220-SGR_inverse.in_ │ ├── t0300-vttest1.in │ ├── t0300-vttest1.text │ ├── t0500-bash_long_line.in │ ├── t0500-bash_long_line.text │ ├── t0501-bash_ls.in │ ├── t0501-bash_ls.text │ ├── t0502-bash_ls_color.in │ ├── t0502-bash_ls_color.text │ ├── t0503-zsh_ls_color.in │ ├── t0503-zsh_ls_color.text │ ├── t0504-vim.in │ ├── t0504-vim.text │ ├── t600-DECSTBM_SR.in │ ├── t600-DECSTBM_SR.text │ ├── t601-DECSTBM_SL.in │ ├── t601-DECSTBM_SL.text │ ├── t602-DECSTBM_DECIC.in │ ├── t602-DECSTBM_DECIC.text │ ├── t603-DECSTBM_DECDC.in │ └── t603-DECSTBM_DECDC.text ├── headless/ │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ └── package.json ├── images/ │ └── build-flow.tldr ├── package.json ├── src/ │ ├── browser/ │ │ ├── AccessibilityManager.ts │ │ ├── Clipboard.test.ts │ │ ├── Clipboard.ts │ │ ├── ColorContrastCache.test.ts │ │ ├── ColorContrastCache.ts │ │ ├── CoreBrowserTerminal.ts │ │ ├── Dom.ts │ │ ├── Linkifier.test.ts │ │ ├── Linkifier.ts │ │ ├── LocalizableStrings.ts │ │ ├── OscLinkProvider.ts │ │ ├── RenderDebouncer.ts │ │ ├── Terminal.test.ts │ │ ├── Terminal2.test.ts │ │ ├── TestUtils.test.ts │ │ ├── TimeBasedDebouncer.ts │ │ ├── Types.ts │ │ ├── Viewport.ts │ │ ├── decorations/ │ │ │ ├── BufferDecorationRenderer.ts │ │ │ ├── ColorZoneStore.test.ts │ │ │ ├── ColorZoneStore.ts │ │ │ └── OverviewRulerRenderer.ts │ │ ├── input/ │ │ │ ├── CompositionHelper.test.ts │ │ │ ├── CompositionHelper.ts │ │ │ ├── Mouse.test.ts │ │ │ ├── Mouse.ts │ │ │ ├── MoveToCell.test.ts │ │ │ └── MoveToCell.ts │ │ ├── public/ │ │ │ └── Terminal.ts │ │ ├── renderer/ │ │ │ ├── dom/ │ │ │ │ ├── DomRenderer.ts │ │ │ │ ├── DomRendererRowFactory.test.ts │ │ │ │ ├── DomRendererRowFactory.ts │ │ │ │ ├── WidthCache.test.ts │ │ │ │ └── WidthCache.ts │ │ │ └── shared/ │ │ │ ├── Constants.ts │ │ │ ├── README.md │ │ │ ├── RendererUtils.test.ts │ │ │ ├── RendererUtils.ts │ │ │ ├── SelectionRenderModel.ts │ │ │ ├── TextBlinkStateManager.test.ts │ │ │ ├── TextBlinkStateManager.ts │ │ │ └── Types.ts │ │ ├── scrollable/ │ │ │ ├── abstractScrollbar.ts │ │ │ ├── fastDomNode.ts │ │ │ ├── globalPointerMoveMonitor.ts │ │ │ ├── horizontalScrollbar.ts │ │ │ ├── mouseEvent.ts │ │ │ ├── scrollable.ts │ │ │ ├── scrollableElement.ts │ │ │ ├── scrollableElementOptions.ts │ │ │ ├── scrollbarArrow.ts │ │ │ ├── scrollbarState.ts │ │ │ ├── scrollbarVisibilityController.ts │ │ │ ├── touch.ts │ │ │ ├── verticalScrollbar.ts │ │ │ └── widget.ts │ │ ├── selection/ │ │ │ ├── SelectionModel.test.ts │ │ │ ├── SelectionModel.ts │ │ │ └── Types.ts │ │ ├── services/ │ │ │ ├── CharSizeService.ts │ │ │ ├── CharacterJoinerService.test.ts │ │ │ ├── CharacterJoinerService.ts │ │ │ ├── CoreBrowserService.ts │ │ │ ├── KeyboardService.ts │ │ │ ├── LinkProviderService.ts │ │ │ ├── MouseCoordsService.ts │ │ │ ├── MouseService.test.ts │ │ │ ├── MouseService.ts │ │ │ ├── RenderService.ts │ │ │ ├── SelectionService.test.ts │ │ │ ├── SelectionService.ts │ │ │ ├── Services.ts │ │ │ ├── ThemeService.test.ts │ │ │ └── ThemeService.ts │ │ ├── shared/ │ │ │ └── Constants.ts │ │ └── tsconfig.json │ ├── common/ │ │ ├── Async.ts │ │ ├── CircularList.test.ts │ │ ├── CircularList.ts │ │ ├── Clone.test.ts │ │ ├── Clone.ts │ │ ├── Color.test.ts │ │ ├── Color.ts │ │ ├── CoreTerminal.ts │ │ ├── Event.test.ts │ │ ├── Event.ts │ │ ├── InputHandler.test.ts │ │ ├── InputHandler.ts │ │ ├── Lifecycle.ts │ │ ├── MultiKeyMap.test.ts │ │ ├── MultiKeyMap.ts │ │ ├── Platform.ts │ │ ├── SortedList.test.ts │ │ ├── SortedList.ts │ │ ├── TaskQueue.ts │ │ ├── TestUtils.test.ts │ │ ├── Types.ts │ │ ├── Version.ts │ │ ├── WindowsMode.ts │ │ ├── buffer/ │ │ │ ├── AttributeData.ts │ │ │ ├── Buffer.test.ts │ │ │ ├── Buffer.ts │ │ │ ├── BufferLine.test.ts │ │ │ ├── BufferLine.ts │ │ │ ├── BufferRange.test.ts │ │ │ ├── BufferRange.ts │ │ │ ├── BufferReflow.test.ts │ │ │ ├── BufferReflow.ts │ │ │ ├── BufferSet.test.ts │ │ │ ├── BufferSet.ts │ │ │ ├── CellData.test.ts │ │ │ ├── CellData.ts │ │ │ ├── Constants.ts │ │ │ ├── Marker.ts │ │ │ └── Types.ts │ │ ├── data/ │ │ │ ├── Charsets.ts │ │ │ └── EscapeSequences.ts │ │ ├── input/ │ │ │ ├── Keyboard.test.ts │ │ │ ├── Keyboard.ts │ │ │ ├── KittyKeyboard.test.ts │ │ │ ├── KittyKeyboard.ts │ │ │ ├── TextDecoder.test.ts │ │ │ ├── TextDecoder.ts │ │ │ ├── UnicodeV6.test.ts │ │ │ ├── UnicodeV6.ts │ │ │ ├── Win32InputMode.test.ts │ │ │ ├── Win32InputMode.ts │ │ │ ├── WriteBuffer.test.ts │ │ │ ├── WriteBuffer.ts │ │ │ ├── XParseColor.test.ts │ │ │ └── XParseColor.ts │ │ ├── parser/ │ │ │ ├── ApcParser.test.ts │ │ │ ├── ApcParser.ts │ │ │ ├── Constants.ts │ │ │ ├── DcsParser.test.ts │ │ │ ├── DcsParser.ts │ │ │ ├── EscapeSequenceParser.test.ts │ │ │ ├── EscapeSequenceParser.ts │ │ │ ├── OscParser.test.ts │ │ │ ├── OscParser.ts │ │ │ ├── Params.test.ts │ │ │ ├── Params.ts │ │ │ └── Types.ts │ │ ├── public/ │ │ │ ├── AddonManager.test.ts │ │ │ ├── AddonManager.ts │ │ │ ├── BufferApiView.ts │ │ │ ├── BufferLineApiView.ts │ │ │ ├── BufferNamespaceApi.ts │ │ │ ├── ParserApi.ts │ │ │ └── UnicodeApi.ts │ │ ├── services/ │ │ │ ├── BufferService.ts │ │ │ ├── CharsetService.ts │ │ │ ├── CoreService.test.ts │ │ │ ├── CoreService.ts │ │ │ ├── DecorationService.test.ts │ │ │ ├── DecorationService.ts │ │ │ ├── InstantiationService.ts │ │ │ ├── LogService.ts │ │ │ ├── MouseStateService.test.ts │ │ │ ├── MouseStateService.ts │ │ │ ├── OptionsService.test.ts │ │ │ ├── OptionsService.ts │ │ │ ├── OscLinkService.test.ts │ │ │ ├── OscLinkService.ts │ │ │ ├── ServiceRegistry.ts │ │ │ ├── Services.ts │ │ │ ├── UnicodeService.test.ts │ │ │ └── UnicodeService.ts │ │ └── tsconfig.json │ ├── headless/ │ │ ├── Terminal.ts │ │ ├── public/ │ │ │ ├── Terminal.test.ts │ │ │ └── Terminal.ts │ │ └── tsconfig.json │ ├── tsconfig-base.json │ └── tsconfig-library-base.json ├── test/ │ ├── benchmark/ │ │ ├── EscapeSequenceParser.benchmark.ts │ │ ├── Event.benchmark.ts │ │ ├── Terminal.benchmark.ts │ │ ├── benchmark.json │ │ └── tsconfig.json │ └── playwright/ │ ├── CharWidth.test.ts │ ├── InputHandler.test.ts │ ├── MouseTracking.test.ts │ ├── Parser.test.ts │ ├── Renderer.test.ts │ ├── SharedRendererTests.ts │ ├── Terminal.test.ts │ ├── TestUtils.ts │ ├── playwright.config.ts │ └── tsconfig.json ├── tsconfig.all.json ├── typings/ │ ├── xterm-headless.d.ts │ └── xterm.d.ts ├── webpack.config.headless.js └── webpack.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .devcontainer/devcontainer.json ================================================ { "name": "xterm.js", "image": "mcr.microsoft.com/devcontainers/typescript-node:22-bookworm", "features": { "ghcr.io/devcontainers/features/node:1": { "version": 22 } // yarn }, "forwardPorts": [ 3000 ], "postCreateCommand": "npm install && npm run setup", "customizations": { "vscode": { "extensions": [ "dbaeumer.vscode-eslint", "editorconfig.editorconfig", "hbenl.vscode-mocha-test-adapter" ] } } } ================================================ FILE: .editorconfig ================================================ root = true [*] indent_style = space indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true [*.{j,t}s] max_line_length = 100 [*.css] indent_size = 4 [*.md] trim_trailing_whitespace = false ================================================ FILE: .gitattributes ================================================ * text=auto ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a bug report --- ## Details - Browser and browser version: - OS version: - xterm.js version: ### Steps to reproduce 1. 2. ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - 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 ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest a feature or enhancement --- ================================================ FILE: .github/copilot-instructions.md ================================================ # xterm.js Copilot Instructions ## Architecture Overview **Core Structure**: xterm.js is a multi-target terminal emulator with three main distributions: - `src/browser/`: Full-featured browser terminal with DOM rendering - `src/headless/`: Server-side terminal for Node.js (no DOM) - `src/common/`: Shared core logic (parsing, buffer management, terminal state) **Key Classes**: - `Terminal` (browser/headless): Public API wrapper - `CoreTerminal` (common): Core terminal logic and state - `CoreBrowserTerminal` (browser): Browser-specific terminal implementation ## Development Workflow **Build System**: ```bash npm run build && npm run esbuild # Build all TypeScript and bundle ``` **Testing**: - Unit tests: `npm run test-unit` (Mocha) - Unit tests filtering to file: `npm run test-unit -- **/fileName.ts - Per-addon unit tests: `npm run test-unit -- addons/addon-image/out-esbuild/*.test.js` - Integration tests: `npm run test-integration` (Playwright across Chrome/Firefox/WebKit) - Integration tests by file: `npm run test-integration -- test/playwright/InputHandler.test.ts`. Never use grep to filter tests, it doesn't work - Integration tests by addon: `npm run test-integration -- --suite=addon-search`. Suites always follow the format `addon-` - Lint changes: `npm run lint-changes` to lint only changed files, `npm run lint-changes-fix` to fix them ## Addon Development Pattern All addons follow this structure: ```typescript export class MyAddon implements ITerminalAddon { activate(terminal: Terminal): void { // Called when loaded via terminal.loadAddon() // Register handlers, access terminal APIs } dispose(): void { // Cleanup when addon is disposed } } ``` **Key Examples**: - `addons/addon-fit/`: Terminal sizing - `addons/addon-webgl/`: GPU-accelerated rendering - `addons/addon-search/`: Text search functionality ## Project-Specific Conventions **TypeScript Project Structure**: Uses TypeScript project references (`tsconfig.all.json`) for incremental builds across browser/headless/addons. **API Design**: - Browser and headless terminals share the same public API - Proposed APIs require `allowProposedApi: true` option - Constructor-only options (cols, rows) cannot be changed after instantiation **Testing Utilities**: Use `TestUtils.ts` helpers: - `openTerminal(ctx, options)` for setup - `pollFor(page, fn, expectedValue)` for async assertions - `writeSync(page, data)` for terminal input ## Common Patterns **Parser Integration**: Register custom escape sequence handlers: ```typescript terminal.parser.registerCsiHandler('m', params => { // Handle SGR sequences return true; // Handled }); ``` **Buffer Access**: Read terminal content via buffer API: ```typescript const line = terminal.buffer.active.getLine(0); const cell = line?.getCell(0); ``` **Events**: All terminals emit standard events (onData, onResize, onRender) plus platform-specific ones. ## Critical Implementation Details - Terminal rendering uses either DOM or WebGL renderers - Buffer lines are immutable; create new instances for modifications - Character width handling supports Unicode 11+ and grapheme clustering - Mouse events translate web events to terminal protocols (X10, VT200, etc.) - Color theming supports both palette and true color modes ## Writing unit tests - Unit tests live alongside the source code file of the thing it's testing with a .test.ts suffix. ================================================ FILE: .github/hooks/setupRepo.json ================================================ { "hooks": { "SessionStart": [ { "type": "command", "command": "npm run agent:setup-repo" } ] } } ================================================ FILE: .github/instructions/benchmark.instructions.md ================================================ --- applyTo: '**/*.benchmark.ts' --- # Benchmark run instructions - Full suite: `npm run benchmark` - Single benchmark file: - Tree: `npm run benchmark -- -t out-test/benchmark/Event.benchmark.js` - Run file: `npm run benchmark -- -s "out-test/benchmark/Event.benchmark.js" out-test/benchmark/Event.benchmark.js` - Single context/case: - Use `-t` to get the path, then: - `npm run benchmark -- -s "" out-test/benchmark/Event.benchmark.js` When writing instructions, use `RuntimeCase` to measure pure runtime in ms, use `ThroughputRuntimeCase` when measuring throughput in MB/s. Notes: - Benchmarks run from built JS in `out-test/benchmark/*.benchmark.js`. - Keep `NODE_PATH=./out` (handled by the npm script). ================================================ FILE: .github/instructions/unit-test.instructions.md ================================================ --- applyTo: '**/*.test.ts' --- When writing unit tests follow these rules: - When writing unit tests for addons, always create a real xterm.js instance instead of mocking it. - Prefer `assert.ok` over `assert.notStrictEqual` when checking something is undefined or not. - Avoid comments as most tests should be self-documenting. ================================================ FILE: .github/workflows/ci.yml ================================================ 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 22.x uses: actions/setup-node@v3 with: node-version: 22.x cache: 'npm' - name: Install dependencies run: npm ci - name: Setup and run tsc run: npm run setup - name: Esbuild run: npm run esbuild - name: Zip artifacts run: | zip -r compressed-build \ ./lib/* \ ./out/* \ ./out-*/* \ ./addons/addon-attach/lib/* \ ./addons/addon-attach/out/* \ ./addons/addon-attach/out-*/* \ ./addons/addon-clipboard/lib/* \ ./addons/addon-clipboard/out/* \ ./addons/addon-clipboard/out-*/* \ ./addons/addon-fit/lib/* \ ./addons/addon-fit/out/* \ ./addons/addon-fit/out-*/* \ ./addons/addon-image/lib/* \ ./addons/addon-image/out/* \ ./addons/addon-image/out-*/* \ ./addons/addon-ligatures/lib/* \ ./addons/addon-ligatures/out/* \ ./addons/addon-ligatures/out-*/* \ ./addons/addon-progress/lib/* \ ./addons/addon-progress/out/* \ ./addons/addon-progress/out-*/* \ ./addons/addon-search/lib/* \ ./addons/addon-search/out/* \ ./addons/addon-search/out-*/* \ ./addons/addon-serialize/lib/* \ ./addons/addon-serialize/out/* \ ./addons/addon-serialize/out-*/* \ ./addons/addon-unicode11/lib/* \ ./addons/addon-unicode11/out/* \ ./addons/addon-unicode11/out-*/* \ ./addons/addon-unicode-graphemes/lib/* \ ./addons/addon-unicode-graphemes/out/* \ ./addons/addon-unicode-graphemes/out-*/* \ ./addons/addon-web-links/lib/* \ ./addons/addon-web-links/out/* \ ./addons/addon-web-links/out-*/* \ ./addons/addon-web-fonts/lib/* \ ./addons/addon-web-fonts/out/* \ ./addons/addon-web-fonts/out-*/* \ ./addons/addon-webgl/lib/* \ ./addons/addon-webgl/out/* \ ./addons/addon-webgl/out-*st/* - name: Upload artifacts uses: actions/upload-artifact@v4 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 22.x uses: actions/setup-node@v3 with: node-version: 22.x cache: 'npm' - name: Install dependencies run: | npm ci - name: Lint code env: NODE_OPTIONS: --max_old_space_size=4096 run: npm run lint - name: Lint API run: npm run lint-api test-unit-coverage: needs: build runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v3 - name: Use Node.js 22.x uses: actions/setup-node@v3 with: node-version: 22.x cache: 'npm' - name: Install dependencies run: | npm ci - uses: actions/download-artifact@v4 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: | npm run test-unit-coverage --forbid-only EXIT_CODE=$? ./node_modules/.bin/nyc report --reporter=cobertura exit $EXIT_CODE test-unit: timeout-minutes: 20 strategy: matrix: node-version: [22] 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: 'npm' - name: Install dependencies run: | npm ci - name: Wait for build job uses: NathanFirmo/wait-for-other-job@v1.1.1 with: token: ${{ secrets.GITHUB_TOKEN }} job: build - uses: actions/download-artifact@v4 with: 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: npm run test-unit --forbid-only test-integration: timeout-minutes: 20 strategy: matrix: node-version: [22] # just one as integration tests are about testing in browser runs-on: [ubuntu-22.04] # macos is flaky browser: [chromium, firefox, webkit] runs-on: ${{ matrix.runs-on }} 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: 'npm' - name: Install dependencies run: | npm ci - 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@v4 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 client run: npm run esbuild-demo-client - name: Build demo server run: npm run esbuild-demo-server - name: Integration tests (core) # Tests use 50% workers to reduce flakiness run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=core - name: Integration tests (addon-attach) run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-attach - name: Integration tests (addon-clipboard) run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-clipboard - name: Integration tests (addon-fit) run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-fit - name: Integration tests (addon-image) run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-image - name: Integration tests (addon-progress) run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-progress - name: Integration tests (addon-search) run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-search - name: Integration tests (addon-serialize) run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-serialize - name: Integration tests (addon-unicode-graphemes) run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-unicode-graphemes - name: Integration tests (addon-unicode11) run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-unicode11 - name: Integration tests (addon-web-fonts) run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-web-fonts - name: Integration tests (addon-web-links) run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-web-links - name: Integration tests (addon-webgl) run: npm run test-integration-${{ matrix.browser }} --workers=50% --forbid-only --suite=addon-webgl release-dry-run: needs: build runs-on: ubuntu-latest strategy: matrix: node-version: [22] 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: 'npm' - name: Install dependencies run: | npm ci - name: Install playwright run: npx playwright install - uses: actions/download-artifact@v4 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: Package headless run: | npm run package-headless node ./bin/package_headless.js - name: Publish to npm (dry run) run: node ./bin/publish.js --dry ================================================ FILE: .github/workflows/codeql-analysis.yml ================================================ 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 ================================================ FILE: .github/workflows/copilot-setup-steps.yml ================================================ name: "Copilot Setup Steps" on: workflow_dispatch: push: paths: - .github/workflows/copilot-setup-steps.yml pull_request: paths: - .github/workflows/copilot-setup-steps.yml jobs: copilot-setup-steps: runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout code uses: actions/checkout@v4 - name: Use Node.js 22.x uses: actions/setup-node@v3 with: node-version: 22.x cache: 'npm' - name: Install dependencies run: npm ci - name: Setup and run tsc run: npm run setup - name: Esbuild run: npm run esbuild ================================================ FILE: .github/workflows/release.yml ================================================ 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 22.x uses: actions/setup-node@v3 with: node-version: 22.x cache: 'npm' - name: Install dependencies run: npm ci - name: Build run: npm run setup - name: Package headless run: | npm run package-headless node ./bin/package_headless.js - name: Publish to npm env: NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: node ./bin/publish.js ================================================ FILE: .gitignore ================================================ node_modules/ *.swp .lock-wscript lib/ out/ out-demo/ out-test/ out-esbuild/ out-esbuild-test/ .nyc_output/ Makefile.gyp *.Makefile *.target.gyp.mk *.node example/*.log docs/ npm-debug.log /.idea/ .env build/ .DS_Store yarn.lock test-results/ # Keep bundled code out of Git dist/ demo/dist/ # dont commit benchmark folders .benchmark/ timeline/ ================================================ FILE: .gitmodules ================================================ ================================================ FILE: .npmignore ================================================ # Blacklist - exclude everything except npm defaults such as LICENSE, etc * !*/ # Whitelist - css/ !css/**/*.css # Whitelist - dist/ !dist/**/*.js !dist/**/*.js.map !dist/**/*.css # Whitelist - lib/ !lib/**/*.d.ts !lib/**/*.js !lib/**/*.js.map !lib/**/*.mjs !lib/**/*.mjs.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 # Blacklist - (normal behavior) these will override any whitelist headless/ typings/xterm-headless.d.ts ================================================ FILE: .nvmrc ================================================ 22 ================================================ FILE: .vscode/extensions.json ================================================ { "recommendations": [ "dbaeumer.vscode-eslint" ] } ================================================ FILE: .vscode/launch.json ================================================ { "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Unit Tests", "cwd": "${workspaceRoot}", "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/mocha", "windows": { "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/mocha.cmd" }, "runtimeArgs": [ "--colors", "--recursive", "${workspaceRoot}/out/**/*.test.js" ], "env": { "NODE_PATH": "${workspaceRoot}/out" }, "sourceMaps": true, "outFiles": [ "${workspaceRoot}/out/**/*.js" ], "internalConsoleOptions": "openOnSessionStart" }, { "type": "node", "request": "launch", "name": "Integration Tests", "cwd": "${workspaceRoot}", "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/mocha", "windows": { "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/mocha.cmd" }, "runtimeArgs": [ "--colors", "--recursive", "${workspaceRoot}/out/**/*.api.js" ], "sourceMaps": true, "outFiles": [ "${workspaceRoot}/lib/**/*.js" ], "internalConsoleOptions": "openOnSessionStart" }, { "type": "chrome", "request": "launch", "name": "Demo Client (Chrome)", "url": "http://127.0.0.1:3000", "webRoot": "${workspaceFolder}/" }, { "type": "msedge", "request": "launch", "name": "Demo Client (Edge)", "url": "http://127.0.0.1:3000", "webRoot": "${workspaceFolder}/" }, { "type": "node", "request": "launch", "name": "Demo Server", "runtimeExecutable": "npm", "runtimeArgs": ["start"], "stopOnEntry": true, "runtimeVersion": "22", "serverReadyAction": { "action": "openExternally", "pattern": "App listening to (http://.*?:[0-9]+)" } } ] } ================================================ FILE: .vscode/settings.json ================================================ { "typescript.preferences.importModuleSpecifier": "non-relative", "typescript.preferences.quoteStyle": "single" } ================================================ FILE: .vscode/tasks.json ================================================ { "version": "2.0.0", "presentation": { "echo": false, "reveal": "always", "focus": false, "panel": "dedicated", "showReuseMessage": true }, "tasks": [ // Compound tasks { "label": "dev", "detail": "Runs all tasks required to run the demo in a single terminal using concurrently. This does not support problem matching.", "type": "npm", "script": "dev", "isBackground": true, "group": { "kind": "build", "isDefault": true } }, { "label": "dev (separate terminals)", "detail": "Runs all tasks required to run the demo in separate terminals. This does support problem matching.", "dependsOn": ["demo-server", "tsc", "esbuild", "esbuild-demo-client", "esbuild-demo-server"], "group": "build" }, // Demo { "label": "demo-server", "type": "npm", "script": "start", "group": "build", "isBackground": true, "problemMatcher": [], "presentation": { "group": "xterm-demo" } }, // Build { "label": "tsc", "type": "npm", "script": "tsc-watch", "group": "build", "isBackground": true, "problemMatcher": "$tsc-watch", "presentation": { "group": "xterm-build" } }, { "label": "esbuild", "type": "npm", "script": "esbuild-watch", "group": "build", "isBackground": true, "problemMatcher": "$esbuild-watch", "presentation": { "group": "xterm-build" } }, { "label": "esbuild-demo-client", "type": "npm", "script": "esbuild-demo-client-watch", "dependsOn": ["esbuild", "tsc"], "group": "build", "isBackground": true, "problemMatcher": "$esbuild-watch", "presentation": { "group": "xterm-demo" } }, { "label": "esbuild-demo-server", "type": "npm", "script": "esbuild-demo-server-watch", "dependsOn": ["esbuild", "tsc"], "group": "build", "isBackground": true, "problemMatcher": "$esbuild-watch", "presentation": { "group": "xterm-demo" } }, // Test { "type": "npm", "script": "test", "group":{ "kind": "test", "isDefault": true }, "problemMatcher": [] } ] } ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting [@Tyriar](https://twitter.com/Tyriar) or [@pariskasid](https://twitter.com/pariskasid) on Twitter. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org ================================================ FILE: CONTRIBUTING.md ================================================ # How to contribute to xterm.js - [Contributing code](#contributing-code) - [Opening issues](#opening-issues) - [Answering discussion questions](#answering-discussion-questions) ## Contributing code You can find issues to work on by looking at issues labeled with [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). It's a good idea to comment on the issue saying that you're looking into it, just in case someone else comes along and you duplicate work. Once you have your issue, here are the steps to contribute: - Fork [xterm.js](https://github.com/xtermjs/xterm.js/) ([how to fork a repo](https://help.github.com/articles/fork-a-repo)). - Get the [xterm.js demo](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo) running. - Make the fix and verify it works in the demo. Be sure to follow thew general code style of the rest of the project. - If your changes are easy to test or likely to regress in the future, add tests. These could be unit or integration (playwright) tests. - Submit a pull request ([how to create a pull request](https://help.github.com/articles/fork-a-repo)). > ![TIP] > Don't put more than one feature or fix in a single pull request. The smaller pull requests are the easier they are to review and merge. By contributing code to xterm.js you: - Agree to license the contributed code under xterm.js' [MIT license](LICENSE). - Confirm that you have the right to contribute and license the code in question. This means that either you hold all rights on the code, or the rights holder has explicitly granted the right to use it like this, through a compatible open source license or through a direct agreement with you. ## Opening issues The preferred way to report bugs or request features is to use [GitHub issues](http://github.com/xtermjs/xterm.js/issues). ### Creating great issues - Include information about **the browser in which the problem occurred** or the terminal being used. If you tested several browsers and the problem occurred in all of them, mention this fact in the bug report. Also include browser version numbers and the operating system that you're on. - Include the version of xterm.js being used, preferably either with the latest `beta` tagged release on npm or reproducing in the demo on the `master` branch. - Mention precisely what went wrong. What did you expect to happen? What happened instead? Describe the exact steps a maintainer has to take to make the problem occur. - If the problem can not be reproduced in the [demo of xterm.js](https://github.com/xtermjs/xterm.js/wiki/Contributing#running-the-demo), provide an HTML document that demonstrates the problem. - Be polite and follow [the code of conduct](https://github.com/xtermjs/xterm.js?tab=coc-ov-file#readme). ### Issue triaging philosophy It's pretty common for maintainers of large open source projects to suffer from burnout, especially when needing to triage a large number of incoming issues instead of actually building things. Here are some of the steps we take to try mitigate this: - Support questions live in [GH discussions](https://github.com/xtermjs/xterm.js/discussions), issues may be transfered there without further comment and core maintainers may or may not participate in discussions. - Issues are strictly for well defined features or bugs that are _actionable_. - Sometimes features are out of scope. A common example of this is a niche feature that the pricipal implementation ([VS Code](https://code.visualstudio.com/)) won't leverage and therefore would be difficult to maintain and likely suffer from bitrot. The reporter may not agree with this, but you could always create an addon if that works or maintain your own fork if it comes to that. - If a feature does not have a clear way forward or needs more discussion it may be closed ro moved to a discussion. - If a bug is not easily reproducible it may be closed or moved to a discussion. Generally issues that are labeled are something we want to do or has actionable steps to look into further. ## 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. ================================================ FILE: LICENSE ================================================ Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js) Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com) Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/) 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. ================================================ FILE: README.md ================================================ # [![xterm.js](images/logo-full.png)](https://xtermjs.org) Xterm.js is a frontend component that enables applications to bring fully-featured terminals to their users in the browser. It's used by popular projects such as [VS Code](https://code.visualstudio.com/) (and its forks), [Tabby](https://tabby.sh/) and [Hyper](https://hyper.is/). ## Features - **Terminal apps just work**: Xterm.js works with most terminal apps such as `bash`, `vim`, and `tmux`, including support for curses-based apps and mouse events. - **Performant**: Xterm.js is *really* fast and includes an optional GPU-accelerated renderer. - **Rich Unicode support**: Supports CJK, emojis, and IMEs. - **Self-contained**: The core library has zero dependencies. - **Accessible**: Screen reader mode and minimum contrast ratio support can be turned on. - **And much more**: Links, theming, custom glyphs, addons, well documented API, etc. ## What xterm.js is not - Xterm.js is not a terminal application that you can download and use on your computer. - Xterm.js is not `bash`. Xterm.js can be connected to processes like `bash` and let you interact with them (provide input, receive output) through a library like [node-pty](https://github.com/microsoft/node-pty). ## Getting Started First, you need to install the module. We ship exclusively through [npm](https://www.npmjs.com), so you need that installed and then add [@xterm/xterm](https://www.npmjs.com/package/@xterm/xterm) as a dependency by running: ```bash npm install --save @xterm/xterm ``` The recommended way to load xterm.js with the ES module syntax, either using TypeScript or a modern JS tooling. Note that both CommonJS and ES module files are shipped with the npm package. ```javascript import { Terminal } from '@xterm/xterm'; ``` Then instantiate a `Terminal` object, open it on an element and write to it: ```javascript const term = new Terminal(); term.open(document.getElementById('terminal')); term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ') ``` Most use cases will hook up input and output to a pseudoterminal API such as [node-pty](https://www.npmjs.com/package/node-pty) which will drive the terminal. ```javascript pty.onData(data => term.write(data)); term.onData(data => pty.write(data)); ``` Then make sure to also include the `css/xterm.css` file, for example in HTML: ```html ``` ### Standalone example Here is a complete standalone example in a HTML file: ```html
``` ### Addons Addons are separate modules that extend the `Terminal` by building on the [xterm.js API](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts). To use an addon, you first need to install it in your project: ```bash npm install --save @xterm/addon-web-links ``` Then import the addon, instantiate it and call `Terminal.loadAddon`: ```ts import { Terminal } from '@xterm/xterm'; import { WebLinksAddon } from '@xterm/addon-web-links'; const terminal = new Terminal(); // Load WebLinksAddon on terminal, this is all that's needed to get web links // working in the terminal. terminal.loadAddon(new WebLinksAddon()); ``` The xterm.js team maintains the following addons, but anyone can build them: - [`@xterm/addon-attach`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-attach): Attaches to a server running a process via a websocket - [`@xterm/addon-clipboard`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-clipboard): Access the browser's clipboard - [`@xterm/addon-fit`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-fit): Fits the terminal to the containing element - [`@xterm/addon-image`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-image): Adds image support - [`@xterm/addon-ligatures`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-ligatures): Enables rendering of ligatures - [`@xterm/addon-progress`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-progress): Adds support for the progress API (`OSC 9;4`) - [`@xterm/addon-search`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-search): Adds search functionality - [`@xterm/addon-serialize`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-serialize): Serializes the terminal's buffer to VT sequences or HTML - [`@xterm/addon-unicode-graphemes`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-unicode-graphemes): Enhanced unicode support including grapheme clustering (experimental) - [`@xterm/addon-unicode11`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-unicode11): Updates character widths to their unicode11 values - [`@xterm/addon-web-fonts`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-web-fonts): Easily integrate web fonts - [`@xterm/addon-web-links`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-web-links): Adds web link detection and interaction - [`@xterm/addon-webgl`](https://github.com/xtermjs/xterm.js/tree/master/addons/addon-webgl): Renders xterm.js using a `canvas` element's webgl2 context ## Browser Support Since xterm.js is typically implemented as a developer tool, generally only modern evergreen browsers are supported officially. Specifically the latest versions of *Chrome*, *Edge*, *Firefox*, and *Safari*. Xterm.js also 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 We also publish [`@xterm/headless`](https://www.npmjs.com/package/@xterm/headless) which is a stripped down version of xterm.js that runs headless in Node.js. An example use case for this is to keep track of a terminal's state where the process is running and using the [serialize addon](https://www.npmjs.com/package/@xterm/addon-serialize) so it can get all state restored upon reconnection. ## API The full API for xterm.js is contained within the [TypeScript declaration file](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts), use the branch/tag picker in GitHub (`w`) to navigate to the correct version of the API. Some APIs may be marked with *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 Stable releases are done on an as needed basis. All current and past releases are available on this repo's [releases page](https://github.com/sourcelair/xterm.js/releases), you can see what's planned for upcoming releases looking through the repository [milestones](https://github.com/sourcelair/xterm.js/milestones). ### Beta builds Beta releases are continuously published off the `master` branch. Install the latest beta build with: ```bash npm install --save @xterm/xterm@beta ``` The principal implementation (VS Code) typically uses the latest or near the latest beta build. Generally they are quite stable but can potentially contain bugs or breaking changes. If stability is very important we recommend using the beta build primarily to test out new features and to verify bug fixes, unless you're tracking what's landing and are comfortable taking that risk. ## Contributing Read [CONTRIBUTING.md](https://github.com/xtermjs/xterm.js/blob/master/CONTRIBUTING.md) to learn how to contribute to the project. ## Real-world uses Xterm.js is used in many world-class applications to provide great terminal experiences. - [**SourceLair**](https://www.sourcelair.com/): In-browser IDE that provides its users with fully-featured Linux terminals based on xterm.js. - [**Microsoft Visual Studio Code**](http://code.visualstudio.com/): Modern, versatile, and powerful open source code editor that provides an integrated terminal based on xterm.js. - [**ttyd**](https://github.com/tsl0922/ttyd): A command-line tool for sharing terminal over the web, with fully-featured terminal emulation based on xterm.js. - [**Eclipse Che**](http://www.eclipse.org/che): Developer workspace server, cloud IDE, and Eclipse next-generation IDE. - [**Codenvy**](http://www.codenvy.com): Cloud workspaces for development teams. - [**CoderPad**](https://coderpad.io): Online interviewing platform for programmers. Run code in many programming languages, with results displayed by xterm.js. - [**WebSSH2**](https://github.com/billchurch/WebSSH2): A web based SSH2 client using xterm.js, socket.io, and ssh2. - [**Spyder Terminal**](https://github.com/spyder-ide/spyder-terminal): A full fledged system terminal embedded on Spyder IDE. - [**Cloud Commander**](https://cloudcmd.io "Cloud Commander"): Orthodox web file manager with console and editor. - [**Next Tech**](https://next.tech "Next Tech"): Online platform for interactive coding and web development courses. Live container-backed terminal uses xterm.js. - [**RStudio**](https://www.rstudio.com/products/RStudio "RStudio"): RStudio is an integrated development environment (IDE) for R. - [**Terminal for Atom**](https://github.com/jsmecham/atom-terminal-tab): A simple terminal for the Atom text editor. - [**Eclipse Orion**](https://orionhub.org): A modern, open source software development environment that runs in the cloud. Code, deploy, and run in the cloud. - [**Gravitational Teleport**](https://github.com/gravitational/teleport): Gravitational Teleport is a modern SSH server for remotely accessing clusters of Linux servers via SSH or HTTPS. - [**Hexlet**](https://en.hexlet.io): Practical programming courses (JavaScript, PHP, Unix, databases, functional programming). A steady path from the first line of code to the first job. - [**Selenoid UI**](https://github.com/aerokube/selenoid-ui): Simple UI for the scalable golang implementation of Selenium Hub named Selenoid. We use XTerm for streaming logs over websockets from docker containers. - [**Portainer**](https://portainer.io): Simple management UI for Docker. - [**SSHy**](https://github.com/stuicey/SSHy): HTML5 Based SSHv2 Web Client with E2E encryption utilising xterm.js, SJCL & websockets. - [**JupyterLab**](https://github.com/jupyterlab/jupyterlab): An extensible computational environment for Jupyter, supporting interactive data science and scientific computing across all programming languages. - [**Theia**](https://github.com/theia-ide/theia): Theia is a cloud & desktop IDE framework implemented in TypeScript. - [**Opshell**](https://github.com/ricktbaker/opshell) Ops Helper tool to make life easier working with AWS instances across multiple organizations. - [**Proxmox VE**](https://www.proxmox.com/en/proxmox-ve): Proxmox VE is a complete open-source platform for enterprise virtualization. It uses xterm.js for container terminals and the host shell. - [**Script Runner**](https://github.com/ioquatix/script-runner): Run scripts (or a shell) in Atom. - [**Whack Whack Terminal**](https://github.com/Microsoft/WhackWhackTerminal): Terminal emulator for Visual Studio 2017. - [**VTerm**](https://github.com/vterm/vterm): Extensible terminal emulator based on Electron and React. - [**electerm**](http://electerm.html5beta.com): electerm is a terminal/ssh/sftp client(mac, win, linux) based on electron/node-pty/xterm. - [**Kubebox**](https://github.com/astefanutti/kubebox): Terminal console for Kubernetes clusters. - [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure. - [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace. - [**rtty**](https://github.com/zhaojh329/rtty): Access your terminals from anywhere via the web. - [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS. - [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker. - [**Azure Data Studio**](https://github.com/Microsoft/azuredatastudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux. - [**FreeMAN**](https://github.com/matthew-matvei/freeman): A free, cross-platform file manager for power users. - [**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/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. - [**SSH Web Client**](https://github.com/roke22/PHP-SSH2-Web-Client): SSH Web Client with PHP. - [**Juno**](http://junolab.org/): A flexible Julia IDE, based on Atom. - [**webssh**](https://github.com/huashengdun/webssh): Web based ssh client. - [**info-beamer hosted**](https://info-beamer.com): Uses xterm.js to manage digital signage devices from the web dashboard. - [**Jumpserver**](https://github.com/jumpserver/luna): Jumpserver Luna project, Jumpserver is a bastion server project, Luna use xterm.js for web terminal emulation. - [**LxdMosaic**](https://github.com/turtle0x1/LxdMosaic): Uses xterm.js to give terminal access to containers through LXD - [**CodeInterview.io**](https://codeinterview.io): A coding interview platform in 25+ languages and many web frameworks. Uses xterm.js to provide shell access. - [**Bastillion**](https://www.bastillion.io): Bastillion is an open-source web-based SSH console that centrally manages administrative access to systems. - [**PHP App Server**](https://github.com/cubiclesoft/php-app-server/): Create lightweight, installable almost-native applications for desktop OSes. ExecTerminal (nicely wraps the xterm.js Terminal), TerminalManager, and RunProcessSDK are self-contained, reusable ES5+ compliant Javascript components. - [**NgTerminal**](https://github.com/qwefgh90/ng-terminal): NgTerminal is a web terminal that leverages xterm.js on Angular 7+. You can easily add it into your application by adding `` into your component. - [**tty-share**](https://tty-share.com): Extremely simple terminal sharing over the Internet. - [**Ten Hands**](https://github.com/saisandeepvaddi/ten-hands): One place to run your command-line tasks. - [**WebAssembly.sh**](https://webassembly.sh): A WebAssembly WASI browser terminal - [**Gus**](https://gus.jp): A shared coding pad where you can run Python with xterm.js - [**Linode**](https://linode.com): Linode uses xterm.js to provide users a web console for their Linode instances. - [**FluffOS**](https://www.fluffos.info): Active maintained LPMUD driver with websocket support. - [**x-terminal**](https://atom.io/packages/x-terminal): Atom plugin for providing terminals inside your Atom workspace. - [**CoCalc**](https://cocalc.com/): Lots of free software pre-installed, to chat, collaborate, develop, program, publish, research, share, teach, in C++, HTML, Julia, Jupyter, LaTeX, Markdown, Python, R, SageMath, Scala, ... - [**Dank Domain**](https://www.DDgame.us/): Open source multiuser medieval game supporting old & new terminal emulation. - [**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 - [**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. - [**Commas**](https://github.com/CyanSalt/commas): Commas is a hackable terminal and command runner. - [**Devtron**](https://github.com/devtron-labs/devtron): Software Delivery Workflow For Kubernetes. - [**NxShell**](https://github.com/nxshell/nxshell): An easy to use new terminal for SSH. - [**gifcast**](https://dstein64.github.io/gifcast/): Converts an asciinema cast to an animated GIF. - [**WizardWebssh**](https://gitlab.com/mikeramsey/wizardwebssh): A terminal with Pyqt5 Widget for embedding, which can be used as an ssh client to connect to your ssh servers. It is written in Python, based on tornado, paramiko, and xterm.js. - [**Wizard Assistant**](https://wizardassistant.com/): Wizard Assistant comes with advanced automation tools, preloaded common and special time-saving commands, and a built-in SSH terminal. Now you can remotely administer, troubleshoot, and analyze any system with ease. - [**ucli**](https://github.com/tsadarsh/ucli): Command Line for everyone :family_man_woman_girl_boy: at [www.ucli.tech](https://www.ucli.tech). - [**Tess**](https://github.com/SquitchYT/Tess/): Simple Terminal Fully Customizable for Everyone. Discover more at [tessapp.dev](https://tessapp.dev) - [**HashiCorp Nomad**](https://www.nomadproject.io/): A container orchestrator with the ability to connect to remote tasks via a web interface using websockets and xterm.js. - [**TermPair**](https://github.com/cs01/termpair): View and control terminals from your browser with end-to-end encryption - [**gdbgui**](https://github.com/cs01/gdbgui): Browser-based frontend to gdb (gnu debugger) - [**goormIDE**](https://ide.goorm.io/): Run almost every programming languages with real-time collaboration, live pair programming, and built-in messenger. - [**FleetDeck**](https://fleetdeck.io): Remote desktop & virtual terminal - [**OpenSumi**](https://github.com/opensumi/core): A framework helps you quickly build Cloud or Desktop IDE products. - [**KubeSail**](https://kubesail.com): The Self-Hosting Company - uses xterm to allow users to exec into kubernetes pods and build github apps - [**WiTTY**](https://github.com/syssecfsu/witty): Web-based interactive terminal emulator that allows users to easily record, share, and replay console sessions. - [**libv86 Terminal Forwarding**](https://github.com/hello-smile6/libv86-terminal-forwarding): Peer-to-peer SSH for the web, using WebRTC via [Bugout](https://github.com/chr15m/bugout) for data transfer and [v86](https://github.com/copy/v86) for web-based virtualization. - [**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 - [**Wave Terminal**](https://waveterm.dev): An open-source, ai-native, terminal built for seamless workflows. - [**eva**](https://github.com/info24/eva): Eva is a web application for SSH remote login, developed in Go. - [**OpenSFTP**](https://opensftp.com): Super beautiful SSH and SFTP integrated workspace client. - [**balena**](https://www.balena.io/): Balena is a full-stack solution for developing, deploying, updating, and troubleshooting IoT Edge devices. We use xterm.js to manage & debug devices on [balenaCloud](https://www.balena.io/cloud). - [**Filet Cloud**](https://github.com/fuglaro/filet-cloud): The lean and powerful personal cloud ⛅. - [**pyTermTk**](https://github.com/ceccopierangiolieugenio/pyTermTk): Python Terminal Toolkit - a Spiced Up Cross Compatible TUI Library 🌶️, use xterm.js for the [HTML5 exporter](https://ceccopierangiolieugenio.github.io/pyTermTk/sandbox/sandbox.html). - [**ecmaOS**](https://ecmaos.sh): A kernel and suite of applications tying modern web technologies into a browser-based operating system. - [**LabEx**](https://labex.io): Interactive learning platform with hands-on labs and xterm.js-based online terminals, focused on learn-by-doing approach. - [**EmuDevz**](https://afska.github.io/emudevz): A free coding game where players learn how to build an emulator from scratch. - [**SSH Connection Manager**](https://github.com/Amtrend/ssh-manager): Modern web-based SSH terminal and SFTP manager with AES-256 encryption, PWA support, and session management. - [**WooTTY**](https://github.com/icoretech/wootty): Flawless browser terminal for real operators. - [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. Please add any new contributions to the end of the list. ## 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-2026, [The xterm.js authors](https://github.com/xtermjs/xterm.js/graphs/contributors) (MIT License)
Copyright (c) 2014-2017, SourceLair, Private Company ([www.sourcelair.com](https://www.sourcelair.com/home)) (MIT License)
Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) ================================================ FILE: addons/addon-attach/.gitignore ================================================ lib node_modules ================================================ FILE: addons/addon-attach/.npmignore ================================================ # Blacklist - exclude everything except npm defaults such as LICENSE, etc * !*/ # Whitelist - lib/ !lib/**/*.d.ts !lib/**/*.js !lib/**/*.js.map !lib/**/*.mjs !lib/**/*.mjs.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 ================================================ FILE: addons/addon-attach/LICENSE ================================================ 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. ================================================ FILE: addons/addon-attach/README.md ================================================ ## @xterm/addon-attach An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables attaching to a web socket. This addon requires xterm.js v4+. ### Install ```bash npm install --save @xterm/addon-attach ``` ### Usage ```ts import { Terminal } from '@xterm/xterm'; import { AttachAddon } from '@xterm/addon-attach'; const terminal = new Terminal(); const attachAddon = new AttachAddon(webSocket); terminal.loadAddon(attachAddon); ``` See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-attach/typings/addon-attach.d.ts) for more advanced usage. ================================================ FILE: addons/addon-attach/package.json ================================================ { "name": "@xterm/addon-attach", "version": "0.12.0", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, "main": "lib/addon-attach.js", "module": "lib/addon-attach.mjs", "types": "typings/addon-attach.d.ts", "repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-attach", "license": "MIT", "keywords": [ "terminal", "xterm", "xterm.js" ], "scripts": { "build": "../../node_modules/.bin/tsgo -p .", "prepackage": "npm run build", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", "start": "node ../../demo/start" } } ================================================ FILE: addons/addon-attach/src/AttachAddon.ts ================================================ /** * Copyright (c) 2014, 2019 The xterm.js authors. All rights reserved. * @license MIT * * Implements the attach method, that attaches the terminal to a WebSocket stream. */ import type { Terminal, IDisposable, ITerminalAddon } from '@xterm/xterm'; import type { AttachAddon as IAttachApi } from '@xterm/addon-attach'; interface IAttachOptions { bidirectional?: boolean; } export class AttachAddon implements ITerminalAddon, IAttachApi { private _socket: WebSocket; private _bidirectional: boolean; private _disposables: IDisposable[] = []; constructor(socket: WebSocket, options?: IAttachOptions) { this._socket = socket; // always set binary type to arraybuffer, we do not handle blobs this._socket.binaryType = 'arraybuffer'; this._bidirectional = !(options && options.bidirectional === false); } public activate(terminal: Terminal): void { this._disposables.push( addSocketListener(this._socket, 'message', ev => { const data: ArrayBuffer | string = ev.data; terminal.write(typeof data === 'string' ? data : new Uint8Array(data)); }) ); if (this._bidirectional) { this._disposables.push(terminal.onData(data => this._sendData(data))); this._disposables.push(terminal.onBinary(data => this._sendBinary(data))); } this._disposables.push(addSocketListener(this._socket, 'close', () => this.dispose())); this._disposables.push(addSocketListener(this._socket, 'error', () => this.dispose())); } public dispose(): void { for (const d of this._disposables) { d.dispose(); } } private _sendData(data: string): void { if (!this._checkOpenSocket()) { return; } this._socket.send(data); } private _sendBinary(data: string): void { if (!this._checkOpenSocket()) { return; } const buffer = new Uint8Array(data.length); for (let i = 0; i < data.length; ++i) { buffer[i] = data.charCodeAt(i) & 255; } 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(socket: WebSocket, type: K, handler: (this: WebSocket, ev: WebSocketEventMap[K]) => any): IDisposable { socket.addEventListener(type, handler); return { dispose: () => { if (!handler) { // Already disposed return; } socket.removeEventListener(type, handler); } }; } ================================================ FILE: addons/addon-attach/src/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es2021", "lib": [ "dom", "es2015" ], "rootDir": ".", "outDir": "../out", "sourceMap": true, "removeComments": true, "strict": true, "types": [ "../../../node_modules/@types/mocha" ], "paths": { "@xterm/addon-attach": [ "../typings/addon-attach.d.ts" ] } }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ] } ================================================ FILE: addons/addon-attach/test/AttachAddon.test.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import WebSocket = require('ws'); import test from '@playwright/test'; import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from '../../../test/playwright/TestUtils'; let ctx: ITestContext; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); await openTerminal(ctx); }); test.afterAll(async () => await ctx.page.close()); test.describe('Search Tests', () => { test.beforeEach(async () => { await ctx.proxy.reset(); }); test('string', async function(): Promise { const port = 8080; const server = new WebSocket.Server({ port }); server.on('connection', socket => socket.send('foo')); await ctx.page.evaluate(`window.term.loadAddon(new window.AttachAddon(new WebSocket('ws://localhost:${port}')))`); await pollFor(ctx.page, `window.term.buffer.active.getLine(0).translateToString(true)`, 'foo'); server.close(); }); test('utf8', async function(): Promise { const port = 8080; const server = new WebSocket.Server({ port }); const data = new Uint8Array([102, 111, 111]); server.on('connection', socket => socket.send(data)); await ctx.page.evaluate(`window.term.loadAddon(new window.AttachAddon(new WebSocket('ws://localhost:${port}')))`); await pollFor(ctx.page, `window.term.buffer.active.getLine(0).translateToString(true)`, 'foo'); server.close(); }); }); ================================================ FILE: addons/addon-attach/test/playwright.config.ts ================================================ import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { testDir: '.', timeout: 10000, projects: [ { name: 'Chromium', use: { browserName: 'chromium' } }, { name: 'FirefoxStable', use: { browserName: 'firefox' } }, { name: 'WebKit', use: { browserName: 'webkit' } } ], reporter: 'list', webServer: { command: 'npm run start', port: 3000, timeout: 120000, reuseExistingServer: !process.env.CI } }; export default config; ================================================ FILE: addons/addon-attach/test/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "ESNext", "lib": [ "es2021", ], "rootDir": ".", "outDir": "../out-test", "sourceMap": true, "removeComments": true, "paths": { "common/*": [ "../../../src/common/*" ], "browser/*": [ "../../../src/browser/*" ], "*": [ "./*" ] }, "strict": true, "types": [ "../../../node_modules/@types/node" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" }, { "path": "../../../test/playwright" } ] } ================================================ FILE: addons/addon-attach/tsconfig.json ================================================ { "files": [], "include": [], "references": [ { "path": "./src" }, { "path": "./test" } ] } ================================================ FILE: addons/addon-attach/typings/addon-attach.d.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal, ITerminalAddon } from '@xterm/xterm'; declare module '@xterm/addon-attach' { export interface IAttachOptions { /** * Whether input should be written to the backend. Defaults to `true`. */ bidirectional?: boolean; } export class AttachAddon implements ITerminalAddon { constructor(socket: WebSocket, options?: IAttachOptions); public activate(terminal: Terminal): void; public dispose(): void; } } ================================================ FILE: addons/addon-attach/webpack.config.js ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ const path = require('path'); const addonName = 'AttachAddon'; const mainFile = 'addon-attach.js'; module.exports = { 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', // Force usage of globalThis instead of global / self. (This is cross-env compatible) globalObject: 'globalThis', }, mode: 'production' }; ================================================ FILE: addons/addon-clipboard/.gitignore ================================================ lib node_modules ================================================ FILE: addons/addon-clipboard/.npmignore ================================================ # Blacklist - exclude everything except npm defaults such as LICENSE, etc * !*/ # Whitelist - lib/ !lib/**/*.d.ts !lib/**/*.js !lib/**/*.js.map !lib/**/*.mjs !lib/**/*.mjs.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 ================================================ FILE: addons/addon-clipboard/LICENSE ================================================ Copyright (c) 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. ================================================ FILE: addons/addon-clipboard/README.md ================================================ ## @xterm/addon-clipboard An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables accessing the system clipboard. This addon requires xterm.js v4+. ### Install ```bash npm install --save @xterm/addon-clipboard ``` ### Usage ```ts import { Terminal } from 'xterm'; import { ClipboardAddon } from '@xterm/addon-clipboard'; const terminal = new Terminal(); const clipboardAddon = new ClipboardAddon(); terminal.loadAddon(clipboardAddon); ``` To use a custom clipboard provider ```ts import { Terminal } from '@xterm/xterm'; import { ClipboardAddon, IClipboardProvider, ClipboardSelectionType } from '@xterm/addon-clipboard'; function b64Encode(data: string): string { // Base64 encode impl } function b64Decode(data: string): string { // Base64 decode impl } class MyCustomClipboardProvider implements IClipboardProvider { private _data: string public readText(selection: ClipboardSelectionType): Promise { return Promise.resolve(b64Encode(this._data)); } public writeText(selection: ClipboardSelectionType, data: string): Promise { this._data = b64Decode(data); return Promise.resolve(); } } const terminal = new Terminal(); const clipboardAddon = new ClipboardAddon(new MyCustomClipboardProvider()); terminal.loadAddon(clipboardAddon); ``` See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-clipboard/typings/addon-clipboard.d.ts) for more advanced usage. ================================================ FILE: addons/addon-clipboard/package.json ================================================ { "name": "@xterm/addon-clipboard", "version": "0.2.0", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, "main": "lib/addon-clipboard.js", "module": "lib/addon-clipboard.mjs", "types": "typings/addon-clipboard.d.ts", "repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-clipboard", "license": "MIT", "keywords": [ "terminal", "xterm", "xterm.js" ], "scripts": { "build": "../../node_modules/.bin/tsgo -p .", "prepackage": "npm run build", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", "start": "node ../../demo/start" }, "dependencies": { "js-base64": "^3.7.5" } } ================================================ FILE: addons/addon-clipboard/src/ClipboardAddon.ts ================================================ /** * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT */ import type { IDisposable, ITerminalAddon, Terminal } from '@xterm/xterm'; import { type IClipboardProvider, ClipboardSelectionType, type IBase64 } from '@xterm/addon-clipboard'; import { Base64 as JSBase64 } from 'js-base64'; export class ClipboardAddon implements ITerminalAddon { private _terminal?: Terminal; private _disposable?: IDisposable; constructor( private _base64: IBase64 = new Base64(), private _provider: IClipboardProvider = new BrowserClipboardProvider() ) {} public activate(terminal: Terminal): void { this._terminal = terminal; this._disposable = terminal.parser.registerOscHandler(52, data => this._setOrReportClipboard(data)); } public dispose(): void { return this._disposable?.dispose(); } private _readText(sel: ClipboardSelectionType, data: string): void { const b64 = this._base64.encodeText(data); this._terminal?.input(`\x1b]52;${sel};${b64}\x07`, false); } private _setOrReportClipboard(data: string): boolean | Promise { const args = data.split(';'); if (args.length < 2) { return true; } const pc = args[0] as ClipboardSelectionType; const pd = args[1]; if (pd === '?') { const text = this._provider.readText(pc); // Report clipboard if (text instanceof Promise) { return text.then((data) => { this._readText(pc, data); return true; }); } this._readText(pc, text); return true; } // Clear clipboard if text is not a base64 encoded string. let text = ''; try { text = this._base64.decodeText(pd); } catch {} const result = this._provider.writeText(pc, text); if (result instanceof Promise) { return result.then(() => true); } return true; } } export class BrowserClipboardProvider implements IClipboardProvider { public async readText(selection: ClipboardSelectionType): Promise { if (selection !== 'c') { return Promise.resolve(''); } return navigator.clipboard.readText(); } public async writeText(selection: ClipboardSelectionType, text: string): Promise { if (selection !== 'c') { return Promise.resolve(); } return navigator.clipboard.writeText(text); } } export class Base64 implements IBase64 { public encodeText(data: string): string { return JSBase64.encode(data); } public decodeText(data: string): string { const text = JSBase64.decode(data); if (!JSBase64.isValid(data) || JSBase64.encode(text) !== data) { return ''; } return text; } } ================================================ FILE: addons/addon-clipboard/src/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es2021", "lib": [ "dom", "es2015" ], "rootDir": ".", "outDir": "../out", "sourceMap": true, "removeComments": true, "strict": true, "types": [ "../../../node_modules/@types/mocha" ], "paths": { "browser/*": [ "../../../src/browser/*" ], "@xterm/addon-clipboard": [ "../typings/addon-clipboard.d.ts" ] } }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/browser" } ] } ================================================ FILE: addons/addon-clipboard/test/ClipboardAddon.test.ts ================================================ /** * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT */ import test from '@playwright/test'; import { deepEqual, ok, strictEqual } from 'assert'; import { ITestContext, createTestContext, launchBrowser, openTerminal, timeout } from '../../../test/playwright/TestUtils'; let ctx: ITestContext; test.beforeAll(async ({ browser }, testInfo) => { ctx = await createTestContext(browser); await openTerminal(ctx); }); test.afterAll(async () => { await ctx.page.close(); }); test.describe('ClipboardAddon', () => { test.beforeEach(async ({}, testInfo) => { // DEBT: This test doesn't work since the migration to @playwright/test if (ctx.browser.browserType().name() !== 'chromium') { testInfo.skip(); return; } if (ctx.browser.browserType().name() === 'chromium') { // Enable clipboard access in chromium without user gesture await ctx.page.context().grantPermissions(['clipboard-read', 'clipboard-write']); } await ctx.page.evaluate(` window.term.reset() window.clipboard?.dispose(); window.clipboard = new ClipboardAddon(); window.term.loadAddon(window.clipboard); `); }); test.beforeEach(async () => { await ctx.proxy.reset(); }); const testDataEncoded = 'aGVsbG8gd29ybGQ='; const testDataDecoded = 'hello world'; test.describe('write data', async function (): Promise { test('simple string', async () => { await ctx.proxy.write(`\x1b]52;c;${testDataEncoded}\x07`); deepEqual(await ctx.page.evaluate(() => window.navigator.clipboard.readText()), testDataDecoded); }); test('invalid base64 string', async () => { await ctx.proxy.write(`\x1b]52;c;${testDataEncoded}invalid\x07`); deepEqual(await ctx.page.evaluate(() => window.navigator.clipboard.readText()), ''); }); test('empty string', async () => { await ctx.proxy.write(`\x1b]52;c;${testDataEncoded}\x07`); await ctx.proxy.write(`\x1b]52;c;\x07`); deepEqual(await ctx.page.evaluate(() => window.navigator.clipboard.readText()), ''); }); }); test.describe('read data', async function (): Promise { test('simple string', async () => { await ctx.page.evaluate(` window.data = []; window.term.onData(e => data.push(e)); `); await ctx.page.evaluate(() => window.navigator.clipboard.writeText('hello world')); await ctx.proxy.write(`\x1b]52;c;?\x07`); deepEqual(await ctx.page.evaluate('window.data'), [`\x1b]52;c;${testDataEncoded}\x07`]); }); test('clear clipboard', async () => { await ctx.proxy.write(`\x1b]52;c;!\x07`); await ctx.proxy.write(`\x1b]52;c;?\x07`); deepEqual(await ctx.page.evaluate(() => window.navigator.clipboard.readText()), ''); }); }); }); ================================================ FILE: addons/addon-clipboard/test/playwright.config.ts ================================================ import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { testDir: '.', timeout: 10000, projects: [ { name: 'Chromium', use: { browserName: 'chromium' } }, { name: 'FirefoxStable', use: { browserName: 'firefox' } }, { name: 'WebKit', use: { browserName: 'webkit' } } ], reporter: 'list', webServer: { command: 'npm run start', port: 3000, timeout: 120000, reuseExistingServer: !process.env.CI } }; export default config; ================================================ FILE: addons/addon-clipboard/test/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "ESNext", "lib": [ "es2021", ], "rootDir": ".", "outDir": "../out-test", "sourceMap": true, "removeComments": true, "paths": { "common/*": [ "../../../src/common/*" ], "browser/*": [ "../../../src/browser/*" ], "*": [ "./*" ] }, "strict": true, "types": [ "../../../node_modules/@types/node" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" }, { "path": "../../../test/playwright" } ] } ================================================ FILE: addons/addon-clipboard/tsconfig.json ================================================ { "files": [], "include": [], "references": [ { "path": "./src" }, { "path": "./test" } ] } ================================================ FILE: addons/addon-clipboard/typings/addon-clipboard.d.ts ================================================ /** * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal, ITerminalAddon } from '@xterm/xterm'; declare module '@xterm/addon-clipboard' { /** * An xterm.js addon that enables accessing the system clipboard from * xterm.js. */ export class ClipboardAddon implements ITerminalAddon { /** * Creates a new clipboard addon. * @param base64 An optional base64 encoder/decoder. * @param provider An optional clipboard provider. */ constructor(base64?: IBase64, provider?: IClipboardProvider); /** * Activates the addon * @param terminal The terminal the addon is being loaded in. */ public activate(terminal: Terminal): void; /** * Disposes the addon. */ public dispose(): void } /** * Clipboard selection type. This is used to specify which selection buffer to * read or write to. * - SYSTEM `c`: The system clipboard. * - PRIMARY `p`: The primary clipboard. This is provided for compatibility * with Linux X11. */ export const enum ClipboardSelectionType { SYSTEM = 'c', PRIMARY = 'p', } export interface IBase64 { /** * Converts a utf-8 string to a base64 string. * @param data The utf-8 string to convert to base64 string. */ encodeText(data: string): string; /** * Converts a base64 string to a utf-8 string. * @param data The base64 string to convert to utf-8 string. * @throws An error if the input is not valid base64. */ decodeText(data: string): string; } /** * A default Base64 encoding and decoding type. **/ export class Base64 implements IBase64 { /** * Converts a utf-8 string to a base64 string. * @param data The utf-8 string to convert to base64 string. */ public encodeText(data: string): string; /** * Converts a base64 string to a utf-8 string. * @param data The base64 string to convert to utf-8 string. * @throws An error if the input is not valid base64. */ public decodeText(data: string): string; } export interface IClipboardProvider { /** * Gets the clipboard content. * @param selection The clipboard selection to read. * @returns A promise that resolves with clipboard selection data. */ readText(selection: ClipboardSelectionType): string | Promise; /** * Sets the clipboard content. * @param selection The clipboard selection to set. * @param data The clipboard text to write. */ writeText(selection: ClipboardSelectionType, text: string): void | Promise; } /** * The clipboard provider interface that enables xterm.js to access the system clipboard. */ export class BrowserClipboardProvider implements IClipboardProvider{ /** * Reads text from the clipboard. * @param selection The selection type to read from. * @returns A promise that resolves with the text from the clipboard. */ public readText(selection: ClipboardSelectionType): Promise; /** * Writes text to the clipboard. * @param selection The selection type to write to. * @param data The text to write to the clipboard. * @returns A promise that resolves when the text has been written to the clipboard. */ public writeText(selection: ClipboardSelectionType, data: string): Promise; } } ================================================ FILE: addons/addon-clipboard/webpack.config.js ================================================ /** * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT */ const path = require('path'); const addonName = 'ClipboardAddon'; const mainFile = 'addon-clipboard.js'; module.exports = { 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' }; ================================================ FILE: addons/addon-fit/.gitignore ================================================ lib node_modules ================================================ FILE: addons/addon-fit/.npmignore ================================================ # Blacklist - exclude everything except npm defaults such as LICENSE, etc * !*/ # Whitelist - lib/ !lib/**/*.d.ts !lib/**/*.js !lib/**/*.js.map !lib/**/*.mjs !lib/**/*.mjs.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 ================================================ FILE: addons/addon-fit/LICENSE ================================================ Copyright (c) 2019, 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. ================================================ FILE: addons/addon-fit/README.md ================================================ ## @xterm/addon-fit An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables fitting the terminal's dimensions to a containing element. This addon requires xterm.js v4+. ### Install ```bash npm install --save @xterm/addon-fit ``` ### Usage ```ts import { Terminal } from '@xterm/xterm'; import { FitAddon } from '@xterm/addon-fit'; const terminal = new Terminal(); const fitAddon = new FitAddon(); terminal.loadAddon(fitAddon); terminal.open(containerElement); fitAddon.fit(); ``` See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-fit/typings/addon-fit.d.ts) for more advanced usage. ================================================ FILE: addons/addon-fit/package.json ================================================ { "name": "@xterm/addon-fit", "version": "0.11.0", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, "main": "lib/addon-fit.js", "module": "lib/addon-fit.mjs", "types": "typings/addon-fit.d.ts", "repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-fit", "license": "MIT", "keywords": [ "terminal", "xterm", "xterm.js" ], "scripts": { "build": "../../node_modules/.bin/tsgo -p .", "prepackage": "npm run build", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", "start": "node ../../demo/start" } } ================================================ FILE: addons/addon-fit/src/FitAddon.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import type { Terminal, ITerminalAddon, IRenderDimensions } from '@xterm/xterm'; import type { FitAddon as IFitApi } from '@xterm/addon-fit'; import { ViewportConstants } from 'browser/shared/Constants'; interface ITerminalDimensions { /** * The number of rows in the terminal. */ rows: number; /** * The number of columns in the terminal. */ cols: number; } const MINIMUM_COLS = 2; const MINIMUM_ROWS = 1; function getWindow(e: Node): Window { if (e?.ownerDocument?.defaultView) { return e.ownerDocument.defaultView; } return window; } function _getComputedStyle(el: HTMLElement): CSSStyleDeclaration { return getWindow(el).getComputedStyle(el, null); } export class FitAddon implements ITerminalAddon, IFitApi { private _terminal: Terminal | undefined; public activate(terminal: Terminal): void { this._terminal = terminal; } public dispose(): void {} public fit(): void { const dims = this.proposeDimensions(); if (!dims || !this._terminal || isNaN(dims.cols) || isNaN(dims.rows)) { return; } // Force a full render if (this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols) { this._terminal.resize(dims.cols, dims.rows); } } public proposeDimensions(): ITerminalDimensions | undefined { if (!this._terminal) { return undefined; } if (!this._terminal.element || !this._terminal.element.parentElement) { return undefined; } const dims: IRenderDimensions | undefined = this._terminal.dimensions; if (!dims || dims.css.cell.width === 0 || dims.css.cell.height === 0) { return undefined; } const showScrollbar = this._terminal.options.scrollbar?.showScrollbar ?? true; const scrollbarWidth = (this._terminal.options.scrollback === 0 || !showScrollbar ? 0 : (this._terminal.options.scrollbar?.width ?? ViewportConstants.DEFAULT_SCROLL_BAR_WIDTH)); const parentElementStyle = _getComputedStyle(this._terminal.element.parentElement); const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')); const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width'))); const elementStyle = _getComputedStyle(this._terminal.element); const elementPadding = { top: parseInt(elementStyle.getPropertyValue('padding-top')), bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')), right: parseInt(elementStyle.getPropertyValue('padding-right')), left: parseInt(elementStyle.getPropertyValue('padding-left')) }; const elementPaddingVer = elementPadding.top + elementPadding.bottom; const elementPaddingHor = elementPadding.right + elementPadding.left; const availableHeight = parentElementHeight - elementPaddingVer; const availableWidth = parentElementWidth - elementPaddingHor - scrollbarWidth; const geometry = { 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; } } ================================================ FILE: addons/addon-fit/src/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es2021", "lib": [ "dom", "es2015" ], "rootDir": ".", "outDir": "../out", "sourceMap": true, "removeComments": true, "strict": true, "types": [ "../../../node_modules/@types/mocha" ], "paths": { "browser/*": [ "../../../src/browser/*" ], "@xterm/addon-fit": [ "../typings/addon-fit.d.ts" ] } }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/browser" } ] } ================================================ FILE: addons/addon-fit/test/FitAddon.test.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import test from '@playwright/test'; import { deepEqual, ok, strictEqual } from 'assert'; import { ITestContext, createTestContext, openTerminal, timeout } from '../../../test/playwright/TestUtils'; let ctx: ITestContext; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); ctx.page.setViewportSize({ width: 1024, height: 768 }); await openTerminal(ctx); }); test.afterAll(async () => await ctx.page.close()); test.describe('FitAddon', () => { test.beforeEach(async function(): Promise { await ctx.page.evaluate(`document.querySelector('#terminal-container').style.display=''`); await ctx.page.evaluate(` window.term.reset() window.fit?.dispose(); window.fit = new FitAddon(); window.term.loadAddon(window.fit); `); }); // test.beforeEach(async function(): Promise { // await ctx.page.evaluate(`document.querySelector('#terminal-container').style.display=''`); // await openTerminal(page); // }); // after(async () => { // await browser.close(); // }); // afterEach(async function(): Promise { // await ctx.proxy.dispose(); // }); test('no terminal', async function(): Promise { await ctx.page.evaluate(`window.fit2 = new FitAddon();`); strictEqual(await ctx.page.evaluate(`window.fit2.proposeDimensions()`), undefined); await ctx.page.evaluate(`window.fit2.dispose();`); }); test.describe('proposeDimensions', () => { test('default', async function(): Promise { await setDimensions(); const dimensions: {cols: number, rows: number} = await ctx.page.evaluate(`window.fit.proposeDimensions()`); ok(dimensions.cols > 85); ok(dimensions.cols < 88); ok(dimensions.rows > 24); ok(dimensions.rows < 29); }); test('width', async function(): Promise { await setDimensions(1008); const dimensions: {cols: number, rows: number} = await ctx.page.evaluate(`window.fit.proposeDimensions()`); ok(dimensions.cols > 108); ok(dimensions.cols < 111); ok(dimensions.rows > 24); ok(dimensions.rows < 29); }); test('small', async function(): Promise { await setDimensions(1, 1); deepEqual(await ctx.page.evaluate(`window.fit.proposeDimensions()`), { cols: 2, rows: 1 }); }); test('hidden', async function(): Promise { await ctx.proxy.dispose(); await ctx.page.evaluate(`document.querySelector('#terminal-container').style.display='none'`); await ctx.page.evaluate(`window.term = new Terminal()`); await ctx.page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`); await setDimensions(); const dimensions: { cols: number, rows: number } | undefined = await ctx.page.evaluate(`window.fit.proposeDimensions()`); // The value of dims will be undefined if the char measure strategy falls back to the DOM // method, so only assert if it's not undefined. if (dimensions) { ok(dimensions.cols > 85); ok(dimensions.cols < 88); ok(dimensions.rows > 24); ok(dimensions.rows < 29); } }); }); test.describe('fit', () => { test('default', async function(): Promise { await setDimensions(); await ctx.page.evaluate(`window.fit.fit()`); const cols: number = await ctx.proxy.cols; const rows: number = await ctx.proxy.rows; ok(cols > 85); ok(cols < 88); ok(rows > 24); ok(rows < 29); }); test('width', async function(): Promise { await setDimensions(1008); await ctx.page.evaluate(`window.fit.fit()`); const cols: number = await ctx.proxy.cols; const rows: number = await ctx.proxy.rows; ok(cols > 108); ok(cols < 111); ok(rows > 24); ok(rows < 29); }); test('small', async function(): Promise { await setDimensions(1, 1); await ctx.page.evaluate(`window.fit.fit()`); strictEqual(await ctx.proxy.cols, 2); strictEqual(await ctx.proxy.rows, 1); }); }); }); async function setDimensions(width: number = 800, height: number = 450): Promise { await ctx.page.evaluate(` document.querySelector('#terminal-container').style.width='${width}px'; document.querySelector('#terminal-container').style.height='${height}px'; document.querySelector('#terminal-container').style.display=''; `); // HACK: Await a short period as hiding #terminal-container can mess with other tests await timeout(500); } ================================================ FILE: addons/addon-fit/test/playwright.config.ts ================================================ import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { testDir: '.', timeout: 10000, projects: [ { name: 'Chromium', use: { browserName: 'chromium' } }, { name: 'FirefoxStable', use: { browserName: 'firefox' } }, { name: 'WebKit', use: { browserName: 'webkit' } } ], reporter: 'list', webServer: { command: 'npm run start', port: 3000, timeout: 120000, reuseExistingServer: !process.env.CI } }; export default config; ================================================ FILE: addons/addon-fit/test/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "ESNext", "lib": [ "es2021", ], "rootDir": ".", "outDir": "../out-test", "sourceMap": true, "removeComments": true, "paths": { "common/*": [ "../../../src/common/*" ], "browser/*": [ "../../../src/browser/*" ], "*": [ "./*" ] }, "strict": true, "types": [ "../../../node_modules/@types/node" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" }, { "path": "../../../test/playwright" } ] } ================================================ FILE: addons/addon-fit/tsconfig.json ================================================ { "files": [], "include": [], "references": [ { "path": "./src" }, { "path": "./test" } ] } ================================================ FILE: addons/addon-fit/typings/addon-fit.d.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal, ITerminalAddon } from '@xterm/xterm'; declare module '@xterm/addon-fit' { /** * An xterm.js addon that enables resizing the terminal to the dimensions of * its containing element. */ export class FitAddon implements ITerminalAddon { /** * Creates a new fit addon. */ 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; /** * Resizes the terminal to the dimensions of its containing element. */ public fit(): void; /** * Gets the proposed dimensions that will be used for a fit. */ public proposeDimensions(): ITerminalDimensions | undefined; } /** * Represents the dimensions of a terminal. */ export interface ITerminalDimensions { /** * The number of rows in the terminal. */ rows: number; /** * The number of columns in the terminal. */ cols: number; } } ================================================ FILE: addons/addon-fit/webpack.config.js ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ const path = require('path'); const addonName = 'FitAddon'; const mainFile = 'addon-fit.js'; module.exports = { 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', // Force usage of globalThis instead of global / self. (This is cross-env compatible) globalObject: 'globalThis', }, mode: 'production' }; ================================================ FILE: addons/addon-image/.gitignore ================================================ 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 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 ================================================ FILE: addons/addon-image/.npmignore ================================================ .github fixture overwrite bootstrap.sh tsconfig.json webpack.config.js test out-test out-esbuild out-esbuild-test inwasm-sdks inwasm-builds ================================================ FILE: addons/addon-image/LICENSE ================================================ 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. ================================================ FILE: addons/addon-image/README.md ================================================ ## @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/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 kittySupport: true, // enable Kitty graphics support kittySizeLimit: 20000000 // size limit of a single Kitty 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 folder from bootstrap.sh - fix peer dependency in package.json ================================================ FILE: addons/addon-image/fixture/endless.sh ================================================ #!/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\\" ================================================ FILE: addons/addon-image/fixture/gcrglf.sh ================================================ #!/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 ================================================ FILE: addons/addon-image/fixture/growing_rect.js ================================================ 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(); ================================================ FILE: addons/addon-image/fixture/iip/palette.iip ================================================ ]1337;File=inline=1;size=525;name=Li4vcGFsZXR0ZS5wbmc=:iVBORw0KGgoAAAANSUhEUgAAAoAAAABQCAYAAACJbMQlAAAABmJLR0QA/wD/AP+gvaeTAAABwklEQVR4nO3dwQnEMAwAQedIIem/yVwLBj0C3pm3uAO/Fj2ia631rh3P1lRu7vnof0+Z836zOe83m/N+sznvN5vzfrM57zeb+23+HAAAhxCAAAAxAhAAIEYAAgDECEAAgBgBCAAQIwABAGIEIABAjAAEAIi59z8tDQDACWwAAQBiBCAAQIwABACIEYAAADECEAAgRgACAMQIQACAGAEIABAjAAEAYlwCAQCIsQEEAIgRgAAAMQIQACBGAAIAxAhAAIAYAQgAECMAAQBiBCAAQIwABACIcQkEACDGBhAAIEYAAgDECEAAgBgBCAAQIwABAGIEIABAjAAEAIgRgAAAMQIQACDGJRAAgBgbQACAGAEIABAjAAEAYgQgAECMAAQAiBGAAAAxAhAAIEYAAgDECEAAgBiXQAAAYmwAAQBiBCAAQIwABACIEYAAADECEAAgRgACAMQIQACAGAEIABAjAAEAYlwCAQCIsQEEAIgRgAAAMQIQACBGAAIAxAhAAIAYAQgAECMAAQBiBCAAQIwABACIcQkEACDGBhAAIEYAAgDECEAAgBgBCAAQIwABAGIEIABAjAAEAIgRgAAAMQIQACDmDz4lC02AaqMRAAAAAElFTkSuQmCC ================================================ FILE: addons/addon-image/fixture/iip/spinfox.iip ================================================ ]1337;File=inline=1;size=613321;name=Li4vdGVzdGltYWdlcy9zcGluZm94LnBuZw==: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 ================================================ FILE: addons/addon-image/fixture/iip/w3c_gif.iip ================================================ ]1337;File=inline=1;size=1667;name=Li4vdGVzdGltYWdlcy93M2NfaG9tZV8yNTYuZ2lm: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 ================================================ FILE: addons/addon-image/fixture/iip/w3c_jpg.iip ================================================ ]1337;File=inline=1;size=2159;name=Li4vdGVzdGltYWdlcy93M2NfaG9tZV8yNTYuanBn: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 ================================================ FILE: addons/addon-image/fixture/iip/w3c_png.iip ================================================ ]1337;File=inline=1;size=1658;name=Li4vdGVzdGltYWdlcy93M2NfaG9tZV8yNTYucG5n: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 ================================================ FILE: addons/addon-image/fixture/inspect_palette.sh ================================================ #!/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 ================================================ FILE: addons/addon-image/fixture/overdraw.sh ================================================ #!/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' ================================================ FILE: addons/addon-image/fixture/palette.sixel ================================================ P0;0;q"1;1;640;80#0;2;0;0;0#1;2;0;13;0#2;2;0;25;0#3;2;0;38;0#4;2;0;50;0#5;2;0;63;0#6;2;0;75;0#7;2;0;88;0#8;2;13;0;0#9;2;13;13;0#10;2;13;25;0#11;2;13;38;0#12;2;13;50;0#13;2;13;63;0#14;2;13;75;0#15;2;13;88;0#16;2;25;0;0#17;2;25;13;0#18;2;25;25;0#19;2;25;38;0#20;2;25;50;0#21;2;25;63;0#22;2;25;75;0#23;2;25;88;0#24;2;38;0;0#25;2;38;13;0#26;2;38;25;0#27;2;38;38;0#28;2;38;50;0#29;2;38;63;0#30;2;38;75;0#31;2;38;88;0#32;2;50;0;0#33;2;50;13;0#34;2;50;25;0#35;2;50;38;0#36;2;50;50;0#37;2;50;63;0#38;2;50;75;0#39;2;50;88;0#40;2;63;0;0#41;2;63;13;0#42;2;63;25;0#43;2;63;38;0#44;2;63;50;0#45;2;63;63;0#46;2;63;75;0#47;2;63;88;0#48;2;75;0;0#49;2;75;13;0#50;2;75;25;0#51;2;75;38;0#52;2;75;50;0#53;2;75;63;0#54;2;75;75;0#55;2;75;88;0#56;2;88;0;0#57;2;88;13;0#58;2;88;25;0#59;2;88;38;0#60;2;88;50;0#61;2;88;63;0#62;2;88;75;0#63;2;88;88;0#64;2;0;0;13#65;2;0;13;13#66;2;0;25;13#67;2;0;38;13#68;2;0;50;13#69;2;0;63;13#70;2;0;75;13#71;2;0;88;13#72;2;13;0;13#73;2;13;13;13#74;2;13;25;13#75;2;13;38;13#76;2;13;50;13#77;2;13;63;13#78;2;13;75;13#79;2;13;88;13#80;2;25;0;13#81;2;25;13;13#82;2;25;25;13#83;2;25;38;13#84;2;25;50;13#85;2;25;63;13#86;2;25;75;13#87;2;25;88;13#88;2;38;0;13#89;2;38;13;13#90;2;38;25;13#91;2;38;38;13#92;2;38;50;13#93;2;38;63;13#94;2;38;75;13#95;2;38;88;13#96;2;50;0;13#97;2;50;13;13#98;2;50;25;13#99;2;50;38;13#100;2;50;50;13#101;2;50;63;13#102;2;50;75;13#103;2;50;88;13#104;2;63;0;13#105;2;63;13;13#106;2;63;25;13#107;2;63;38;13#108;2;63;50;13#109;2;63;63;13#110;2;63;75;13#111;2;63;88;13#112;2;75;0;13#113;2;75;13;13#114;2;75;25;13#115;2;75;38;13#116;2;75;50;13#117;2;75;63;13#118;2;75;75;13#119;2;75;88;13#120;2;88;0;13#121;2;88;13;13#122;2;88;25;13#123;2;88;38;13#124;2;88;50;13#125;2;88;63;13#126;2;88;75;13#127;2;88;88;13#128;2;0;0;25#129;2;0;13;25#130;2;0;25;25#131;2;0;38;25#132;2;0;50;25#133;2;0;63;25#134;2;0;75;25#135;2;0;88;25#136;2;13;0;25#137;2;13;13;25#138;2;13;25;25#139;2;13;38;25#140;2;13;50;25#141;2;13;63;25#142;2;13;75;25#143;2;13;88;25#144;2;25;0;25#145;2;25;13;25#146;2;25;25;25#147;2;25;38;25#148;2;25;50;25#149;2;25;63;25#150;2;25;75;25#151;2;25;88;25#152;2;38;0;25#153;2;38;13;25#154;2;38;25;25#155;2;38;38;25#156;2;38;50;25#157;2;38;63;25#158;2;38;75;25#159;2;38;88;25#160;2;50;0;25#161;2;50;13;25#162;2;50;25;25#163;2;50;38;25#164;2;50;50;25#165;2;50;63;25#166;2;50;75;25#167;2;50;88;25#168;2;63;0;25#169;2;63;13;25#170;2;63;25;25#171;2;63;38;25#172;2;63;50;25#173;2;63;63;25#174;2;63;75;25#175;2;63;88;25#176;2;75;0;25#177;2;75;13;25#178;2;75;25;25#179;2;75;38;25#180;2;75;50;25#181;2;75;63;25#182;2;75;75;25#183;2;75;88;25#184;2;88;0;25#185;2;88;13;25#186;2;88;25;25#187;2;88;38;25#188;2;88;50;25#189;2;88;63;25#190;2;88;75;25#191;2;88;88;25#192;2;0;0;38#193;2;0;13;38#194;2;0;25;38#195;2;0;38;38#196;2;0;50;38#197;2;0;63;38#198;2;0;75;38#199;2;0;88;38#200;2;13;0;38#201;2;13;13;38#202;2;13;25;38#203;2;13;38;38#204;2;13;50;38#205;2;13;63;38#206;2;13;75;38#207;2;13;88;38#208;2;25;0;38#209;2;25;13;38#210;2;25;25;38#211;2;25;38;38#212;2;25;50;38#213;2;25;63;38#214;2;25;75;38#215;2;25;88;38#216;2;38;0;38#217;2;38;13;38#218;2;38;25;38#219;2;38;38;38#220;2;38;50;38#221;2;38;63;38#222;2;38;75;38#223;2;38;88;38#224;2;50;0;38#225;2;50;13;38#226;2;50;25;38#227;2;50;38;38#228;2;50;50;38#229;2;50;63;38#230;2;50;75;38#231;2;50;88;38#232;2;63;0;38#233;2;63;13;38#234;2;63;25;38#235;2;63;38;38#236;2;63;50;38#237;2;63;63;38#238;2;63;75;38#239;2;63;88;38#240;2;75;0;38#241;2;75;13;38#242;2;75;25;38#243;2;75;38;38#244;2;75;50;38#245;2;75;63;38#246;2;75;75;38#247;2;75;88;38#248;2;88;0;38#249;2;88;13;38#250;2;88;25;38#251;2;88;38;38#252;2;88;50;38#253;2;88;63;38#254;2;88;75;38#255;2;88;88;38#256;2;0;0;50#257;2;0;13;50#258;2;0;25;50#259;2;0;38;50#260;2;0;50;50#261;2;0;63;50#262;2;0;75;50#263;2;0;88;50#264;2;13;0;50#265;2;13;13;50#266;2;13;25;50#267;2;13;38;50#268;2;13;50;50#269;2;13;63;50#270;2;13;75;50#271;2;13;88;50#272;2;25;0;50#273;2;25;13;50#274;2;25;25;50#275;2;25;38;50#276;2;25;50;50#277;2;25;63;50#278;2;25;75;50#279;2;25;88;50#280;2;38;0;50#281;2;38;13;50#282;2;38;25;50#283;2;38;38;50#284;2;38;50;50#285;2;38;63;50#286;2;38;75;50#287;2;38;88;50#288;2;50;0;50#289;2;50;13;50#290;2;50;25;50#291;2;50;38;50#292;2;50;50;50#293;2;50;63;50#294;2;50;75;50#295;2;50;88;50#296;2;63;0;50#297;2;63;13;50#298;2;63;25;50#299;2;63;38;50#300;2;63;50;50#301;2;63;63;50#302;2;63;75;50#303;2;63;88;50#304;2;75;0;50#305;2;75;13;50#306;2;75;25;50#307;2;75;38;50#308;2;75;50;50#309;2;75;63;50#310;2;75;75;50#311;2;75;88;50#312;2;88;0;50#313;2;88;13;50#314;2;88;25;50#315;2;88;38;50#316;2;88;50;50#317;2;88;63;50#318;2;88;75;50#319;2;88;88;50#320;2;0;0;63#321;2;0;13;63#322;2;0;25;63#323;2;0;38;63#324;2;0;50;63#325;2;0;63;63#326;2;0;75;63#327;2;0;88;63#328;2;13;0;63#329;2;13;13;63#330;2;13;25;63#331;2;13;38;63#332;2;13;50;63#333;2;13;63;63#334;2;13;75;63#335;2;13;88;63#336;2;25;0;63#337;2;25;13;63#338;2;25;25;63#339;2;25;38;63#340;2;25;50;63#341;2;25;63;63#342;2;25;75;63#343;2;25;88;63#344;2;38;0;63#345;2;38;13;63#346;2;38;25;63#347;2;38;38;63#348;2;38;50;63#349;2;38;63;63#350;2;38;75;63#351;2;38;88;63#352;2;50;0;63#353;2;50;13;63#354;2;50;25;63#355;2;50;38;63#356;2;50;50;63#357;2;50;63;63#358;2;50;75;63#359;2;50;88;63#360;2;63;0;63#361;2;63;13;63#362;2;63;25;63#363;2;63;38;63#364;2;63;50;63#365;2;63;63;63#366;2;63;75;63#367;2;63;88;63#368;2;75;0;63#369;2;75;13;63#370;2;75;25;63#371;2;75;38;63#372;2;75;50;63#373;2;75;63;63#374;2;75;75;63#375;2;75;88;63#376;2;88;0;63#377;2;88;13;63#378;2;88;25;63#379;2;88;38;63#380;2;88;50;63#381;2;88;63;63#382;2;88;75;63#383;2;88;88;63#384;2;0;0;75#385;2;0;13;75#386;2;0;25;75#387;2;0;38;75#388;2;0;50;75#389;2;0;63;75#390;2;0;75;75#391;2;0;88;75#392;2;13;0;75#393;2;13;13;75#394;2;13;25;75#395;2;13;38;75#396;2;13;50;75#397;2;13;63;75#398;2;13;75;75#399;2;13;88;75#400;2;25;0;75#401;2;25;13;75#402;2;25;25;75#403;2;25;38;75#404;2;25;50;75#405;2;25;63;75#406;2;25;75;75#407;2;25;88;75#408;2;38;0;75#409;2;38;13;75#410;2;38;25;75#411;2;38;38;75#412;2;38;50;75#413;2;38;63;75#414;2;38;75;75#415;2;38;88;75#416;2;50;0;75#417;2;50;13;75#418;2;50;25;75#419;2;50;38;75#420;2;50;50;75#421;2;50;63;75#422;2;50;75;75#423;2;50;88;75#424;2;63;0;75#425;2;63;13;75#426;2;63;25;75#427;2;63;38;75#428;2;63;50;75#429;2;63;63;75#430;2;63;75;75#431;2;63;88;75#432;2;75;0;75#433;2;75;13;75#434;2;75;25;75#435;2;75;38;75#436;2;75;50;75#437;2;75;63;75#438;2;75;75;75#439;2;75;88;75#440;2;88;0;75#441;2;88;13;75#442;2;88;25;75#443;2;88;38;75#444;2;88;50;75#445;2;88;63;75#446;2;88;75;75#447;2;88;88;75#448;2;0;0;88#449;2;0;13;88#450;2;0;25;88#451;2;0;38;88#452;2;0;50;88#453;2;0;63;88#454;2;0;75;88#455;2;0;88;88#456;2;13;0;88#457;2;13;13;88#458;2;13;25;88#459;2;13;38;88#460;2;13;50;88#461;2;13;63;88#462;2;13;75;88#463;2;13;88;88#464;2;25;0;88#465;2;25;13;88#466;2;25;25;88#467;2;25;38;88#468;2;25;50;88#469;2;25;63;88#470;2;25;75;88#471;2;25;88;88#472;2;38;0;88#473;2;38;13;88#474;2;38;25;88#475;2;38;38;88#476;2;38;50;88#477;2;38;63;88#478;2;38;75;88#479;2;38;88;88#480;2;50;0;88#481;2;50;13;88#482;2;50;25;88#483;2;50;38;88#484;2;50;50;88#485;2;50;63;88#486;2;50;75;88#487;2;50;88;88#488;2;63;0;88#489;2;63;13;88#490;2;63;25;88#491;2;63;38;88#492;2;63;50;88#493;2;63;63;88#494;2;63;75;88#495;2;63;88;88#496;2;75;0;88#497;2;75;13;88#498;2;75;25;88#499;2;75;38;88#500;2;75;50;88#501;2;75;63;88#502;2;75;75;88#503;2;75;88;88#504;2;88;0;88#505;2;88;13;88#506;2;88;25;88#507;2;88;38;88#508;2;88;50;88#509;2;88;63;88#510;2;88;75;88#511;2;88;88;88#0!10~$#1!10?!10~$#2!20?!10~$#3!30?!10~$#4!40?!10~$#5!50?!10~$#6!60?!10~$#7!70?!10~$#8!80?!10~$#9!90?!10~$#10!100?!10~$#11!110?!10~$#12!120?!10~$#13!130?!10~$#14!140?!10~$#15!150?!10~$#16!160?!10~$#17!170?!10~$#18!180?!10~$#19!190?!10~$#20!200?!10~$#21!210?!10~$#22!220?!10~$#23!230?!10~$#24!240?!10~$#25!250?!10~$#26!260?!10~$#27!270?!10~$#28!280?!10~$#29!290?!10~$#30!300?!10~$#31!310?!10~$#32!320?!10~$#33!330?!10~$#34!340?!10~$#35!350?!10~$#36!360?!10~$#37!370?!10~$#38!380?!10~$#39!390?!10~$#40!400?!10~$#41!410?!10~$#42!420?!10~$#43!430?!10~$#44!440?!10~$#45!450?!10~$#46!460?!10~$#47!470?!10~$#48!480?!10~$#49!490?!10~$#50!500?!10~$#51!510?!10~$#52!520?!10~$#53!530?!10~$#54!540?!10~$#55!550?!10~$#56!560?!10~$#57!570?!10~$#58!580?!10~$#59!590?!10~$#60!600?!10~$#61!610?!10~$#62!620?!10~$#63!630?!10~$- #0!10N$#64!10o$#1!10?!10N$#65!10?!10o$#2!20?!10N$#66!20?!10o$#3!30?!10N$#67!30?!10o$#4!40?!10N$#68!40?!10o$#5!50?!10N$#69!50?!10o$#6!60?!10N$#70!60?!10o$#7!70?!10N$#71!70?!10o$#8!80?!10N$#72!80?!10o$#9!90?!10N$#73!90?!10o$#10!100?!10N$#74!100?!10o$#11!110?!10N$#75!110?!10o$#12!120?!10N$#76!120?!10o$#13!130?!10N$#77!130?!10o$#14!140?!10N$#78!140?!10o$#15!150?!10N$#79!150?!10o$#16!160?!10N$#80!160?!10o$#17!170?!10N$#81!170?!10o$#18!180?!10N$#82!180?!10o$#19!190?!10N$#83!190?!10o$#20!200?!10N$#84!200?!10o$#21!210?!10N$#85!210?!10o$#22!220?!10N$#86!220?!10o$#23!230?!10N$#87!230?!10o$#24!240?!10N$#88!240?!10o$#25!250?!10N$#89!250?!10o$#26!260?!10N$#90!260?!10o$#27!270?!10N$#91!270?!10o$#28!280?!10N$#92!280?!10o$#29!290?!10N$#93!290?!10o$#30!300?!10N$#94!300?!10o$#31!310?!10N$#95!310?!10o$#32!320?!10N$#96!320?!10o$#33!330?!10N$#97!330?!10o$#34!340?!10N$#98!340?!10o$#35!350?!10N$#99!350?!10o$#36!360?!10N$#100!360?!10o$#37!370?!10N$#101!370?!10o$#38!380?!10N$#102!380?!10o$#39!390?!10N$#103!390?!10o$#40!400?!10N$#104!400?!10o$#41!410?!10N$#105!410?!10o$#42!420?!10N$#106!420?!10o$#43!430?!10N$#107!430?!10o$#44!440?!10N$#108!440?!10o$#45!450?!10N$#109!450?!10o$#46!460?!10N$#110!460?!10o$#47!470?!10N$#111!470?!10o$#48!480?!10N$#112!480?!10o$#49!490?!10N$#113!490?!10o$#50!500?!10N$#114!500?!10o$#51!510?!10N$#115!510?!10o$#52!520?!10N$#116!520?!10o$#53!530?!10N$#117!530?!10o$#54!540?!10N$#118!540?!10o$#55!550?!10N$#119!550?!10o$#56!560?!10N$#120!560?!10o$#57!570?!10N$#121!570?!10o$#58!580?!10N$#122!580?!10o$#59!590?!10N$#123!590?!10o$#60!600?!10N$#124!600?!10o$#61!610?!10N$#125!610?!10o$#62!620?!10N$#126!620?!10o$#63!630?!10N$#127!630?!10o$- #64!10~$#65!10?!10~$#66!20?!10~$#67!30?!10~$#68!40?!10~$#69!50?!10~$#70!60?!10~$#71!70?!10~$#72!80?!10~$#73!90?!10~$#74!100?!10~$#75!110?!10~$#76!120?!10~$#77!130?!10~$#78!140?!10~$#79!150?!10~$#80!160?!10~$#81!170?!10~$#82!180?!10~$#83!190?!10~$#84!200?!10~$#85!210?!10~$#86!220?!10~$#87!230?!10~$#88!240?!10~$#89!250?!10~$#90!260?!10~$#91!270?!10~$#92!280?!10~$#93!290?!10~$#94!300?!10~$#95!310?!10~$#96!320?!10~$#97!330?!10~$#98!340?!10~$#99!350?!10~$#100!360?!10~$#101!370?!10~$#102!380?!10~$#103!390?!10~$#104!400?!10~$#105!410?!10~$#106!420?!10~$#107!430?!10~$#108!440?!10~$#109!450?!10~$#110!460?!10~$#111!470?!10~$#112!480?!10~$#113!490?!10~$#114!500?!10~$#115!510?!10~$#116!520?!10~$#117!530?!10~$#118!540?!10~$#119!550?!10~$#120!560?!10~$#121!570?!10~$#122!580?!10~$#123!590?!10~$#124!600?!10~$#125!610?!10~$#126!620?!10~$#127!630?!10~$- #64!10B$#128!10{$#65!10?!10B$#129!10?!10{$#66!20?!10B$#130!20?!10{$#67!30?!10B$#131!30?!10{$#68!40?!10B$#132!40?!10{$#69!50?!10B$#133!50?!10{$#70!60?!10B$#134!60?!10{$#71!70?!10B$#135!70?!10{$#72!80?!10B$#136!80?!10{$#73!90?!10B$#137!90?!10{$#74!100?!10B$#138!100?!10{$#75!110?!10B$#139!110?!10{$#76!120?!10B$#140!120?!10{$#77!130?!10B$#141!130?!10{$#78!140?!10B$#142!140?!10{$#79!150?!10B$#143!150?!10{$#80!160?!10B$#144!160?!10{$#81!170?!10B$#145!170?!10{$#82!180?!10B$#146!180?!10{$#83!190?!10B$#147!190?!10{$#84!200?!10B$#148!200?!10{$#85!210?!10B$#149!210?!10{$#86!220?!10B$#150!220?!10{$#87!230?!10B$#151!230?!10{$#88!240?!10B$#152!240?!10{$#89!250?!10B$#153!250?!10{$#90!260?!10B$#154!260?!10{$#91!270?!10B$#155!270?!10{$#92!280?!10B$#156!280?!10{$#93!290?!10B$#157!290?!10{$#94!300?!10B$#158!300?!10{$#95!310?!10B$#159!310?!10{$#96!320?!10B$#160!320?!10{$#97!330?!10B$#161!330?!10{$#98!340?!10B$#162!340?!10{$#99!350?!10B$#163!350?!10{$#100!360?!10B$#164!360?!10{$#101!370?!10B$#165!370?!10{$#102!380?!10B$#166!380?!10{$#103!390?!10B$#167!390?!10{$#104!400?!10B$#168!400?!10{$#105!410?!10B$#169!410?!10{$#106!420?!10B$#170!420?!10{$#107!430?!10B$#171!430?!10{$#108!440?!10B$#172!440?!10{$#109!450?!10B$#173!450?!10{$#110!460?!10B$#174!460?!10{$#111!470?!10B$#175!470?!10{$#112!480?!10B$#176!480?!10{$#113!490?!10B$#177!490?!10{$#114!500?!10B$#178!500?!10{$#115!510?!10B$#179!510?!10{$#116!520?!10B$#180!520?!10{$#117!530?!10B$#181!530?!10{$#118!540?!10B$#182!540?!10{$#119!550?!10B$#183!550?!10{$#120!560?!10B$#184!560?!10{$#121!570?!10B$#185!570?!10{$#122!580?!10B$#186!580?!10{$#123!590?!10B$#187!590?!10{$#124!600?!10B$#188!600?!10{$#125!610?!10B$#189!610?!10{$#126!620?!10B$#190!620?!10{$#127!630?!10B$#191!630?!10{$- #128!10~$#129!10?!10~$#130!20?!10~$#131!30?!10~$#132!40?!10~$#133!50?!10~$#134!60?!10~$#135!70?!10~$#136!80?!10~$#137!90?!10~$#138!100?!10~$#139!110?!10~$#140!120?!10~$#141!130?!10~$#142!140?!10~$#143!150?!10~$#144!160?!10~$#145!170?!10~$#146!180?!10~$#147!190?!10~$#148!200?!10~$#149!210?!10~$#150!220?!10~$#151!230?!10~$#152!240?!10~$#153!250?!10~$#154!260?!10~$#155!270?!10~$#156!280?!10~$#157!290?!10~$#158!300?!10~$#159!310?!10~$#160!320?!10~$#161!330?!10~$#162!340?!10~$#163!350?!10~$#164!360?!10~$#165!370?!10~$#166!380?!10~$#167!390?!10~$#168!400?!10~$#169!410?!10~$#170!420?!10~$#171!430?!10~$#172!440?!10~$#173!450?!10~$#174!460?!10~$#175!470?!10~$#176!480?!10~$#177!490?!10~$#178!500?!10~$#179!510?!10~$#180!520?!10~$#181!530?!10~$#182!540?!10~$#183!550?!10~$#184!560?!10~$#185!570?!10~$#186!580?!10~$#187!590?!10~$#188!600?!10~$#189!610?!10~$#190!620?!10~$#191!630?!10~$- #192!10~$#193!10?!10~$#194!20?!10~$#195!30?!10~$#196!40?!10~$#197!50?!10~$#198!60?!10~$#199!70?!10~$#200!80?!10~$#201!90?!10~$#202!100?!10~$#203!110?!10~$#204!120?!10~$#205!130?!10~$#206!140?!10~$#207!150?!10~$#208!160?!10~$#209!170?!10~$#210!180?!10~$#211!190?!10~$#212!200?!10~$#213!210?!10~$#214!220?!10~$#215!230?!10~$#216!240?!10~$#217!250?!10~$#218!260?!10~$#219!270?!10~$#220!280?!10~$#221!290?!10~$#222!300?!10~$#223!310?!10~$#224!320?!10~$#225!330?!10~$#226!340?!10~$#227!350?!10~$#228!360?!10~$#229!370?!10~$#230!380?!10~$#231!390?!10~$#232!400?!10~$#233!410?!10~$#234!420?!10~$#235!430?!10~$#236!440?!10~$#237!450?!10~$#238!460?!10~$#239!470?!10~$#240!480?!10~$#241!490?!10~$#242!500?!10~$#243!510?!10~$#244!520?!10~$#245!530?!10~$#246!540?!10~$#247!550?!10~$#248!560?!10~$#249!570?!10~$#250!580?!10~$#251!590?!10~$#252!600?!10~$#253!610?!10~$#254!620?!10~$#255!630?!10~$- #192!10N$#256!10o$#193!10?!10N$#257!10?!10o$#194!20?!10N$#258!20?!10o$#195!30?!10N$#259!30?!10o$#196!40?!10N$#260!40?!10o$#197!50?!10N$#261!50?!10o$#198!60?!10N$#262!60?!10o$#199!70?!10N$#263!70?!10o$#200!80?!10N$#264!80?!10o$#201!90?!10N$#265!90?!10o$#202!100?!10N$#266!100?!10o$#203!110?!10N$#267!110?!10o$#204!120?!10N$#268!120?!10o$#205!130?!10N$#269!130?!10o$#206!140?!10N$#270!140?!10o$#207!150?!10N$#271!150?!10o$#208!160?!10N$#272!160?!10o$#209!170?!10N$#273!170?!10o$#210!180?!10N$#274!180?!10o$#211!190?!10N$#275!190?!10o$#212!200?!10N$#276!200?!10o$#213!210?!10N$#277!210?!10o$#214!220?!10N$#278!220?!10o$#215!230?!10N$#279!230?!10o$#216!240?!10N$#280!240?!10o$#217!250?!10N$#281!250?!10o$#218!260?!10N$#282!260?!10o$#219!270?!10N$#283!270?!10o$#220!280?!10N$#284!280?!10o$#221!290?!10N$#285!290?!10o$#222!300?!10N$#286!300?!10o$#223!310?!10N$#287!310?!10o$#224!320?!10N$#288!320?!10o$#225!330?!10N$#289!330?!10o$#226!340?!10N$#290!340?!10o$#227!350?!10N$#291!350?!10o$#228!360?!10N$#292!360?!10o$#229!370?!10N$#293!370?!10o$#230!380?!10N$#294!380?!10o$#231!390?!10N$#295!390?!10o$#232!400?!10N$#296!400?!10o$#233!410?!10N$#297!410?!10o$#234!420?!10N$#298!420?!10o$#235!430?!10N$#299!430?!10o$#236!440?!10N$#300!440?!10o$#237!450?!10N$#301!450?!10o$#238!460?!10N$#302!460?!10o$#239!470?!10N$#303!470?!10o$#240!480?!10N$#304!480?!10o$#241!490?!10N$#305!490?!10o$#242!500?!10N$#306!500?!10o$#243!510?!10N$#307!510?!10o$#244!520?!10N$#308!520?!10o$#245!530?!10N$#309!530?!10o$#246!540?!10N$#310!540?!10o$#247!550?!10N$#311!550?!10o$#248!560?!10N$#312!560?!10o$#249!570?!10N$#313!570?!10o$#250!580?!10N$#314!580?!10o$#251!590?!10N$#315!590?!10o$#252!600?!10N$#316!600?!10o$#253!610?!10N$#317!610?!10o$#254!620?!10N$#318!620?!10o$#255!630?!10N$#319!630?!10o$- #256!10~$#257!10?!10~$#258!20?!10~$#259!30?!10~$#260!40?!10~$#261!50?!10~$#262!60?!10~$#263!70?!10~$#264!80?!10~$#265!90?!10~$#266!100?!10~$#267!110?!10~$#268!120?!10~$#269!130?!10~$#270!140?!10~$#271!150?!10~$#272!160?!10~$#273!170?!10~$#274!180?!10~$#275!190?!10~$#276!200?!10~$#277!210?!10~$#278!220?!10~$#279!230?!10~$#280!240?!10~$#281!250?!10~$#282!260?!10~$#283!270?!10~$#284!280?!10~$#285!290?!10~$#286!300?!10~$#287!310?!10~$#288!320?!10~$#289!330?!10~$#290!340?!10~$#291!350?!10~$#292!360?!10~$#293!370?!10~$#294!380?!10~$#295!390?!10~$#296!400?!10~$#297!410?!10~$#298!420?!10~$#299!430?!10~$#300!440?!10~$#301!450?!10~$#302!460?!10~$#303!470?!10~$#304!480?!10~$#305!490?!10~$#306!500?!10~$#307!510?!10~$#308!520?!10~$#309!530?!10~$#310!540?!10~$#311!550?!10~$#312!560?!10~$#313!570?!10~$#314!580?!10~$#315!590?!10~$#316!600?!10~$#317!610?!10~$#318!620?!10~$#319!630?!10~$- #256!10B$#320!10{$#257!10?!10B$#321!10?!10{$#258!20?!10B$#322!20?!10{$#259!30?!10B$#323!30?!10{$#260!40?!10B$#324!40?!10{$#261!50?!10B$#325!50?!10{$#262!60?!10B$#326!60?!10{$#263!70?!10B$#327!70?!10{$#264!80?!10B$#328!80?!10{$#265!90?!10B$#329!90?!10{$#266!100?!10B$#330!100?!10{$#267!110?!10B$#331!110?!10{$#268!120?!10B$#332!120?!10{$#269!130?!10B$#333!130?!10{$#270!140?!10B$#334!140?!10{$#271!150?!10B$#335!150?!10{$#272!160?!10B$#336!160?!10{$#273!170?!10B$#337!170?!10{$#274!180?!10B$#338!180?!10{$#275!190?!10B$#339!190?!10{$#276!200?!10B$#340!200?!10{$#277!210?!10B$#341!210?!10{$#278!220?!10B$#342!220?!10{$#279!230?!10B$#343!230?!10{$#280!240?!10B$#344!240?!10{$#281!250?!10B$#345!250?!10{$#282!260?!10B$#346!260?!10{$#283!270?!10B$#347!270?!10{$#284!280?!10B$#348!280?!10{$#285!290?!10B$#349!290?!10{$#286!300?!10B$#350!300?!10{$#287!310?!10B$#351!310?!10{$#288!320?!10B$#352!320?!10{$#289!330?!10B$#353!330?!10{$#290!340?!10B$#354!340?!10{$#291!350?!10B$#355!350?!10{$#292!360?!10B$#356!360?!10{$#293!370?!10B$#357!370?!10{$#294!380?!10B$#358!380?!10{$#295!390?!10B$#359!390?!10{$#296!400?!10B$#360!400?!10{$#297!410?!10B$#361!410?!10{$#298!420?!10B$#362!420?!10{$#299!430?!10B$#363!430?!10{$#300!440?!10B$#364!440?!10{$#301!450?!10B$#365!450?!10{$#302!460?!10B$#366!460?!10{$#303!470?!10B$#367!470?!10{$#304!480?!10B$#368!480?!10{$#305!490?!10B$#369!490?!10{$#306!500?!10B$#370!500?!10{$#307!510?!10B$#371!510?!10{$#308!520?!10B$#372!520?!10{$#309!530?!10B$#373!530?!10{$#310!540?!10B$#374!540?!10{$#311!550?!10B$#375!550?!10{$#312!560?!10B$#376!560?!10{$#313!570?!10B$#377!570?!10{$#314!580?!10B$#378!580?!10{$#315!590?!10B$#379!590?!10{$#316!600?!10B$#380!600?!10{$#317!610?!10B$#381!610?!10{$#318!620?!10B$#382!620?!10{$#319!630?!10B$#383!630?!10{$- #320!10~$#321!10?!10~$#322!20?!10~$#323!30?!10~$#324!40?!10~$#325!50?!10~$#326!60?!10~$#327!70?!10~$#328!80?!10~$#329!90?!10~$#330!100?!10~$#331!110?!10~$#332!120?!10~$#333!130?!10~$#334!140?!10~$#335!150?!10~$#336!160?!10~$#337!170?!10~$#338!180?!10~$#339!190?!10~$#340!200?!10~$#341!210?!10~$#342!220?!10~$#343!230?!10~$#344!240?!10~$#345!250?!10~$#346!260?!10~$#347!270?!10~$#348!280?!10~$#349!290?!10~$#350!300?!10~$#351!310?!10~$#352!320?!10~$#353!330?!10~$#354!340?!10~$#355!350?!10~$#356!360?!10~$#357!370?!10~$#358!380?!10~$#359!390?!10~$#360!400?!10~$#361!410?!10~$#362!420?!10~$#363!430?!10~$#364!440?!10~$#365!450?!10~$#366!460?!10~$#367!470?!10~$#368!480?!10~$#369!490?!10~$#370!500?!10~$#371!510?!10~$#372!520?!10~$#373!530?!10~$#374!540?!10~$#375!550?!10~$#376!560?!10~$#377!570?!10~$#378!580?!10~$#379!590?!10~$#380!600?!10~$#381!610?!10~$#382!620?!10~$#383!630?!10~$- #384!10~$#385!10?!10~$#386!20?!10~$#387!30?!10~$#388!40?!10~$#389!50?!10~$#390!60?!10~$#391!70?!10~$#392!80?!10~$#393!90?!10~$#394!100?!10~$#395!110?!10~$#396!120?!10~$#397!130?!10~$#398!140?!10~$#399!150?!10~$#400!160?!10~$#401!170?!10~$#402!180?!10~$#403!190?!10~$#404!200?!10~$#405!210?!10~$#406!220?!10~$#407!230?!10~$#408!240?!10~$#409!250?!10~$#410!260?!10~$#411!270?!10~$#412!280?!10~$#413!290?!10~$#414!300?!10~$#415!310?!10~$#416!320?!10~$#417!330?!10~$#418!340?!10~$#419!350?!10~$#420!360?!10~$#421!370?!10~$#422!380?!10~$#423!390?!10~$#424!400?!10~$#425!410?!10~$#426!420?!10~$#427!430?!10~$#428!440?!10~$#429!450?!10~$#430!460?!10~$#431!470?!10~$#432!480?!10~$#433!490?!10~$#434!500?!10~$#435!510?!10~$#436!520?!10~$#437!530?!10~$#438!540?!10~$#439!550?!10~$#440!560?!10~$#441!570?!10~$#442!580?!10~$#443!590?!10~$#444!600?!10~$#445!610?!10~$#446!620?!10~$#447!630?!10~$- #384!10N$#448!10o$#385!10?!10N$#449!10?!10o$#386!20?!10N$#450!20?!10o$#387!30?!10N$#451!30?!10o$#388!40?!10N$#452!40?!10o$#389!50?!10N$#453!50?!10o$#390!60?!10N$#454!60?!10o$#391!70?!10N$#455!70?!10o$#392!80?!10N$#456!80?!10o$#393!90?!10N$#457!90?!10o$#394!100?!10N$#458!100?!10o$#395!110?!10N$#459!110?!10o$#396!120?!10N$#460!120?!10o$#397!130?!10N$#461!130?!10o$#398!140?!10N$#462!140?!10o$#399!150?!10N$#463!150?!10o$#400!160?!10N$#464!160?!10o$#401!170?!10N$#465!170?!10o$#402!180?!10N$#466!180?!10o$#403!190?!10N$#467!190?!10o$#404!200?!10N$#468!200?!10o$#405!210?!10N$#469!210?!10o$#406!220?!10N$#470!220?!10o$#407!230?!10N$#471!230?!10o$#408!240?!10N$#472!240?!10o$#409!250?!10N$#473!250?!10o$#410!260?!10N$#474!260?!10o$#411!270?!10N$#475!270?!10o$#412!280?!10N$#476!280?!10o$#413!290?!10N$#477!290?!10o$#414!300?!10N$#478!300?!10o$#415!310?!10N$#479!310?!10o$#416!320?!10N$#480!320?!10o$#417!330?!10N$#481!330?!10o$#418!340?!10N$#482!340?!10o$#419!350?!10N$#483!350?!10o$#420!360?!10N$#484!360?!10o$#421!370?!10N$#485!370?!10o$#422!380?!10N$#486!380?!10o$#423!390?!10N$#487!390?!10o$#424!400?!10N$#488!400?!10o$#425!410?!10N$#489!410?!10o$#426!420?!10N$#490!420?!10o$#427!430?!10N$#491!430?!10o$#428!440?!10N$#492!440?!10o$#429!450?!10N$#493!450?!10o$#430!460?!10N$#494!460?!10o$#431!470?!10N$#495!470?!10o$#432!480?!10N$#496!480?!10o$#433!490?!10N$#497!490?!10o$#434!500?!10N$#498!500?!10o$#435!510?!10N$#499!510?!10o$#436!520?!10N$#500!520?!10o$#437!530?!10N$#501!530?!10o$#438!540?!10N$#502!540?!10o$#439!550?!10N$#503!550?!10o$#440!560?!10N$#504!560?!10o$#441!570?!10N$#505!570?!10o$#442!580?!10N$#506!580?!10o$#443!590?!10N$#507!590?!10o$#444!600?!10N$#508!600?!10o$#445!610?!10N$#509!610?!10o$#446!620?!10N$#510!620?!10o$#447!630?!10N$#511!630?!10o$- #448!10~$#449!10?!10~$#450!20?!10~$#451!30?!10~$#452!40?!10~$#453!50?!10~$#454!60?!10~$#455!70?!10~$#456!80?!10~$#457!90?!10~$#458!100?!10~$#459!110?!10~$#460!120?!10~$#461!130?!10~$#462!140?!10~$#463!150?!10~$#464!160?!10~$#465!170?!10~$#466!180?!10~$#467!190?!10~$#468!200?!10~$#469!210?!10~$#470!220?!10~$#471!230?!10~$#472!240?!10~$#473!250?!10~$#474!260?!10~$#475!270?!10~$#476!280?!10~$#477!290?!10~$#478!300?!10~$#479!310?!10~$#480!320?!10~$#481!330?!10~$#482!340?!10~$#483!350?!10~$#484!360?!10~$#485!370?!10~$#486!380?!10~$#487!390?!10~$#488!400?!10~$#489!410?!10~$#490!420?!10~$#491!430?!10~$#492!440?!10~$#493!450?!10~$#494!460?!10~$#495!470?!10~$#496!480?!10~$#497!490?!10~$#498!500?!10~$#499!510?!10~$#500!520?!10~$#501!530?!10~$#502!540?!10~$#503!550?!10~$#504!560?!10~$#505!570?!10~$#506!580?!10~$#507!590?!10~$#508!600?!10~$#509!610?!10~$#510!620?!10~$#511!630?!10~$- #448!10B$#449!10?!10B$#450!20?!10B$#451!30?!10B$#452!40?!10B$#453!50?!10B$#454!60?!10B$#455!70?!10B$#456!80?!10B$#457!90?!10B$#458!100?!10B$#459!110?!10B$#460!120?!10B$#461!130?!10B$#462!140?!10B$#463!150?!10B$#464!160?!10B$#465!170?!10B$#466!180?!10B$#467!190?!10B$#468!200?!10B$#469!210?!10B$#470!220?!10B$#471!230?!10B$#472!240?!10B$#473!250?!10B$#474!260?!10B$#475!270?!10B$#476!280?!10B$#477!290?!10B$#478!300?!10B$#479!310?!10B$#480!320?!10B$#481!330?!10B$#482!340?!10B$#483!350?!10B$#484!360?!10B$#485!370?!10B$#486!380?!10B$#487!390?!10B$#488!400?!10B$#489!410?!10B$#490!420?!10B$#491!430?!10B$#492!440?!10B$#493!450?!10B$#494!460?!10B$#495!470?!10B$#496!480?!10B$#497!490?!10B$#498!500?!10B$#499!510?!10B$#500!520?!10B$#501!530?!10B$#502!540?!10B$#503!550?!10B$#504!560?!10B$#505!570?!10B$#506!580?!10B$#507!590?!10B$#508!600?!10B$#509!610?!10B$#510!620?!10B$#511!630?!10B$\ ================================================ FILE: addons/addon-image/fixture/textcursor.sh ================================================ #!/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 ================================================ FILE: addons/addon-image/package.json ================================================ { "name": "@xterm/addon-image", "version": "0.9.0", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, "main": "lib/addon-image.js", "module": "lib/addon-image.mjs", "types": "typings/addon-image.d.ts", "repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-image", "license": "MIT", "keywords": [ "terminal", "image", "sixel", "xterm", "xterm.js" ], "scripts": { "prepackage": "../../node_modules/.bin/tsgo -p .", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", "start": "node ../../demo/start" }, "devDependencies": { "sixel": "^0.16.0", "xterm-wasm-parts": "^0.3.0" } } ================================================ FILE: addons/addon-image/src/IIPHandler.ts ================================================ /** * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT */ import { IImageAddonOptions, IOscHandler, IResetHandler, ITerminalExt } from './Types'; import { ImageRenderer } from './ImageRenderer'; import { IIPImageStorage } from './IIPImageStorage'; import { 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'; // Local const enum mirror - esbuild can't inline const enums from external packages const enum DecoderConst { // Limit held memory in base64 decoder (encoded bytes). KEEP_DATA = 4194304, // Initial buffer allocation for the decoder. INITIAL_DATA = 1048576, // Local mirror of const enum (esbuild can't inline const enums from external packages) OK = 0 } // 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: Base64Decoder; private _metrics = UNSUPPORTED_TYPE; constructor( private readonly _opts: IImageAddonOptions, private readonly _renderer: ImageRenderer, private readonly _storage: IIPImageStorage, private readonly _coreTerminal: ITerminalExt ) { const maxEncodedBytes = Math.ceil(this._opts.iipSizeLimit * 4 / 3); const initialBytes = Math.min(DecoderConst.INITIAL_DATA, maxEncodedBytes); this._dec = new Base64Decoder(DecoderConst.KEEP_DATA, maxEncodedBytes, initialBytes); } 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.subarray(start, end)) as number) !== DecoderConst.OK) { 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(); if ((this._dec.put(data.subarray(dataPos, end)) as number) !== DecoderConst.OK) { this._dec.release(); this._aborted = true; } } } } public end(success: boolean): boolean | Promise { 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()) { if (cond = this._dec.data8.length === this._header.size) { 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; } // HACK: The types on Blob are too restrictive, this is a Uint8Array so the browser accepts it const blob = new Blob([this._dec.data8 as Uint8Array], { type: this._metrics.mime }); this._dec.release(); if (!window.createImageBitmap) { const url = URL.createObjectURL(blob); const img = new Image(); return new Promise(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; } } ================================================ FILE: addons/addon-image/src/IIPHeaderParser.test.ts ================================================ /** * 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]); }); }); ================================================ FILE: addons/addon-image/src/IIPHeaderParser.ts ================================================ /** * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT */ // eslint-disable-next-line declare const Buffer: any; export interface IHeaderFields { [key: string]: number | string | Uint32Array | null | undefined; // 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) => number | string} = { 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]: number | string | Uint32Array | null | undefined} = {}; 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 { return false; } return true; } return false; } } ================================================ FILE: addons/addon-image/src/IIPImageStorage.ts ================================================ /** * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT */ import { ImageStorage } from './ImageStorage'; /** * IIP (iTerm Image Protocol) specific image storage controller. * * Wraps the shared ImageStorage with IIP protocol semantics: * - Always uses scrolling mode (cursor advances with image) */ export class IIPImageStorage { constructor( private readonly _storage: ImageStorage ) {} /** * Add an IIP image to storage. * Always uses scrolling mode — cursor advances past the image. */ public addImage(img: HTMLCanvasElement | ImageBitmap): void { this._storage.addImage(img, true); } } ================================================ FILE: addons/addon-image/src/IIPMetrics.test.ts ================================================ /** * 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 }], ['w3c_home_noexif.jpg', { mime: 'image/jpeg', 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/addon-image/fixture/testimages/' + TEST_IMAGES[i][0]); assert.deepStrictEqual(imageType(imageData), TEST_IMAGES[i][1]); } }); }); ================================================ FILE: addons/addon-image/src/IIPMetrics.ts ================================================ /** * 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 if (d[0] === 0xFF && d[1] === 0xD8 && d[2] === 0xFF) { 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]; } } ================================================ FILE: addons/addon-image/src/ImageAddon.ts ================================================ /** * Copyright (c) 2020 The xterm.js authors. All rights reserved. * @license MIT */ import type { ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { ImageAddon as IImageApi } from '@xterm/addon-image'; import { Emitter, type IEvent } from 'common/Event'; import { IIPHandler } from './IIPHandler'; import { ImageRenderer } from './ImageRenderer'; import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage'; import { KittyGraphicsHandler } from './kitty/KittyGraphicsHandler'; import { KittyImageStorage } from './kitty/KittyImageStorage'; import { SixelHandler } from './SixelHandler'; import { SixelImageStorage } from './SixelImageStorage'; import { IIPImageStorage } from './IIPImageStorage'; 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, kittySupport: true, kittySizeLimit: 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, IImageApi { private _opts: IImageAddonOptions; private _defaultOpts: IImageAddonOptions; private _storage: ImageStorage | undefined; private _renderer: ImageRenderer | undefined; private _disposables: IDisposable[] = []; private _terminal: ITerminalExt | undefined; private _handlers: Map = new Map(); private readonly _onImageAdded = new Emitter(); public readonly onImageAdded: IEvent = this._onImageAdded.event; constructor(opts?: Partial) { 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(); this._onImageAdded.dispose(); } 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); this._storage.onImageAdded = () => this._onImageAdded.fire(); // 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 sixelStorage = new SixelImageStorage(this._storage!, this._opts, this._renderer!, terminal); const sixelHandler = new SixelHandler(this._opts, sixelStorage, terminal); this._handlers.set('sixel', sixelHandler); this._disposeLater( terminal._core._inputHandler._parser.registerDcsHandler({ final: 'q' }, sixelHandler) ); } // iTerm IIP handler if (this._opts.iipSupport) { const iipStorage = new IIPImageStorage(this._storage!); const iipHandler = new IIPHandler(this._opts, this._renderer!, iipStorage, terminal); this._handlers.set('iip', iipHandler); this._disposeLater( terminal._core._inputHandler._parser.registerOscHandler(1337, iipHandler) ); } // Kitty graphics handler if (this._opts.kittySupport) { const kittyStorage = new KittyImageStorage(this._storage!); const kittyHandler = new KittyGraphicsHandler(this._opts, this._renderer!, kittyStorage, terminal); this._handlers.set('kitty', kittyHandler); this._disposeLater( kittyStorage, kittyHandler, terminal._core._inputHandler._parser.registerApcHandler(0x47, kittyHandler) ); } } // 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; } } ================================================ FILE: addons/addon-image/src/ImageRenderer.ts ================================================ /** * Copyright (c) 2020 The xterm.js authors. All rights reserved. * @license MIT */ import { toRGBA8888 } from 'sixel/lib/Colors'; import { IDisposable } from '@xterm/xterm'; import { ICellSize, ImageLayer, 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 { /** @deprecated Kept for backward compat — points to top layer canvas. */ public get canvas(): HTMLCanvasElement | undefined { return this._layers.get('top')?.canvas; } private _layers = new Map(); 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 { 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(); this.removeLayerFromDom('bottom'); 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._layers.clear(); 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._terminal.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, layer?: ImageLayer): void { const y = start * (this.dimensions?.css.cell.height || 0); const w = this.dimensions?.css.canvas.width || 0; const h = (++end - start) * (this.dimensions?.css.cell.height || 0); if (!layer || layer === 'top') { this._layers.get('top')?.clearRect(0, y, w, h); } if (!layer || layer === 'bottom') { this._layers.get('bottom')?.clearRect(0, y, w, h); } } /** * Clear whole image canvas. */ public clearAll(layer?: ImageLayer): void { if (!layer || layer === 'top') { const ctx = this._layers.get('top'); ctx?.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); } if (!layer || layer === 'bottom') { const ctx = this._layers.get('bottom'); ctx?.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); } } /** * Draw neighboring tiles on the image layer canvas. */ public draw(imgSpec: IImageSpec, tileId: number, col: number, row: number, count: number = 1): void { const ctx = this._layers.get(imgSpec.layer); if (!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 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 { const ctx = this._layers.get('top'); if (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; 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 { const w = this.dimensions?.css.canvas.width || 0; const h = this.dimensions?.css.canvas.height || 0; for (const ctx of this._layers.values()) { if (ctx.canvas.width !== w || ctx.canvas.height !== h) { ctx.canvas.width = w; ctx.canvas.height = h; } } } /** * 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) => { for (const key of [...this._layers.keys()]) { this.removeLayerFromDom(key); } this._oldSetRenderer?.call(this._renderService, renderer); }; } public insertLayerToDom(layer: ImageLayer = 'top'): void { // make sure that the terminal is attached to a document and to DOM if (!this.document || !this._terminal._core.screenElement) { console.warn('image addon: cannot insert output canvas to DOM, missing document or screenElement'); return; } if (this._layers.has(layer)) { return; } const canvas = ImageRenderer.createCanvas( this.document, this.dimensions?.css.canvas.width || 0, this.dimensions?.css.canvas.height || 0 ); canvas.classList.add(`xterm-image-layer-${layer}`); const screenElement = this._terminal._core.screenElement; // Use isolation to create a stacking context without overriding z-index, // which would conflict with integrators (e.g. VS Code) that set their // own z-index on the screen element. screenElement.style.isolation = 'isolate'; if (layer === 'bottom') { // Use z-index:-1 so it paints behind non-positioned text elements. // The screen element needs to be a stacking context (via isolation) // to contain the negative z-index, otherwise it would go behind the // entire terminal. canvas.style.zIndex = '-1'; screenElement.insertBefore(canvas, screenElement.firstChild); } else { // Explicit z-index ensures the image canvas reliably stacks above // the text layer (DOM renderer rows). z-index: 0 is below the // selection overlay (z-index: 1). canvas.style.zIndex = '0'; screenElement.appendChild(canvas); } const ctx = canvas.getContext('2d', { alpha: true, desynchronized: true }); if (!ctx) { canvas.remove(); return; } this._layers.set(layer, ctx); this.clearAll(layer); } public removeLayerFromDom(layer: ImageLayer = 'top'): void { const ctx = this._layers.get(layer); if (ctx) { ctx.canvas.remove(); this._layers.delete(layer); } } public hasLayer(layer: ImageLayer): boolean { return this._layers.has(layer); } 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; } } ================================================ FILE: addons/addon-image/src/ImageStorage.ts ================================================ /** * Copyright (c) 2020 The xterm.js authors. All rights reserved. * @license MIT */ import { IDisposable } from '@xterm/xterm'; import { ImageRenderer } from './ImageRenderer'; import { ITerminalExt, IExtendedAttrsImage, IImageAddonOptions, IImageSpec, IBufferLineExt, BgFlags, Cell, Content, ICellSize, ExtFlags, Attributes, UnderlineStyle, ImageLayer } 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); } public get underlineVariantOffset(): number { const val = (this._ext & ExtFlags.VARIANT_OFFSET) >> 29; if (val < 0) { return val ^ 0xFFFFFFF8; } return val; } public set underlineVariantOffset(value: number) { this._ext &= ~ExtFlags.VARIANT_OFFSET; this._ext |= (value << 29) & ExtFlags.VARIANT_OFFSET; } 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 = 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 }; public onImageAdded: (() => void) | undefined; public onImageDeleted: ((storageId: number) => void) | undefined; constructor( private _terminal: ITerminalExt, private _renderer: ImageRenderer, private _opts: IImageAddonOptions ) { try { this.setLimit(this._opts.storageLimit); } catch (e: unknown) { if (e instanceof Error) { 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); if (!spec) return; this._images.delete(id); // FIXME: really ugly workaround to get bitmaps deallocated :( if (window.ImageBitmap && spec.orig instanceof ImageBitmap) { spec.orig.close(); } this.onImageDeleted?.(id); } /** * 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; } /** * Delete an image by its internal storage ID. * Used by protocols that support explicit deletion (e.g. Kitty a=d). */ public deleteImage(id: number): void { const spec = this._images.get(id); if (spec) { spec.marker?.dispose(); this._delImg(id); } } /** * Method to add an image to the storage. * @param img - The image to add (canvas or bitmap). * @param scrolling - When true, cursor advances with the image (lineFeed per row). * When false, image is placed at (0,0) and cursor is restored (DECSET 80 / sixel origin mode). * @param layer - Which canvas layer to render on ('top' or 'bottom'). * @param zIndex - Z-index for image layering within the same layer. * @returns The internal image ID assigned to the stored image. */ public addImage(img: HTMLCanvasElement | ImageBitmap, scrolling: boolean, layer: ImageLayer = 'top', zIndex: number = 0): number { // 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 (!scrolling) { 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 (scrolling) { 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 (scrolling) { 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, layer, zIndex }; // finally add the image this._images.set(imageId, imgSpec); this.onImageAdded?.(); return imageId; } /** * 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 { // Determine which layers have images let hasTopImages = false; let hasBottomImages = false; for (const spec of this._images.values()) { if (spec.layer === 'bottom') { hasBottomImages = true; } else { hasTopImages = true; } if (hasTopImages && hasBottomImages) break; } // Lazily insert layers that are needed if (hasTopImages && !this._renderer.hasLayer('top')) { this._renderer.insertLayerToDom('top'); if (!this._renderer.hasLayer('top')) return; } if (hasBottomImages && !this._renderer.hasLayer('bottom')) { this._renderer.insertLayerToDom('bottom'); } // 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.hasLayer('top')) { this._renderer.removeLayerFromDom('top'); } if (this._renderer.hasLayer('bottom')) { this._renderer.removeLayerFromDom('bottom'); } return; } // Remove layers no longer needed if (!hasTopImages && this._renderer.hasLayer('top')) { this._renderer.clearAll('top'); this._renderer.removeLayerFromDom('top'); } if (!hasBottomImages && this._renderer.hasLayer('bottom')) { this._renderer.clearAll('bottom'); this._renderer.removeLayerFromDom('bottom'); } // 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); // Collect draw calls so we can sort by z-index (lower z drawn first). const drawCalls: { imgSpec: IImageSpec, tileId: number, col: number, row: number, count: number }[] = []; const placeholderCalls: { col: number, row: number, count: number }[] = []; // walk all cells in viewport and collect tiles found // Note: We check _extendedAttrs directly (not just HAS_EXTENDED flag) // because text writes clear the BG flag but leave image tile data intact. // This lets top-layer images survive text overwrites (kitty C=1 behavior). 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) { let e: IExtendedAttrsImage; if (line.getBg(col) & BgFlags.HAS_EXTENDED) { e = line._extendedAttrs[col] ?? EMPTY_ATTRS; } else { const maybeImg = line._extendedAttrs[col] as IExtendedAttrsImage | undefined; if (!maybeImg || maybeImg.imageId === undefined || maybeImg.imageId === -1) { continue; } e = maybeImg; } 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 * Also check _extendedAttrs directly for cells where text cleared HAS_EXTENDED. */ while (++col < cols) { const nextE = line._extendedAttrs[col] as IExtendedAttrsImage | undefined; if (!nextE || nextE.imageId !== imageId || nextE.tileId !== startTile + count) { break; } e = nextE; count++; } col--; if (imgSpec) { if (imgSpec.actual) { drawCalls.push({ imgSpec, tileId: startTile, col: startCol, row, count }); } } else if (this._opts.showPlaceholder) { placeholderCalls.push({ col: startCol, row, count }); } this._fullyCleared = false; } } } // Sort by z-index so lower z draws first (higher z renders on top) drawCalls.sort((a, b) => a.imgSpec.zIndex - b.imgSpec.zIndex); // Draw placeholders first (lowest priority) for (const call of placeholderCalls) { this._renderer.drawPlaceholder(call.col, call.row, call.count); } // Draw images in z-index order for (const call of drawCalls) { this._renderer.draw(call.imgSpec, call.tileId, call.col, call.row, call.count); } } 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); } } } ================================================ FILE: addons/addon-image/src/SixelHandler.ts ================================================ /** * Copyright (c) 2020, 2023 The xterm.js authors. All rights reserved. * @license MIT */ import { SixelImageStorage } from './SixelImageStorage'; 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: SixelImageStorage, 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 { 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 as Uint8ClampedArray, 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; } ================================================ FILE: addons/addon-image/src/SixelImageStorage.ts ================================================ /** * Copyright (c) 2020 The xterm.js authors. All rights reserved. * @license MIT */ import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage'; import { IImageAddonOptions, ITerminalExt } from './Types'; import { ImageRenderer } from './ImageRenderer'; /** * Sixel-specific image storage controller. * * Wraps the shared ImageStorage with sixel protocol semantics: * - Cursor behavior governed by DECSET 80 (sixelScrolling option) * - advanceCursor for empty sixels carrying only height */ export class SixelImageStorage { constructor( private readonly _storage: ImageStorage, private readonly _opts: IImageAddonOptions, private readonly _renderer: ImageRenderer, private readonly _terminal: ITerminalExt ) {} /** * Add a sixel image to storage. * Cursor behavior depends on the sixelScrolling option (DECSET 80). */ public addImage(img: HTMLCanvasElement | ImageBitmap): void { this._storage.addImage(img, this._opts.sixelScrolling); } /** * 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(); } } } } ================================================ FILE: addons/addon-image/src/Types.ts ================================================ /** * Copyright (c) 2020 The xterm.js authors. All rights reserved. * @license MIT */ import { IDisposable, IMarker, Terminal } from '@xterm/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, IApcHandler, 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, IApcHandler, 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; kittySupport: boolean; kittySizeLimit: 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 type ImageLayer = 'top' | 'bottom'; export interface IImageSpec { orig: HTMLCanvasElement | ImageBitmap | undefined; origCellSize: ICellSize; actual: HTMLCanvasElement | ImageBitmap | undefined; actualCellSize: ICellSize; marker: IMarker | undefined; tileCount: number; bufferType: 'alternate' | 'normal'; layer: ImageLayer; zIndex: number; } ================================================ FILE: addons/addon-image/src/kitty/KittyGraphicsHandler.ts ================================================ /** * Copyright (c) 2026 The xterm.js authors. All rights reserved. * @license MIT */ import { IDisposable } from '@xterm/xterm'; import { IApcHandler, IImageAddonOptions, IResetHandler, ITerminalExt, ImageLayer } from '../Types'; import { ImageRenderer } from '../ImageRenderer'; import { CELL_SIZE_DEFAULT } from '../ImageStorage'; import { KittyImageStorage } from './KittyImageStorage'; import Base64Decoder, { type DecodeStatus } from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm'; import { KittyAction, KittyFormat, KittyCompression, IKittyCommand, IPendingTransmission, IKittyImageData, BYTES_PER_PIXEL_RGB, BYTES_PER_PIXEL_RGBA, ALPHA_OPAQUE, parseKittyCommand } from './KittyGraphicsTypes'; // Memory limit for base64 decoder (4MB, same as IIPHandler) const DECODER_KEEP_DATA = 4194304; const DECODER_INITIAL_DATA = 4194304; // 4MB // Local mirror of const enum (esbuild can't inline const enums from external packages) const DECODER_OK: DecodeStatus.OK = 0; // Maximum control data size const MAX_CONTROL_DATA_SIZE = 512; // Semicolon codepoint const SEMICOLON = 0x3B; // Kitty graphics protocol handler with streaming base64 decoding. export class KittyGraphicsHandler implements IApcHandler, IResetHandler, IDisposable { private _aborted = false; private _decodeError = false; private _activeDecoder: Base64Decoder | null = null; private readonly _maxEncodedBytes: number; private readonly _initialEncodedBytes: number; // Streaming related states // True while receiving control data (before semicolon). private _inControlData = true; // Buffer for control data. private _controlData = new Uint32Array(MAX_CONTROL_DATA_SIZE); private _controlLength = 0; // Pre-calculated encoded size limit private _encodedSizeLimit = 0; private _totalEncodedSize = 0; // Parsed command. These are the control data before semicolon. private _parsedCommand: IKittyCommand | null = null; // Storage related states private _pendingTransmissions: Map = new Map(); // Tracks the pending key of the most recently started chunked upload. // Per spec, subsequent chunks only need m= (and optionally q=), without i=. // When a chunk arrives with no i=, this key is used to find the pending upload. private _lastPendingKey: number | undefined; constructor( private readonly _opts: IImageAddonOptions, private readonly _renderer: ImageRenderer, private readonly _kittyStorage: KittyImageStorage, private readonly _coreTerminal: ITerminalExt ) { // Convert decoded size limit -> max encoded bytes. this._maxEncodedBytes = Math.ceil(this._opts.kittySizeLimit * 4 / 3); // ensure we preallocate more than configured limit while using 4mb initial size. this._initialEncodedBytes = Math.min(DECODER_INITIAL_DATA, this._maxEncodedBytes); } public reset(): void { this._cleanupAllPending(); if (this._activeDecoder) { this._activeDecoder.release(); this._activeDecoder = null; } this._kittyStorage.reset(); } public dispose(): void { this.reset(); } private _removePendingEntry(key: number): void { this._pendingTransmissions.delete(key); if (this._lastPendingKey === key) { this._lastPendingKey = undefined; } } private _cleanupAllPending(): void { for (const pending of this._pendingTransmissions.values()) { pending.decoder.release(); } this._pendingTransmissions.clear(); this._lastPendingKey = undefined; } public start(): void { this._aborted = false; this._decodeError = false; this._inControlData = true; this._controlLength = 0; this._parsedCommand = null; // Pre-calculate encoded limit once: base64 is 4 bytes encoded → 3 bytes decoded this._encodedSizeLimit = this._maxEncodedBytes; this._totalEncodedSize = 0; this._activeDecoder = null; } public put(data: Uint32Array, start: number, end: number): void { if (this._aborted) return; if (!this._inControlData) { this._streamPayload(data, start, end); } else { // Scan for semicolon let controlEnd = end; for (let i = start; i < end; i++) { if (data[i] === SEMICOLON) { this._inControlData = false; controlEnd = i; break; } } // Copy control data const copyLength = controlEnd - start; if (this._controlLength + copyLength > MAX_CONTROL_DATA_SIZE) { this._aborted = true; return; } this._controlData.set(data.subarray(start, controlEnd), this._controlLength); this._controlLength += copyLength; if (!this._inControlData) { // Found semicolon - parse control data early for validation this._parsedCommand = parseKittyCommand(this._parseControlDataString()); // Early validation: i+I conflict if (this._parsedCommand.id !== undefined && this._parsedCommand.imageNumber !== undefined) { this._sendResponse(this._parsedCommand.id, 'EINVAL:cannot specify both i and I keys', this._parsedCommand.quiet ?? 0); this._aborted = true; return; } // Delete action doesn't need payload - skip streaming if (this._parsedCommand.action === KittyAction.DELETE) { return; } // Stream remaining as payload const payloadStart = controlEnd + 1; if (payloadStart < end) { this._streamPayload(data, payloadStart, end); } } } } // Stream payload bytes into the base64 decoder. private _streamPayload(data: Uint32Array, start: number, end: number): void { if (this._aborted) return; // Check size limit (compare encoded bytes against pre-calculated limit) // Include cumulative size from pending transmission for multi-chunk images. // Per spec, subsequent chunks may omit i=, so fall back to _lastPendingKey. const pendingKey = this._parsedCommand?.id ?? this._lastPendingKey ?? 0; const pending = this._pendingTransmissions.get(pendingKey); const previousEncodedSize = pending?.totalEncodedSize ?? 0; this._totalEncodedSize += end - start; const cumulativeEncodedSize = previousEncodedSize + this._totalEncodedSize; if (cumulativeEncodedSize > this._encodedSizeLimit) { const decoderToRelease = this._activeDecoder ?? pending?.decoder; if (decoderToRelease) { decoderToRelease.release(); } this._activeDecoder = null; if (pending) { this._removePendingEntry(pendingKey); } this._aborted = true; return; } if (this._decodeError) return; if (pending?.decoder && !this._activeDecoder) { this._activeDecoder = pending.decoder; } if (!this._activeDecoder) { this._activeDecoder = new Base64Decoder(DECODER_KEEP_DATA, this._maxEncodedBytes, this._initialEncodedBytes); this._activeDecoder.init(); } if (this._activeDecoder.put(data.subarray(start, end)) !== DECODER_OK) { this._activeDecoder.release(); this._activeDecoder = null; this._decodeError = true; if (pending) { this._removePendingEntry(pendingKey); } } } public end(success: boolean): boolean | Promise { if (this._aborted || !success) { if (this._activeDecoder) { this._activeDecoder.release(); this._activeDecoder = null; } return true; } // No semicolon = no payload (delete, capability query) if (this._inControlData) { return this._handleNoPayloadCommand(); } // Use command parsed early in put() - i+I already validated there const cmd = this._parsedCommand!; // Delete action was handled by skipping payload - just execute if (cmd.action === KittyAction.DELETE) { return this._handleDelete(cmd); } // Per spec, subsequent chunks may omit i=, so fall back to _lastPendingKey. const pendingKey = cmd.id ?? this._lastPendingKey ?? 0; const isMoreComing = cmd.more === 1; const pending = this._pendingTransmissions.get(pendingKey); if (isMoreComing) { if (this._activeDecoder) { if (pending) { pending.totalEncodedSize += this._totalEncodedSize; pending.decodeError = pending.decodeError || this._decodeError; } else { this._pendingTransmissions.set(pendingKey, { cmd: { ...cmd }, decoder: this._activeDecoder, totalEncodedSize: this._totalEncodedSize, decodeError: this._decodeError }); } this._lastPendingKey = pendingKey; this._activeDecoder = null; } return true; } // Final chunk received — clear the last pending key if (pending) { this._lastPendingKey = undefined; } let decodeError = this._decodeError; let finalCmd = cmd; let decoder = this._activeDecoder; if (pending) { finalCmd = pending.cmd; decoder = pending.decoder; decodeError = decodeError || pending.decodeError; this._pendingTransmissions.delete(pendingKey); } let imageBytes = new Uint8Array(0); if (decoder) { if (decoder.end() !== DECODER_OK) { decodeError = true; } imageBytes = decoder.data8; } this._activeDecoder = null; // Handle command first — handlers create Blob/ImageData from imageBytes, // which copies the data. Only then is it safe to release the decoder's // wasm memory that imageBytes points into. const result = this._handleCommandWithBytesAndCmd(finalCmd, imageBytes, decodeError); if (decoder) { decoder.release(); } return result; } // Command handling private _parseControlDataString(): string { let str = ''; for (let i = 0; i < this._controlLength; i++) { str += String.fromCodePoint(this._controlData[i]); } return str; } private _handleNoPayloadCommand(): boolean | Promise { const cmd = parseKittyCommand(this._parseControlDataString()); // Per spec: specifying both i and I is an error if (cmd.id !== undefined && cmd.imageNumber !== undefined) { this._sendResponse(cmd.id, 'EINVAL:cannot specify both i and I keys', cmd.quiet ?? 0); return true; } const action = cmd.action ?? 't'; switch (action) { case KittyAction.DELETE: return this._handleDelete(cmd); case KittyAction.QUERY: this._sendResponse(cmd.id ?? 0, 'OK', cmd.quiet ?? 0); return true; case KittyAction.PLACEMENT: return this._handlePlacement(cmd); default: // TODO: Implement remaining actions when needed: // - a=f (frame): animation frame operations // - a=a (animation): animation control // - a=c (compose): compose images if (cmd.id !== undefined) { this._sendResponse(cmd.id, 'EINVAL:unsupported action', cmd.quiet ?? 0); } return true; } } private _handleCommandWithBytesAndCmd(cmd: IKittyCommand, bytes: Uint8Array, decodeError: boolean): boolean | Promise { const action = cmd.action ?? 't'; switch (action) { case KittyAction.TRANSMIT: { const result = this._handleTransmit(cmd, bytes, decodeError); // Only send response when _handleTransmit didn't already respond // (it handles unsupported transmission medium responses internally) if ((cmd.transmission ?? 'd') === 'd' && cmd.id !== undefined) { if (decodeError) { this._sendResponse(cmd.id, 'EINVAL:invalid base64 data', cmd.quiet ?? 0); } else if (bytes.length > 0) { this._sendResponse(cmd.id, 'OK', cmd.quiet ?? 0); } } return result; } case KittyAction.TRANSMIT_DISPLAY: return this._handleTransmitDisplay(cmd, bytes, decodeError); case KittyAction.QUERY: return this._handleQuery(cmd, bytes, decodeError); case KittyAction.PLACEMENT: // a=p ignores any payload — image data was already transmitted return this._handlePlacement(cmd); default: // TODO: Implement remaining actions when needed: // - a=f (frame): animation frame operations // - a=a (animation): animation control // - a=c (compose): compose images if (cmd.id !== undefined) { this._sendResponse(cmd.id, 'EINVAL:unsupported action', cmd.quiet ?? 0); } return true; } } private _handlePlacement(cmd: IKittyCommand): boolean | Promise { if (cmd.id === undefined) { return true; } const id = cmd.id; const image = this._kittyStorage.getImage(id); if (!image) { this._sendResponse(id, 'ENOENT:image not found', cmd.quiet ?? 0, cmd.placementId); return true; } const result = this._displayImage(image, cmd); return result.then(success => { this._sendResponse(id, success ? 'OK' : 'EINVAL:image rendering failed', cmd.quiet ?? 0, cmd.placementId); return true; }); } private _handleTransmit(cmd: IKittyCommand, bytes: Uint8Array, decodeError: boolean): boolean { // TODO: Support file-based transmission modes (t=f, t=t, t=s) // Currently only supports direct transmission (t=d, the default). // - t=f (file): Payload is base64-encoded file path. Terminal reads image from that path. // - t=t (temp file): Payload is base64-encoded path in temp directory. Terminal reads, deletes. // - t=s: Payload is base64-encoded POSIX shm name. Terminal reads from shared memory. // These modes require filesystem/IPC access not available in browsers. For Node.js/Electron: // 1. Check cmd.transmission (t key) before treating bytes as image data // 2. For t=f/t/s: decode bytes as UTF-8 string (the path/name), then read file contents // 3. For t=d: treat bytes as image data (current behavior) // When implementing, also update _handleQuery to accept these transmission mediums. const transmission = cmd.transmission ?? 'd'; if (transmission !== 'd') { if (cmd.id !== undefined) { this._sendResponse(cmd.id, 'EINVAL:unsupported transmission medium', cmd.quiet ?? 0); } return true; } if (decodeError || bytes.length === 0) return true; this._kittyStorage.storeImage(cmd.id, { data: new Blob([bytes as BlobPart]), width: cmd.width ?? 0, height: cmd.height ?? 0, format: (cmd.format ?? KittyFormat.RGBA) as 24 | 32 | 100, compression: cmd.compression ?? '' }); return true; } private _handleTransmitDisplay(cmd: IKittyCommand, bytes: Uint8Array, decodeError: boolean): boolean | Promise { if (decodeError) { if (cmd.id !== undefined) { this._sendResponse(cmd.id, 'EINVAL:invalid base64 data', cmd.quiet ?? 0); } return true; } this._handleTransmit(cmd, bytes, decodeError); const id = cmd.id ?? this._kittyStorage.lastImageId; const image = this._kittyStorage.getImage(id); if (image) { const result = this._displayImage(image, cmd); if (cmd.id !== undefined) { return result.then(success => { this._sendResponse(id, success ? 'OK' : 'EINVAL:image rendering failed', cmd.quiet ?? 0); return true; }); } return result.then(() => true); } return true; } private _handleQuery(cmd: IKittyCommand, bytes: Uint8Array, decodeError: boolean): boolean { const id = cmd.id ?? 0; const quiet = cmd.quiet ?? 0; // Per spec: reject unsupported transmission mediums (only t=d is supported atm) // TODO: When filesystem support is added (Node.js/Electron), update this to accept // t=f (file), t=t (temp file), and t=s (shared memory) and respond OK for queries. const transmission = cmd.transmission ?? 'd'; if (transmission !== 'd') { this._sendResponse(id, 'EINVAL:unsupported transmission medium', quiet); return true; } // Check decode error first (invalid base64) if (decodeError) { this._sendResponse(id, 'EINVAL:invalid base64 data', quiet); return true; } // Capability query (no payload) - just respond OK if (bytes.length === 0) { this._sendResponse(id, 'OK', quiet); return true; } const format = cmd.format ?? KittyFormat.RGBA; if (format === KittyFormat.PNG) { this._sendResponse(id, 'OK', quiet); } else { const width = cmd.width ?? 0; const height = cmd.height ?? 0; if (!width || !height) { this._sendResponse(id, 'EINVAL:width and height required for raw pixel data', quiet); return true; } const bytesPerPixel = format === KittyFormat.RGBA ? BYTES_PER_PIXEL_RGBA : BYTES_PER_PIXEL_RGB; const expectedBytes = width * height * bytesPerPixel; if (bytes.length < expectedBytes) { this._sendResponse(id, `EINVAL:insufficient pixel data`, quiet); return true; } this._sendResponse(id, 'OK', quiet); } return true; } private _handleDelete(cmd: IKittyCommand): boolean { // Per spec: default delete selector is 'a' (delete all visible placements) const selector = cmd.deleteSelector ?? 'a'; // TODO: Distinguish lowercase (delete placements only) from uppercase // (delete placements + free stored image data). Currently both variants // free everything since we don't separate stored data from placements. switch (selector) { case 'a': case 'A': this._cleanupAllPending(); this._kittyStorage.deleteAll(); break; case 'i': case 'I': // TODO: When placement id tracking is implemented (see TODO in // KittyImageStorage), d=i with p= should delete only that // specific placement, while d=i without p should delete all // placements for the image. if (cmd.id !== undefined) { const pending = this._pendingTransmissions.get(cmd.id); if (pending) { pending.decoder.release(); } this._removePendingEntry(cmd.id); this._kittyStorage.deleteById(cmd.id); } break; default: // Unsupported selectors (c, n, p, q, r, x, y, z, f) — ignore for now break; } return true; } private _sendResponse(id: number, message: string, quiet: number, placementId?: number): void { const isOk = message === 'OK'; if (isOk && quiet === 1) return; if (!isOk && quiet === 2) return; const pPart = placementId ? `,p=${placementId}` : ''; const response = `\x1b_Gi=${id}${pPart};${message}\x1b\\`; this._coreTerminal._core.coreService.triggerDataEvent(response); } // Image display private _displayImage(image: IKittyImageData, cmd: IKittyCommand): Promise { return this._decodeAndDisplay(image, cmd) .then(() => true) .catch(() => false); } private async _decodeAndDisplay(image: IKittyImageData, cmd: IKittyCommand): Promise { let bitmap: ImageBitmap | undefined = await this._createBitmap(image); try { const cropX = Math.max(0, cmd.x ?? 0); const cropY = Math.max(0, cmd.y ?? 0); const cropW = cmd.sourceWidth || (bitmap.width - cropX); const cropH = cmd.sourceHeight || (bitmap.height - cropY); const maxCropW = Math.max(0, bitmap.width - cropX); const maxCropH = Math.max(0, bitmap.height - cropY); const finalCropW = Math.max(0, Math.min(cropW, maxCropW)); const finalCropH = Math.max(0, Math.min(cropH, maxCropH)); if (finalCropW === 0 || finalCropH === 0) { throw new Error('invalid source rectangle'); } if (cropX !== 0 || cropY !== 0 || finalCropW !== bitmap.width || finalCropH !== bitmap.height) { const cropped = await createImageBitmap(bitmap, cropX, cropY, finalCropW, finalCropH); bitmap.close(); bitmap = cropped; } const cw = this._renderer.dimensions?.css.cell.width || CELL_SIZE_DEFAULT.width; const ch = this._renderer.dimensions?.css.cell.height || CELL_SIZE_DEFAULT.height; // Per spec: c/r default to image's natural cell dimensions. // If only one of c/r is specified, compute the other from image aspect ratio. let imgCols: number; let imgRows: number; if (cmd.columns !== undefined && cmd.rows !== undefined) { imgCols = cmd.columns; imgRows = cmd.rows; } else if (cmd.columns !== undefined) { imgCols = cmd.columns; imgRows = Math.max(1, Math.ceil((bitmap.height / bitmap.width) * (imgCols * cw) / ch)); } else if (cmd.rows !== undefined) { imgRows = cmd.rows; imgCols = Math.max(1, Math.ceil((bitmap.width / bitmap.height) * (imgRows * ch) / cw)); } else { imgCols = Math.ceil(bitmap.width / cw); imgRows = Math.ceil(bitmap.height / ch); } let w = bitmap.width; let h = bitmap.height; // Scale bitmap to fit placement rectangle when c/r are specified if (cmd.columns !== undefined || cmd.rows !== undefined) { w = Math.round(imgCols * cw); h = Math.round(imgRows * ch); } if (w * h > this._opts.pixelLimit) { throw new Error('image exceeds pixel limit'); } // Save cursor position before addImage modifies it const buffer = this._coreTerminal._core.buffer; const savedX = buffer.x; const savedY = buffer.y; const savedYbase = buffer.ybase; // Determine layer based on z-index: negative = behind text, 0+ = on top. // When z<0 we always use the bottom layer even without allowTransparency — // the image will simply be hidden behind the opaque text background, which // is the correct behavior (client asked for "behind text"). const wantsBottom = cmd.zIndex !== undefined && cmd.zIndex < 0; const layer: ImageLayer = wantsBottom ? 'bottom' : 'top'; if (w !== bitmap.width || h !== bitmap.height) { const scaled = await createImageBitmap(bitmap, { resizeWidth: w, resizeHeight: h }); bitmap.close(); bitmap = scaled; } // Per spec: X/Y are pixel offsets within the first cell, so clamp to cell dimensions const xOffset = Math.min(Math.max(0, cmd.xOffset ?? 0), cw - 1); const yOffset = Math.min(Math.max(0, cmd.yOffset ?? 0), ch - 1); if (xOffset !== 0 || yOffset !== 0) { // Per spec: X/Y is not added to c/r area. When c/r are explicit, the // total placement area remains c*cw × r*ch pixels and the offset image // is clipped to fit. When c/r are unset, the padded canvas determines // the natural cell dimensions. const canvasW = (cmd.columns !== undefined) ? Math.round(imgCols * cw) : bitmap.width + xOffset; const canvasH = (cmd.rows !== undefined) ? Math.round(imgRows * ch) : bitmap.height + yOffset; const offsetCanvas = ImageRenderer.createCanvas(window.document, canvasW, canvasH); const offsetCtx = offsetCanvas.getContext('2d'); if (!offsetCtx) { throw new Error('Failed to create offset canvas context'); } offsetCtx.drawImage(bitmap, xOffset, yOffset); const offsetBitmap = await createImageBitmap(offsetCanvas); offsetCanvas.width = offsetCanvas.height = 0; bitmap.close(); bitmap = offsetBitmap; w = bitmap.width; h = bitmap.height; if (w * h > this._opts.pixelLimit) { throw new Error('image exceeds pixel limit'); } if (cmd.columns === undefined) { imgCols = Math.ceil(bitmap.width / cw); } if (cmd.rows === undefined) { imgRows = Math.ceil(bitmap.height / ch); } } const zIndex = cmd.zIndex ?? 0; this._kittyStorage.addImage(image.id, bitmap, true, layer, zIndex); bitmap = undefined; // ownership transferred to storage // Kitty cursor movement // Per spec: cursor placed at first column after last image column, // on the last row of the image. C=1 means don't move cursor. if (cmd.cursorMovement === 1) { // C=1: restore cursor to position before image was placed const scrolled = buffer.ybase - savedYbase; buffer.x = savedX; // Can't restore cursor to scrollback? buffer.y = Math.max(savedY - scrolled, 0); } else { // Default (C=0): advance cursor horizontally past the image // addImage already positioned cursor on the last row via lineFeeds buffer.x = Math.min(savedX + imgCols, this._coreTerminal.cols); } } catch (e) { bitmap?.close(); throw e; } } // Create ImageBitmap from already-decoded image data. private async _createBitmap(image: IKittyImageData): Promise { let bytes: Uint8Array = new Uint8Array(await image.data.arrayBuffer()); if (image.compression === KittyCompression.ZLIB) { bytes = await this._decompressZlib(bytes); } if (image.format === KittyFormat.PNG) { const blob = new Blob([bytes as BlobPart], { type: 'image/png' }); if (!window.createImageBitmap) { const url = URL.createObjectURL(blob); const img = new Image(); return new Promise((resolve, reject) => { img.addEventListener('load', () => { URL.revokeObjectURL(url); const canvas = ImageRenderer.createCanvas(window.document, img.width, img.height); canvas.getContext('2d')?.drawImage(img, 0, 0); createImageBitmap(canvas).then(resolve).catch(reject); }); img.addEventListener('error', () => { URL.revokeObjectURL(url); reject(new Error('Failed to load image')); }); img.src = url; }); } return createImageBitmap(blob); } // Raw pixel data const width = image.width; const height = image.height; if (!width || !height) { throw new Error('Width and height required for raw pixel data'); } const bytesPerPixel = image.format === KittyFormat.RGBA ? BYTES_PER_PIXEL_RGBA : BYTES_PER_PIXEL_RGB; const expectedBytes = width * height * bytesPerPixel; if (bytes.length < expectedBytes) { throw new Error('Insufficient pixel data'); } const pixelCount = width * height; if (image.format === KittyFormat.RGBA) { // RGBA: use bytes directly — no copy needed return createImageBitmap(new ImageData(new Uint8ClampedArray(bytes.buffer as ArrayBuffer, bytes.byteOffset, pixelCount * BYTES_PER_PIXEL_RGBA), width, height)); } // RGB→RGBA: interleave alpha using uint32 block processing (4 pixels per iteration). // 3 uint32 reads + 4 uint32 writes per 4 pixels vs 28 byte reads/writes — ~6x faster. // Assumes little-endian (all modern browsers/Node.js). const data = new Uint8ClampedArray(pixelCount * BYTES_PER_PIXEL_RGBA); const src32 = new Uint32Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 4)); const dst32 = new Uint32Array(data.buffer); const alignedPixels = pixelCount & ~3; // round down to multiple of 4 let srcOffset = 0; let dstOffset = 0; for (let i = 0; i < alignedPixels; i += 4) { const b0 = src32[srcOffset++]; const b1 = src32[srcOffset++]; const b2 = src32[srcOffset++]; // Little-endian: pixel bytes are [R,G,B] → uint32 ABGR layout dst32[dstOffset++] = 0xFF000000 | b0; dst32[dstOffset++] = 0xFF000000 | (b0 >>> 24) | (b1 << 8); dst32[dstOffset++] = 0xFF000000 | (b1 >>> 16) | (b2 << 16); dst32[dstOffset++] = 0xFF000000 | (b2 >>> 8); } // Handle remaining 1–3 pixels let srcByte = alignedPixels * BYTES_PER_PIXEL_RGB; let dstByte = alignedPixels * BYTES_PER_PIXEL_RGBA; for (let i = alignedPixels; i < pixelCount; i++) { data[dstByte] = bytes[srcByte]; data[dstByte + 1] = bytes[srcByte + 1]; data[dstByte + 2] = bytes[srcByte + 2]; data[dstByte + 3] = ALPHA_OPAQUE; srcByte += BYTES_PER_PIXEL_RGB; dstByte += BYTES_PER_PIXEL_RGBA; } return createImageBitmap(new ImageData(data, width, height)); } private async _decompressZlib(compressed: Uint8Array): Promise { try { return await this._decompress(compressed, 'deflate'); } catch { return await this._decompress(compressed, 'deflate-raw'); } } private async _decompress(compressed: Uint8Array, format: 'deflate' | 'deflate-raw'): Promise { const ds = new DecompressionStream(format); const writer = ds.writable.getWriter(); writer.write(compressed as BufferSource); writer.close(); const chunks: Uint8Array[] = []; const reader = ds.readable.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; chunks.push(value); } const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0); const result = new Uint8Array(totalLength); let offset = 0; for (const chunk of chunks) { result.set(chunk, offset); offset += chunk.length; } return result; } public get images(): ReadonlyMap { return this._kittyStorage.images; } public get _kittyIdToStorageId(): ReadonlyMap { return this._kittyStorage.kittyIdToStorageId; } public get pendingTransmissions(): ReadonlyMap { return this._pendingTransmissions; } } ================================================ FILE: addons/addon-image/src/kitty/KittyGraphicsTypes.test.ts ================================================ /** * Copyright (c) 2026 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import { parseKittyCommand, KittyAction, KittyFormat } from './KittyGraphicsTypes'; describe('KittyGraphicsTypes', () => { describe('parseKittyCommand', () => { it('should parse control data with action and format', () => { const cmd = parseKittyCommand('a=T,f=100'); assert.strictEqual(cmd.action, 'T'); assert.strictEqual(cmd.format, 100); }); it('should parse control data with all options', () => { const cmd = parseKittyCommand('a=t,f=32,i=5,s=10,v=20,c=3,r=2,m=1,q=2'); assert.strictEqual(cmd.action, 't'); assert.strictEqual(cmd.format, 32); assert.strictEqual(cmd.id, 5); assert.strictEqual(cmd.width, 10); assert.strictEqual(cmd.height, 20); assert.strictEqual(cmd.columns, 3); assert.strictEqual(cmd.rows, 2); assert.strictEqual(cmd.more, 1); assert.strictEqual(cmd.quiet, 2); }); it('should handle empty control data', () => { const cmd = parseKittyCommand(''); assert.strictEqual(cmd.action, undefined); assert.strictEqual(cmd.format, undefined); }); it('should parse transmit action', () => { const cmd = parseKittyCommand('a=t,f=100'); assert.strictEqual(cmd.action, KittyAction.TRANSMIT); assert.strictEqual(cmd.format, KittyFormat.PNG); }); it('should parse delete action', () => { const cmd = parseKittyCommand('a=d,i=5'); assert.strictEqual(cmd.action, KittyAction.DELETE); assert.strictEqual(cmd.id, 5); }); it('should parse empty action as empty string', () => { const cmd = parseKittyCommand('a=,f=100'); assert.strictEqual(cmd.action, ''); assert.strictEqual(cmd.format, 100); }); it('should leave action undefined when key is not present', () => { const cmd = parseKittyCommand('f=100,i=5'); assert.strictEqual(cmd.action, undefined); assert.strictEqual(cmd.format, 100); assert.strictEqual(cmd.id, 5); }); it('should parse compression key', () => { const cmd = parseKittyCommand('a=t,f=32,o=z'); assert.strictEqual(cmd.action, 't'); assert.strictEqual(cmd.format, 32); assert.strictEqual(cmd.compression, 'z'); }); it('should parse cursor movement key', () => { const cmd = parseKittyCommand('a=T,f=100,C=1'); assert.strictEqual(cmd.cursorMovement, 1); }); it('should parse cursor movement key C=0', () => { const cmd = parseKittyCommand('a=T,f=100,C=0'); assert.strictEqual(cmd.cursorMovement, 0); }); it('should parse x and y offset', () => { const cmd = parseKittyCommand('a=T,x=10,y=20'); assert.strictEqual(cmd.x, 10); assert.strictEqual(cmd.y, 20); }); it('should handle keys without values', () => { const cmd = parseKittyCommand('a=t,f=,i=5'); assert.strictEqual(cmd.action, 't'); assert.ok(isNaN(cmd.format!)); assert.strictEqual(cmd.id, 5); }); it('should parse z-index key with positive value', () => { const cmd = parseKittyCommand('a=T,f=100,z=10'); assert.strictEqual(cmd.zIndex, 10); }); it('should parse z-index key with zero', () => { const cmd = parseKittyCommand('a=T,f=100,z=0'); assert.strictEqual(cmd.zIndex, 0); }); it('should parse z-index key with negative value', () => { const cmd = parseKittyCommand('a=T,f=100,z=-1'); assert.strictEqual(cmd.zIndex, -1); }); it('should leave zIndex undefined when not specified', () => { const cmd = parseKittyCommand('a=T,f=100'); assert.strictEqual(cmd.zIndex, undefined); }); it('should parse delete selector key', () => { const cmd = parseKittyCommand('a=d,d=i,i=5'); assert.strictEqual(cmd.action, 'd'); assert.strictEqual(cmd.deleteSelector, 'i'); assert.strictEqual(cmd.id, 5); }); it('should parse uppercase delete selector', () => { const cmd = parseKittyCommand('a=d,d=A'); assert.strictEqual(cmd.deleteSelector, 'A'); }); it('should parse delete selector d=a (all)', () => { const cmd = parseKittyCommand('a=d,d=a'); assert.strictEqual(cmd.deleteSelector, 'a'); }); it('should leave deleteSelector undefined when not specified', () => { const cmd = parseKittyCommand('a=d,i=5'); assert.strictEqual(cmd.deleteSelector, undefined); }); it('should parse placement id key', () => { const cmd = parseKittyCommand('a=d,d=i,i=5,p=3'); assert.strictEqual(cmd.placementId, 3); assert.strictEqual(cmd.deleteSelector, 'i'); assert.strictEqual(cmd.id, 5); }); it('should leave placementId undefined when not specified', () => { const cmd = parseKittyCommand('a=d,d=i,i=5'); assert.strictEqual(cmd.placementId, undefined); }); it('should parse image number key', () => { const cmd = parseKittyCommand('a=t,f=100,I=42'); assert.strictEqual(cmd.imageNumber, 42); }); }); }); ================================================ FILE: addons/addon-image/src/kitty/KittyGraphicsTypes.ts ================================================ /** * Copyright (c) 2026 The xterm.js authors. All rights reserved. * @license MIT * * Kitty graphics protocol types, constants, and parsing utilities. */ import type Base64Decoder from 'xterm-wasm-parts/lib/base64/Base64Decoder.wasm'; // Kitty graphics protocol action types. // See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'a'. export const enum KittyAction { TRANSMIT = 't', TRANSMIT_DISPLAY = 'T', QUERY = 'q', PLACEMENT = 'p', DELETE = 'd' } // Kitty graphics protocol format types. // See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference export const enum KittyFormat { RGB = 24, RGBA = 32, PNG = 100 } // Kitty graphics protocol compression types. // See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference under key 'o'. export const enum KittyCompression { NONE = '', ZLIB = 'z' } // Kitty graphics protocol control data keys. // See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#control-data-reference export const enum KittyKey { // Action to perform (t=transmit, T=transmit+display, q=query, p=placement, d=delete) ACTION = 'a', // Image format (24=RGB, 32=RGBA, 100=PNG) FORMAT = 'f', // Image ID for referencing stored images ID = 'i', // Image number (alternative to ID, terminal assigns ID) IMAGE_NUMBER = 'I', // Source image width in pixels WIDTH = 's', // Source image height in pixels HEIGHT = 'v', // The left edge (in pixels) of the image area to display X_OFFSET = 'x', // The top edge (in pixels) of the image area to display Y_OFFSET = 'y', // Width (in pixels) of the source rectangle to display SOURCE_WIDTH = 'w', // Height (in pixels) of the source rectangle to display SOURCE_HEIGHT = 'h', // Horizontal offset (in pixels) within the first cell X_PLACEMENT_OFFSET = 'X', // Vertical offset (in pixels) within the first cell Y_PLACEMENT_OFFSET = 'Y', // Number of terminal columns to display the image over COLUMNS = 'c', // Number of terminal rows to display the image over ROWS = 'r', // More data flag (1=more chunks coming, 0=final chunk) MORE = 'm', // Compression type (z=zlib). This is essential for chunking larger images. COMPRESSION = 'o', // Quiet mode (1=suppress OK responses, 2=suppress error responses) QUIET = 'q', // Cursor movement policy (0=move cursor after image, 1=don't move cursor) CURSOR_MOVEMENT = 'C', // Z-index for image layering (negative = behind text, 0+ = on top) Z_INDEX = 'z', // Transmission medium (d=direct, f=file, t=temp file, s=shared memory) TRANSMISSION = 't', // Delete selector (a/A=all, i/I=by id, c/C=at cursor, etc.) — only used when a=d DELETE_SELECTOR = 'd', // Placement ID for targeting specific placements PLACEMENT_ID = 'p' } // Pixel format constants export const BYTES_PER_PIXEL_RGB = 3; export const BYTES_PER_PIXEL_RGBA = 4; export const ALPHA_OPAQUE = 255; // Parsed Kitty graphics command. export interface IKittyCommand { action?: string; format?: number; id?: number; imageNumber?: number; width?: number; height?: number; x?: number; y?: number; sourceWidth?: number; sourceHeight?: number; xOffset?: number; yOffset?: number; columns?: number; rows?: number; more?: number; quiet?: number; cursorMovement?: number; zIndex?: number; transmission?: string; deleteSelector?: string; placementId?: number; compression?: string; payload?: string; } // Pending chunked transmission state. // Stores metadata from the first chunk while accumulating decoded payload data. export interface IPendingTransmission { // The parsed command from the first chunk (contains action, format, dimensions, etc.) cmd: IKittyCommand; // Decoder used across chunked payloads decoder: Base64Decoder; // Total encoded (base64) bytes received across all chunks - for size limit enforcement totalEncodedSize: number; // Whether any chunk has failed to decode decodeError: boolean; } // Stored Kitty image data. export interface IKittyImageData { id: number; // Decoded image data stored as Blob (off JS heap) to avoid 2GB heap limit data: Blob; width: number; height: number; format: 24 | 32 | 100; compression?: string; } // Parses Kitty graphics control data into a command object. export function parseKittyCommand(data: string): IKittyCommand { const cmd: IKittyCommand = {}; const parts = data.split(','); for (const part of parts) { const eqIdx = part.indexOf('='); if (eqIdx === -1) continue; const key = part.substring(0, eqIdx); const value = part.substring(eqIdx + 1); // Handle string keys first if (key === KittyKey.ACTION) { cmd.action = value; continue; } if (key === KittyKey.COMPRESSION) { cmd.compression = value; continue; } if (key === KittyKey.TRANSMISSION) { cmd.transmission = value; continue; } if (key === KittyKey.DELETE_SELECTOR) { cmd.deleteSelector = value; continue; } const numValue = parseInt(value); switch (key) { case KittyKey.FORMAT: cmd.format = numValue; break; case KittyKey.ID: cmd.id = numValue; break; case KittyKey.IMAGE_NUMBER: cmd.imageNumber = numValue; break; case KittyKey.WIDTH: cmd.width = numValue; break; case KittyKey.HEIGHT: cmd.height = numValue; break; case KittyKey.X_OFFSET: cmd.x = numValue; break; case KittyKey.Y_OFFSET: cmd.y = numValue; break; case KittyKey.SOURCE_WIDTH: cmd.sourceWidth = numValue; break; case KittyKey.SOURCE_HEIGHT: cmd.sourceHeight = numValue; break; case KittyKey.X_PLACEMENT_OFFSET: cmd.xOffset = numValue; break; case KittyKey.Y_PLACEMENT_OFFSET: cmd.yOffset = numValue; break; case KittyKey.COLUMNS: cmd.columns = numValue; break; case KittyKey.ROWS: cmd.rows = numValue; break; case KittyKey.MORE: cmd.more = numValue; break; case KittyKey.QUIET: cmd.quiet = numValue; break; case KittyKey.CURSOR_MOVEMENT: cmd.cursorMovement = numValue; break; case KittyKey.Z_INDEX: cmd.zIndex = numValue; break; case KittyKey.PLACEMENT_ID: cmd.placementId = numValue; break; } } return cmd; } ================================================ FILE: addons/addon-image/src/kitty/KittyImageStorage.ts ================================================ /** * Copyright (c) 2026 The xterm.js authors. All rights reserved. * @license MIT */ import { IDisposable } from '@xterm/xterm'; import { ImageStorage } from '../ImageStorage'; import { ImageLayer } from '../Types'; import { IKittyImageData } from './KittyGraphicsTypes'; // Kitty-specific image storage controller. // // Wraps shared ImageStorage with kitty protocol semantics: // - tracks transmitted image payloads by kitty image id // - tracks kitty image id -> shared ImageStorage id mapping for displayed images // - mirrors shared-storage evictions into kitty maps // - applies protocol-level undisplayed-image eviction policy export class KittyImageStorage implements IDisposable { private static readonly _maxStoredImages = 256; private _nextImageId = 1; private readonly _images: Map = new Map(); // TODO: Support multiple placements per image. The kitty spec identifies // placements by an (image id, placement id) pair — same i + different p // values should coexist, and same i + same p should replace the prior // placement. Currently we track only one storage entry per kitty image id, // so multiple placements of the same image overwrite each other. Fixing // this requires changing these maps to Map> // (kittyId → placementId → storageId) and updating addImage/deleteById // accordingly. The underlying shared ImageStorage would also need to // support multiple entries per logical image. private readonly _kittyIdToStorageId: Map = new Map(); private readonly _storageIdToKittyId: Map = new Map(); private readonly _previousOnImageDeleted: ((storageId: number) => void) | undefined; private readonly _wrappedOnImageDeleted: (storageId: number) => void; private readonly _handleStorageImageDeleted = (storageId: number): void => { const kittyId = this._storageIdToKittyId.get(storageId); if (kittyId !== undefined) { this._kittyIdToStorageId.delete(kittyId); this._storageIdToKittyId.delete(storageId); this._images.delete(kittyId); } }; constructor( private readonly _storage: ImageStorage ) { this._previousOnImageDeleted = this._storage.onImageDeleted; this._wrappedOnImageDeleted = (storageId: number) => { this._previousOnImageDeleted?.(storageId); this._handleStorageImageDeleted(storageId); }; this._storage.onImageDeleted = this._wrappedOnImageDeleted; } public reset(): void { this._nextImageId = 1; this._images.clear(); this._kittyIdToStorageId.clear(); this._storageIdToKittyId.clear(); } public dispose(): void { this.reset(); if (this._storage.onImageDeleted === this._wrappedOnImageDeleted) { this._storage.onImageDeleted = this._previousOnImageDeleted; } } public storeImage(id: number | undefined, imageData: Omit): number { const imageId = id ?? this._nextImageId++; const oldStorageId = this._kittyIdToStorageId.get(imageId); if (oldStorageId !== undefined) { this._storage.deleteImage(oldStorageId); this._kittyIdToStorageId.delete(imageId); this._storageIdToKittyId.delete(oldStorageId); } if (!this._images.has(imageId) && this._images.size >= KittyImageStorage._maxStoredImages) { this._evictUndisplayedImages(); } this._images.set(imageId, { ...imageData, id: imageId }); return imageId; } public addImage(kittyId: number, image: HTMLCanvasElement | ImageBitmap, scrolling: boolean, layer: ImageLayer, zIndex: number): void { // Clean up stale reverse-mapping from a previous placement of the same // kitty image. The old shared-storage entry is kept (it may still be // visible on screen) but its reverse mapping is removed so that eviction // of the old entry won't incorrectly delete the kitty image data. const oldStorageId = this._kittyIdToStorageId.get(kittyId); if (oldStorageId !== undefined) { this._storageIdToKittyId.delete(oldStorageId); } const storageId = this._storage.addImage(image, scrolling, layer, zIndex); this._kittyIdToStorageId.set(kittyId, storageId); this._storageIdToKittyId.set(storageId, kittyId); } public getImage(kittyId: number): IKittyImageData | undefined { return this._images.get(kittyId); } public deleteById(kittyId: number): void { this._images.delete(kittyId); const storageId = this._kittyIdToStorageId.get(kittyId); if (storageId !== undefined) { this._storage.deleteImage(storageId); this._kittyIdToStorageId.delete(kittyId); this._storageIdToKittyId.delete(storageId); } } public deleteAll(): void { this._images.clear(); for (const storageId of this._kittyIdToStorageId.values()) { this._storage.deleteImage(storageId); } this._kittyIdToStorageId.clear(); this._storageIdToKittyId.clear(); } public get images(): ReadonlyMap { return this._images; } public get kittyIdToStorageId(): ReadonlyMap { return this._kittyIdToStorageId; } public get lastImageId(): number { return this._nextImageId - 1; } private _evictUndisplayedImages(): void { for (const [kittyId] of this._images) { if (this._images.size <= KittyImageStorage._maxStoredImages / 2) { break; } if (!this._kittyIdToStorageId.has(kittyId)) { this._images.delete(kittyId); } } } } ================================================ FILE: addons/addon-image/src/tsconfig.json ================================================ { "compilerOptions": { "target": "es2017", "module": "commonjs", "sourceMap": true, "outDir": "../out", "rootDir": ".", "strict": true, "noUnusedLocals": true, "preserveWatchOutput": true, "types": [ "../../../node_modules/@types/mocha" ], "paths": { "browser/*": [ "../../../src/browser/*" ], "common/*": [ "../../../src/common/*" ], "@xterm/addon-image": [ "../typings/addon-image.d.ts" ], "*": [ "./*" ] } }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/browser" }, { "path": "../../../src/common" } ] } ================================================ FILE: addons/addon-image/test/ImageAddon.test.ts ================================================ /** * Copyright (c) 2020 The xterm.js authors. All rights reserved. * @license MIT */ import test from '@playwright/test'; import { readFileSync } from 'fs'; import { FINALIZER, introducer, sixelEncode } from 'sixel'; import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from '../../../test/playwright/TestUtils'; import { deepStrictEqual, ok, strictEqual } from 'assert'; /** * 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; kittySupport: boolean; kittySizeLimit: number; } // eslint-disable-next-line declare const ImageAddon: { new(options?: Partial): 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/addon-image/fixture/palette.blob'); const data32 = new Uint32Array(data8.buffer); const palette = new Set(); 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/addon-image/fixture/iip/palette.iip', { encoding: 'utf-8' }), [640, 80]], [readFileSync('./addons/addon-image/fixture/iip/spinfox.iip', { encoding: 'utf-8' }), [148, 148]], [readFileSync('./addons/addon-image/fixture/iip/w3c_gif.iip', { encoding: 'utf-8' }), [72, 48]], [readFileSync('./addons/addon-image/fixture/iip/w3c_jpg.iip', { encoding: 'utf-8' }), [72, 48]], [readFileSync('./addons/addon-image/fixture/iip/w3c_png.iip', { encoding: 'utf-8' }), [72, 48]] ]; let ctx: ITestContext; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); await openTerminal(ctx, { cols: 80, rows: 24 }); }); test.afterAll(async () => await ctx.page.close()); test.describe('ImageAddon', () => { test.beforeEach(async ({}, testInfo) => { // DEBT: This test never worked on webkit if (ctx.browser.browserType().name() === 'webkit') { testInfo.skip(); return; } await ctx.page.evaluate(` window.term.reset() window.imageAddon?.dispose(); window.imageAddon = new ImageAddon({ sixelPaletteLimit: 512 }); window.term.loadAddon(window.imageAddon); `); }); test('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) { strictEqual( await ctx.page.evaluate('(() => { const v = window.term.' + prop + '; return v !== undefined && v !== null; })()'), true, `problem at ${prop}` ); } // bufferline privates strictEqual(await ctx.page.evaluate('window.term._core.buffer.lines.get(0)._data instanceof Uint32Array'), true); strictEqual(await ctx.page.evaluate('window.term._core.buffer.lines.get(0)._extendedAttrs instanceof Object'), true); // inputhandler privates strictEqual(await ctx.page.evaluate('window.term._core._inputHandler._curAttrData.constructor.name'), '_AttributeData'); strictEqual(await ctx.page.evaluate('window.term._core._inputHandler._parser.constructor.name'), 'EscapeSequenceParser'); }); test.describe('ctor options', () => { test('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, kittySupport: true, kittySizeLimit: 20000000 }; deepStrictEqual(await ctx.page.evaluate(`window.imageAddon._opts`), DEFAULT_OPTIONS); }); test('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, kittySupport: false, kittySizeLimit: 1000 }; await ctx.page.evaluate(opts => { (window as any).imageAddonCustom = new ImageAddon(opts.opts); (window as any).term.loadAddon((window as any).imageAddonCustom); }, { opts: customSettings }); deepStrictEqual(await ctx.page.evaluate(`window.imageAddonCustom._opts`), customSettings); }); }); test.describe('scrolling & cursor modes', () => { test('testdata default (scrolling with VT240 cursor pos)', async () => { const dim = await getDimensions(); await ctx.proxy.write(SIXEL_SEQ_0); deepStrictEqual(await getCursor(), [0, Math.floor(TESTDATA.height/dim.cellHeight)]); // moved to right by 10 cells await ctx.proxy.write('#'.repeat(10) + SIXEL_SEQ_0); deepStrictEqual(await getCursor(), [10, Math.floor(TESTDATA.height/dim.cellHeight) * 2]); }); test('write testdata noScrolling', async () => { await ctx.proxy.write('\x1b[?80h' + SIXEL_SEQ_0); deepStrictEqual(await getCursor(), [0, 0]); // second draw does not change anything await ctx.proxy.write(SIXEL_SEQ_0); deepStrictEqual(await getCursor(), [0, 0]); }); test('testdata cursor always at VT240 pos', async () => { const dim = await getDimensions(); // offset 0 await ctx.proxy.write(SIXEL_SEQ_0); deepStrictEqual(await getCursor(), [0, Math.floor(TESTDATA.height/dim.cellHeight)]); // moved to right by 10 cells await ctx.proxy.write('#'.repeat(10) + SIXEL_SEQ_0); deepStrictEqual(await getCursor(), [10, Math.floor(TESTDATA.height/dim.cellHeight) * 2]); // moved by 30 cells (+10 prev) await ctx.proxy.write('#'.repeat(30) + SIXEL_SEQ_0); deepStrictEqual(await getCursor(), [10 + 30, Math.floor(TESTDATA.height/dim.cellHeight) * 3]); }); }); test.describe('image lifecycle & eviction', () => { test('onImageAdded fires for each image', async () => { await ctx.page.evaluate(` window._imageAddedCount = 0; window.imageAddon.onImageAdded(() => { window._imageAddedCount++; }); `); await ctx.proxy.write(SIXEL_SEQ_0); await pollFor(ctx.page, 'window._imageAddedCount', 1); await ctx.proxy.write(SIXEL_SEQ_0); await pollFor(ctx.page, 'window._imageAddedCount', 2); await ctx.proxy.write(SIXEL_SEQ_0); await pollFor(ctx.page, 'window._imageAddedCount', 3); }); test('delete image once scrolled off', async () => { await ctx.proxy.write(SIXEL_SEQ_0); pollFor(ctx.page, 'window.imageAddon._storage._images.size', 1); // scroll to scrollback + rows - 1 await ctx.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 timeout(100); pollFor(ctx.page, 'window.imageAddon._storage._images.size', 1); // scroll one further should delete the image await ctx.page.evaluate(() => new Promise(res => (window as any).term.write('\n', res))); pollFor(ctx.page, 'window.imageAddon._storage._images.size', 0); }); test('get storageUsage', async () => { strictEqual(await ctx.page.evaluate('window.imageAddon.storageUsage'), 0); await ctx.proxy.write(SIXEL_SEQ_0); ok(Math.abs((await ctx.page.evaluate('window.imageAddon.storageUsage')) - 640 * 80 * 4 / 1000000) < 0.05); }); test('get/set storageLimit', async () => { strictEqual(await ctx.page.evaluate('window.imageAddon.storageLimit'), 128); strictEqual(await ctx.page.evaluate('window.imageAddon.storageLimit = 1'), 1); strictEqual(await ctx.page.evaluate('window.imageAddon.storageLimit'), 1); }); test('remove images by storage limit pressure', async () => { strictEqual(await ctx.page.evaluate('window.imageAddon.storageLimit = 1'), 1); // never go beyond storage limit await ctx.proxy.write(SIXEL_SEQ_0); await ctx.proxy.write(SIXEL_SEQ_0); await ctx.proxy.write(SIXEL_SEQ_0); await ctx.proxy.write(SIXEL_SEQ_0); await timeout(100); const usage = await ctx.page.evaluate('window.imageAddon.storageUsage'); await ctx.proxy.write(SIXEL_SEQ_0); await ctx.proxy.write(SIXEL_SEQ_0); await ctx.proxy.write(SIXEL_SEQ_0); await ctx.proxy.write(SIXEL_SEQ_0); await timeout(100); strictEqual(await ctx.page.evaluate('window.imageAddon.storageUsage'), usage); strictEqual(usage as number < 1, true); }); test('set storageLimit removes images synchronously', async () => { await ctx.proxy.write(SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0); const usage: number = await ctx.page.evaluate('window.imageAddon.storageUsage'); const newUsage: number = await ctx.page.evaluate('window.imageAddon.storageLimit = 0.5; window.imageAddon.storageUsage'); strictEqual(newUsage < usage, true); strictEqual(newUsage < 0.5, true); }); test('clear alternate images on buffer change', async () => { strictEqual(await ctx.page.evaluate('window.imageAddon.storageUsage'), 0); await ctx.proxy.write('\x1b[?1049h' + SIXEL_SEQ_0); ok(Math.abs((await ctx.page.evaluate('window.imageAddon.storageUsage')) - 640 * 80 * 4 / 1000000) < 0.05); await ctx.proxy.write('\x1b[?1049l'); strictEqual(await ctx.page.evaluate('window.imageAddon.storageUsage'), 0); }); test('evict tiles by in-place overwrites (only full overwrite tested)', async () => { await timeout(50); await ctx.proxy.write('\x1b[H' + SIXEL_SEQ_0 + '\x1b[100;100H'); await timeout(50); let usage = await ctx.page.evaluate('window.imageAddon.storageUsage'); while (usage === 0) { await timeout(50); usage = await ctx.page.evaluate('window.imageAddon.storageUsage'); } await ctx.proxy.write('\x1b[H' + SIXEL_SEQ_0 + '\x1b[100;100H'); await timeout(200); // wait some time and re-check strictEqual(await ctx.page.evaluate('window.imageAddon.storageUsage'), usage); }); test('manual eviction on alternate buffer must not miss images', async () => { await ctx.proxy.write('\x1b[?1049h'); await ctx.proxy.write(SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0); await timeout(100); const usage: number = await ctx.page.evaluate('window.imageAddon.storageUsage'); await ctx.proxy.write(SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0); await ctx.proxy.write(SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0 + SIXEL_SEQ_0); await timeout(100); const newUsage: number = await ctx.page.evaluate('window.imageAddon.storageUsage'); strictEqual(newUsage, usage); }); }); test.describe('IIP support - testimages', () => { test('palette.png', async () => { await ctx.proxy.write(TESTDATA_IIP[0][0]); deepStrictEqual(await getOrigSize(1), TESTDATA_IIP[0][1]); }); test('spinfox.png', async () => { await ctx.proxy.write(TESTDATA_IIP[1][0]); deepStrictEqual(await getOrigSize(1), TESTDATA_IIP[1][1]); }); test('w3c gif', async () => { await ctx.proxy.write(TESTDATA_IIP[2][0]); deepStrictEqual(await getOrigSize(1), TESTDATA_IIP[2][1]); }); test('w3c jpeg', async () => { await ctx.proxy.write(TESTDATA_IIP[3][0]); deepStrictEqual(await getOrigSize(1), TESTDATA_IIP[3][1]); }); test('w3c png', async () => { await ctx.proxy.write(TESTDATA_IIP[4][0]); deepStrictEqual(await getOrigSize(1), TESTDATA_IIP[4][1]); }); }); }); /** * terminal access helpers. */ async function getDimensions(): Promise { const dimensions: any = await ctx.page.evaluate(`term.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 ctx.page.evaluate('[window.term.buffer.active.cursorX, window.term.buffer.active.cursorY]'); } async function getImageStorageLength(): Promise { return ctx.page.evaluate('window.imageAddon._storage._images.size'); } async function getScrollbackPlusRows(): Promise { return ctx.page.evaluate('window.term.options.scrollback + window.term.rows'); } async function getOrigSize(id: number): Promise<[number, number]> { return ctx.page.evaluate(`[ window.imageAddon._storage._images.get(${id}).orig.width, window.imageAddon._storage._images.get(${id}).orig.height ]`); } ================================================ FILE: addons/addon-image/test/KittyGraphics.test.ts ================================================ /** * Copyright (c) 2026 The xterm.js authors. All rights reserved. * @license MIT */ import test from '@playwright/test'; import { readFileSync } from 'fs'; import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from '../../../test/playwright/TestUtils'; import { deepStrictEqual, ok, strictEqual } from 'assert'; /** * 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; kittySupport: boolean; kittySizeLimit: number; } // eslint-disable-next-line declare const ImageAddon: { new(options?: Partial): any; }; interface IDimensions { cellWidth: number; cellHeight: number; width: number; height: number; } // Kitty graphics test images const KITTY_BLACK_1X1_BASE64 = readFileSync('./addons/addon-image/fixture/kitty/black-1x1.png').toString('base64'); const KITTY_BLACK_1X1_BYTES = Array.from(readFileSync('./addons/addon-image/fixture/kitty/black-1x1.png')); const KITTY_RGB_3X1_BASE64 = readFileSync('./addons/addon-image/fixture/kitty/rgb-3x1.png').toString('base64'); const KITTY_MULTICOLOR_200X100_BASE64 = readFileSync('./addons/addon-image/fixture/kitty/multicolor-200x100.png').toString('base64'); const KITTY_MULTICOLOR_200X100_BYTES = Array.from(readFileSync('./addons/addon-image/fixture/kitty/multicolor-200x100.png')); // Raw RGB pixel data (f=24): 3 bytes per pixel, no header — requires s= and v= const RAW_RGB_1X1_BLACK = Buffer.from([0, 0, 0]).toString('base64'); const RAW_RGB_1X1_RED = Buffer.from([255, 0, 0]).toString('base64'); const RAW_RGB_3X1 = Buffer.from([ 255, 0, 0, 0, 255, 0, 0, 0, 255 ]).toString('base64'); const RAW_RGB_2X2 = Buffer.from([ 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 0 ]).toString('base64'); // 5 pixels (1 uint32 block + 1 remainder) — tests block+tail boundary const RAW_RGB_5X1 = Buffer.from([ 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 0, 255, 0, 255 ]).toString('base64'); // 8 pixels (2 full uint32 blocks, 0 remainder) — tests multi-block path const RAW_RGB_4X2 = Buffer.from([ 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 0, 255, 0, 255, 0, 255, 255, 128, 128, 128, 255, 255, 255 ]).toString('base64'); // Raw RGBA pixel data (f=32): 4 bytes per pixel, no header — requires s= and v= const RAW_RGBA_1X1_WHITE = Buffer.from([255, 255, 255, 255]).toString('base64'); const RAW_RGBA_1X1_RED = Buffer.from([255, 0, 0, 255]).toString('base64'); const RAW_RGBA_1X1_TRANSPARENT = Buffer.from([0, 0, 0, 0]).toString('base64'); const RAW_RGBA_3X1 = Buffer.from([ 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255 ]).toString('base64'); const RAW_RGBA_2X2 = Buffer.from([ 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255, 255, 0, 255 ]).toString('base64'); // 5 pixels — tests RGBA zero-copy with non-power-of-2 count const RAW_RGBA_5X1 = Buffer.from([ 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255, 255, 0, 255, 255, 0, 255, 255 ]).toString('base64'); let ctx: ITestContext; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); await openTerminal(ctx, { cols: 80, rows: 24 }); }); test.afterAll(async () => await ctx.page.close()); test.describe('Kitty Graphics Protocol', () => { // TODO: Add tests for larger images with various dimensions // TODO: Add tests for virtual placement (U=1) // TODO: Add tests for animation frames // TODO: Add performance tests for streaming large images // TODO: Implement cursor movement per Kitty spec - cursor should move by cols/rows after placement (unless C=1) // TODO: Distinguish lowercase delete selectors (placement only) from uppercase (placement + free data) test.beforeEach(async ({}, testInfo) => { // DEBT: This test never worked on webkit if (ctx.browser.browserType().name() === 'webkit') { testInfo.skip(); return; } await ctx.page.evaluate(` window.term.reset() window.imageAddon?.dispose(); window.imageAddon = new ImageAddon({ sixelPaletteLimit: 512 }); window.term.loadAddon(window.imageAddon); `); }); test.describe('Basic transmission and storage', () => { test('stores 1x1 black PNG with a=T (transmit and display)', async () => { const seq = `\x1b_Ga=T,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`; await ctx.proxy.write(seq); await timeout(100); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [1, 1]); }); test('stores 3x1 RGB PNG with a=T', async () => { const seq = `\x1b_Ga=T,f=100;${KITTY_RGB_3X1_BASE64}\x1b\\`; await ctx.proxy.write(seq); await timeout(100); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [3, 1]); }); test('transmit only (a=t) does not display but stores in handler', async () => { const seq = `\x1b_Ga=t,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`; await ctx.proxy.write(seq); await timeout(100); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 1); }); test('uses specified image ID', async () => { const seq = `\x1b_Ga=t,f=100,i=42;${KITTY_BLACK_1X1_BASE64}\x1b\\`; await ctx.proxy.write(seq); await timeout(100); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(42)`), true); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(1)`), false); }); test('assigns auto-incrementing IDs when not specified', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await ctx.proxy.write(`\x1b_Ga=t,f=100;${KITTY_RGB_3X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 2); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(1)`), true); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(2)`), true); }); test('defaults to transmit action when action is omitted', async () => { const seq = `\x1b_Gf=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`; await ctx.proxy.write(seq); await timeout(100); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 1); }); test('ignores command when action is empty string', async () => { const seq = `\x1b_Ga=,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`; await ctx.proxy.write(seq); await timeout(100); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 0); }); }); test.describe('Chunked transmission', () => { test('handles chunked transmission (m=1)', async () => { const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, half); const part2 = KITTY_BLACK_1X1_BASE64.substring(half); const seq1 = `\x1b_Ga=T,f=100,i=99,m=1;${part1}\x1b\\`; const seq2 = `\x1b_Ga=T,f=100,i=99;${part2}\x1b\\`; await ctx.proxy.write(seq1); await timeout(50); strictEqual(await getImageStorageLength(), 0); await ctx.proxy.write(seq2); await timeout(100); strictEqual(await getImageStorageLength(), 1); }); test('verifies chunked data is assembled correctly', async () => { const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, half); const part2 = KITTY_BLACK_1X1_BASE64.substring(half); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=99,m=1;${part1}\x1b\\`); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=99;${part2}\x1b\\`); await timeout(100); const storedData = await ctx.page.evaluate(async () => { const blob = (window as any).imageAddon._handlers.get('kitty').images.get(99).data; const buffer = await blob.arrayBuffer(); return Array.from(new Uint8Array(buffer)); }); deepStrictEqual(storedData, KITTY_BLACK_1X1_BYTES); }); test('enforces size limit across chunked transmissions', async () => { // Create a custom addon with very small size limit (100 bytes) // The 1x1 PNG is ~164 bytes base64, so 2 chunks should exceed 100 await ctx.page.evaluate(() => { (window as any).smallLimitAddon = new ImageAddon({ kittySupport: true, kittySizeLimit: 100 // Very small limit }); (window as any).term.loadAddon((window as any).smallLimitAddon); }); // Split the base64 data into two chunks const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, half); const part2 = KITTY_BLACK_1X1_BASE64.substring(half); // Send chunked data - first chunk (~82 bytes) is under limit await ctx.proxy.write(`\x1b_Ga=t,f=100,i=777,m=1;${part1}\x1b\\`); await timeout(50); // Second chunk brings total to ~164 bytes, exceeding 100 byte limit await ctx.proxy.write(`\x1b_Ga=t,f=100,i=777;${part2}\x1b\\`); await timeout(100); // Image should NOT be stored due to size limit strictEqual(await ctx.page.evaluate(`window.smallLimitAddon._handlers.get('kitty').images.has(777)`), false); // Cleanup await ctx.page.evaluate(() => { (window as any).smallLimitAddon.dispose(); }); }); test('chunked a=T works when subsequent chunks omit i= (spec pattern)', async () => { const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, half); const part2 = KITTY_BLACK_1X1_BASE64.substring(half); await ctx.proxy.write(`\x1b_Ga=T,f=100,i=400,m=1;${part1}\x1b\\`); await timeout(50); strictEqual(await getImageStorageLength(), 0); await ctx.proxy.write(`\x1b_Gm=0;${part2}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); }); test('chunked a=t works when subsequent chunks omit i= (spec pattern)', async () => { const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, half); const part2 = KITTY_BLACK_1X1_BASE64.substring(half); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=401,m=1;${part1}\x1b\\`); await ctx.proxy.write(`\x1b_Gm=0;${part2}\x1b\\`); await timeout(100); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(401)`), true); }); test('chunked data without i= on subsequent chunks is assembled correctly', async () => { const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, half); const part2 = KITTY_BLACK_1X1_BASE64.substring(half); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=402,m=1;${part1}\x1b\\`); await ctx.proxy.write(`\x1b_Gm=0;${part2}\x1b\\`); await timeout(100); const storedData = await ctx.page.evaluate(async () => { const blob = (window as any).imageAddon._handlers.get('kitty').images.get(402).data; const buffer = await blob.arrayBuffer(); return Array.from(new Uint8Array(buffer)); }); deepStrictEqual(storedData, KITTY_BLACK_1X1_BYTES); }); test('three-chunk transfer with only m= on middle and last chunks', async () => { const third = Math.floor(KITTY_BLACK_1X1_BASE64.length / 3); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, third); const part2End = third + Math.floor((KITTY_BLACK_1X1_BASE64.length - third) / 2); const alignedPart2End = part2End - (part2End - third) % 4 + third; const part2 = KITTY_BLACK_1X1_BASE64.substring(third, alignedPart2End); const part3 = KITTY_BLACK_1X1_BASE64.substring(alignedPart2End); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=403,m=1;${part1}\x1b\\`); await ctx.proxy.write(`\x1b_Gm=1;${part2}\x1b\\`); await ctx.proxy.write(`\x1b_Gm=0;${part3}\x1b\\`); await timeout(100); const storedData = await ctx.page.evaluate(async () => { const blob = (window as any).imageAddon._handlers.get('kitty').images.get(403).data; const buffer = await blob.arrayBuffer(); return Array.from(new Uint8Array(buffer)); }); deepStrictEqual(storedData, KITTY_BLACK_1X1_BYTES); }); test('chunked a=T without i= on any chunk works (no response)', async () => { const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, half); const part2 = KITTY_BLACK_1X1_BASE64.substring(half); await ctx.proxy.write(`\x1b_Ga=T,f=100,m=1;${part1}\x1b\\`); await timeout(50); strictEqual(await getImageStorageLength(), 0); await ctx.proxy.write(`\x1b_Gm=0;${part2}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); }); test('chunked transfer responds OK on final chunk when i= on first only', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, half); const part2 = KITTY_BLACK_1X1_BASE64.substring(half); await ctx.proxy.write(`\x1b_Ga=T,f=100,i=405,m=1;${part1}\x1b\\`); await timeout(50); let response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, ''); await ctx.proxy.write(`\x1b_Gm=0;${part2}\x1b\\`); await timeout(100); response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=405;OK\x1b\\'); }); }); test.describe('Delete commands', () => { test('delete command (a=d,d=i) removes specific image by id', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=10;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 1); await ctx.proxy.write(`\x1b_Ga=d,d=i,i=10\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 0); }); test('delete command (a=d) removes all images when no id specified', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=1;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=2;${KITTY_RGB_3X1_BASE64}\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 2); await ctx.proxy.write(`\x1b_Ga=d\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 0); }); test('delete by id aborts in-flight chunked upload', async () => { const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, half); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=50,m=1;${part1}\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').pendingTransmissions.size`), 1); await ctx.proxy.write(`\x1b_Ga=d,d=i,i=50\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').pendingTransmissions.size`), 0); }); test('delete by id only aborts targeted upload, not others', async () => { const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, half); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=55,m=1;${part1}\x1b\\`); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=56,m=1;${part1}\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').pendingTransmissions.size`), 2); await ctx.proxy.write(`\x1b_Ga=d,d=i,i=55\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').pendingTransmissions.size`), 1); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').pendingTransmissions.has(56)`), true); }); test('delete all aborts in-flight chunked upload', async () => { const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, half); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=60,m=1;${part1}\x1b\\`); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=61,m=1;${part1}\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').pendingTransmissions.size`), 2); await ctx.proxy.write(`\x1b_Ga=d\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').pendingTransmissions.size`), 0); }); test('d=i selector deletes specific image by id', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=80;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=81;${KITTY_RGB_3X1_BASE64}\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 2); await ctx.proxy.write(`\x1b_Ga=d,d=i,i=80\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 1); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(81)`), true); }); test('d=I selector deletes specific image by id (uppercase)', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=82;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=83;${KITTY_RGB_3X1_BASE64}\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 2); await ctx.proxy.write(`\x1b_Ga=d,d=I,i=82\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 1); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(83)`), true); }); test('d=a selector deletes all images', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=84;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=85;${KITTY_RGB_3X1_BASE64}\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 2); await ctx.proxy.write(`\x1b_Ga=d,d=a\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 0); }); test('d=A selector deletes all images (uppercase)', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=86;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=87;${KITTY_RGB_3X1_BASE64}\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 2); await ctx.proxy.write(`\x1b_Ga=d,d=A\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 0); }); test('d=a selector also removes displayed images from storage', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,i=88;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); await ctx.proxy.write(`\x1b_Ga=d,d=a\x1b\\`); await timeout(50); strictEqual(await getImageStorageLength(), 0); }); test('d=i selector also removes displayed image from storage', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,i=89;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); await ctx.proxy.write(`\x1b_Ga=d,d=i,i=89\x1b\\`); await timeout(50); strictEqual(await getImageStorageLength(), 0); }); test('d=i without id does nothing', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=90;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 1); await ctx.proxy.write(`\x1b_Ga=d,d=i\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 1); }); test('d=i selector clears pixels from canvas', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,i=92,q=1;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [0, 0, 0, 255]); await ctx.proxy.write(`\x1b_Ga=d,d=i,i=92\x1b\\`); await timeout(100); strictEqual(await getPixel(0, 0, 0, 0), null); }); test('d=a selector clears all pixels from canvas', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,i=93,q=1;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [0, 0, 0, 255]); await ctx.proxy.write(`\x1b_Ga=d,d=a\x1b\\`); await timeout(100); strictEqual(await getPixel(0, 0, 0, 0), null); }); test('unsupported delete selector is ignored', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=91;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 1); await ctx.proxy.write(`\x1b_Ga=d,d=c\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 1); }); test('chunks sent after delete are not assembled with previous data', async () => { const half = Math.floor(KITTY_BLACK_1X1_BASE64.length / 2); const part1 = KITTY_BLACK_1X1_BASE64.substring(0, half); const part2 = KITTY_BLACK_1X1_BASE64.substring(half); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=70,m=1;${part1}\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').pendingTransmissions.size`), 1); await ctx.proxy.write(`\x1b_Ga=d\x1b\\`); await timeout(50); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=70;${part2}\x1b\\`); await timeout(100); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(70)`), true); const storedSize: number = await ctx.page.evaluate(async () => { const blob = (window as any).imageAddon._handlers.get('kitty').images.get(70).data; return blob.size; }); ok(storedSize < KITTY_BLACK_1X1_BYTES.length, 'stored data should be smaller than full image (only second half)'); }); }); test.describe('Query support (a=q)', () => { test('responds with OK for capability query without payload', async () => { let response = ''; await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write('\x1b_Gi=31,a=q;\x1b\\'); await timeout(100); response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=31;OK\x1b\\'); }); test('responds with OK for valid PNG query', async () => { let response = ''; await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=42,a=q,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=42;OK\x1b\\'); }); test('query does NOT store the image (unlike transmit)', async () => { await ctx.page.evaluate(() => { (window as any).term.onData(() => { /* consume response */ }); }); await ctx.proxy.write(`\x1b_Gi=50,a=q,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(50)`), false); }); test('responds with error for invalid base64', async () => { let response = ''; await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write('\x1b_Gi=60,a=q,f=100;!!!invalid!!!\x1b\\'); await timeout(100); response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response.startsWith('\x1b_Gi=60;EINVAL:'), true); }); test('responds with error for RGB data without dimensions', async () => { let response = ''; await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write('\x1b_Gi=70,a=q,f=24;AAAA\x1b\\'); await timeout(100); response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=70;EINVAL:width and height required for raw pixel data\x1b\\'); }); test('suppresses OK response when q=1', async () => { await ctx.page.evaluate(() => { (window as any).kittyGotResponse = false; (window as any).term.onData(() => { (window as any).kittyGotResponse = true; }); }); await ctx.proxy.write(`\x1b_Gi=80,a=q,q=1,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await ctx.page.evaluate('window.kittyGotResponse'), false); }); test('suppresses error response when q=2', async () => { await ctx.page.evaluate(() => { (window as any).kittyGotResponse = false; (window as any).term.onData(() => { (window as any).kittyGotResponse = true; }); }); await ctx.proxy.write('\x1b_Gi=90,a=q,q=2,f=100;!!!invalid!!!\x1b\\'); await timeout(100); strictEqual(await ctx.page.evaluate('window.kittyGotResponse'), false); }); test('responds with EINVAL when both i and I keys are specified', async () => { let response = ''; await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); // Per spec: "Specifying both i and I keys in any command is an error" await ctx.proxy.write(`\x1b_Gi=100,I=200,a=q,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=100;EINVAL:cannot specify both i and I keys\x1b\\'); }); test('responds with EINVAL for i+I conflict even without payload', async () => { let response = ''; await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); // Delete command with both i and I (no payload case) await ctx.proxy.write('\x1b_Gi=101,I=201,a=d\x1b\\'); await timeout(100); response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=101;EINVAL:cannot specify both i and I keys\x1b\\'); }); }); test.describe('Error responses for transmit and display', () => { test('a=t sends EINVAL on decode error when id is specified', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write('\x1b_Gi=110,a=t,f=100;!!!invalid!!!\x1b\\'); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=110;EINVAL:invalid base64 data\x1b\\'); }); test('a=t sends no response on decode error without id', async () => { await ctx.page.evaluate(() => { (window as any).kittyGotResponse = false; (window as any).term.onData(() => { (window as any).kittyGotResponse = true; }); }); await ctx.proxy.write('\x1b_Ga=t,f=100;!!!invalid!!!\x1b\\'); await timeout(100); strictEqual(await ctx.page.evaluate('window.kittyGotResponse'), false); }); test('a=T sends EINVAL on decode error when id is specified', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write('\x1b_Gi=120,a=T,f=100;!!!invalid!!!\x1b\\'); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=120;EINVAL:invalid base64 data\x1b\\'); }); test('a=T sends no response on decode error without id', async () => { await ctx.page.evaluate(() => { (window as any).kittyGotResponse = false; (window as any).term.onData(() => { (window as any).kittyGotResponse = true; }); }); await ctx.proxy.write('\x1b_Ga=T,f=100;!!!invalid!!!\x1b\\'); await timeout(100); strictEqual(await ctx.page.evaluate('window.kittyGotResponse'), false); }); test('a=T sends EINVAL when raw pixel render fails (missing dimensions)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=130,a=T,f=24;${RAW_RGB_1X1_BLACK}\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response.startsWith('\x1b_Gi=130;EINVAL:'), true); }); test('a=T sends OK on successful render with id', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=140,a=T,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=140;OK\x1b\\'); }); test('a=t sends OK on successful transmit with id', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=150,a=t,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=150;OK\x1b\\'); }); test('a=t EINVAL suppressed by q=2', async () => { await ctx.page.evaluate(() => { (window as any).kittyGotResponse = false; (window as any).term.onData(() => { (window as any).kittyGotResponse = true; }); }); await ctx.proxy.write('\x1b_Gi=160,a=t,q=2,f=100;!!!invalid!!!\x1b\\'); await timeout(100); strictEqual(await ctx.page.evaluate('window.kittyGotResponse'), false); }); test('a=T EINVAL suppressed by q=2', async () => { await ctx.page.evaluate(() => { (window as any).kittyGotResponse = false; (window as any).term.onData(() => { (window as any).kittyGotResponse = true; }); }); await ctx.proxy.write('\x1b_Gi=170,a=T,q=2,f=100;!!!invalid!!!\x1b\\'); await timeout(100); strictEqual(await ctx.page.evaluate('window.kittyGotResponse'), false); }); test('a=t OK suppressed by q=1', async () => { await ctx.page.evaluate(() => { (window as any).kittyGotResponse = false; (window as any).term.onData(() => { (window as any).kittyGotResponse = true; }); }); await ctx.proxy.write(`\x1b_Gi=180,a=t,q=1,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await ctx.page.evaluate('window.kittyGotResponse'), false); }); test('a=T OK suppressed by q=1', async () => { await ctx.page.evaluate(() => { (window as any).kittyGotResponse = false; (window as any).term.onData(() => { (window as any).kittyGotResponse = true; }); }); await ctx.proxy.write(`\x1b_Gi=190,a=T,q=1,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await ctx.page.evaluate('window.kittyGotResponse'), false); }); }); test.describe('Transmission medium rejection', () => { test('query rejects t=f (file transmission)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=200,a=q,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response.startsWith('\x1b_Gi=200;EINVAL:'), true); }); test('query rejects t=s (shared memory)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=201,a=q,t=s,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response.startsWith('\x1b_Gi=201;EINVAL:'), true); }); test('query rejects t=t (temp file)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=202,a=q,t=t,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response.startsWith('\x1b_Gi=202;EINVAL:'), true); }); test('query accepts t=d (direct transmission)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=203,a=q,t=d,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=203;OK\x1b\\'); }); test('query without t key defaults to direct (OK)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=204,a=q,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=204;OK\x1b\\'); }); test('transmit rejects t=f with id (EINVAL response)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=300,a=t,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response.startsWith('\x1b_Gi=300;EINVAL:'), true); }); test('transmit rejects t=s with id (EINVAL response)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=301,a=t,t=s,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response.startsWith('\x1b_Gi=301;EINVAL:'), true); }); test('transmit rejects t=t with id (EINVAL response)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=302,a=t,t=t,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response.startsWith('\x1b_Gi=302;EINVAL:'), true); }); test('transmit rejects t=f without id (no response)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Ga=t,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, ''); }); test('transmit+display rejects t=f with id (EINVAL response)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=310,a=T,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response.startsWith('\x1b_Gi=310;EINVAL:'), true); }); test('transmit+display rejects t=s with id (EINVAL response)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=311,a=T,t=s,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response.startsWith('\x1b_Gi=311;EINVAL:'), true); }); test('transmit+display rejects t=t with id (EINVAL response)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=312,a=T,t=t,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response.startsWith('\x1b_Gi=312;EINVAL:'), true); }); test('transmit+display rejects t=f without id (no response)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Ga=T,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, ''); }); }); test.describe('Placement action (a=p)', () => { test('displays a previously transmitted image at cursor', async () => { // Transmit image without display await ctx.proxy.write(`\x1b_Ga=t,f=100,i=210;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 0); // Place the previously transmitted image await ctx.proxy.write(`\x1b_Ga=p,i=210\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getPixel(0, 0, 0, 0), [0, 0, 0, 255]); }); test('responds OK on successful placement', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=211;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Ga=p,i=211\x1b\\`); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=211;OK\x1b\\'); }); test('responds ENOENT for non-existent image id', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Ga=p,i=9999\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=9999;ENOENT:image not found\x1b\\'); }); test('without id sends no response', async () => { await ctx.page.evaluate(() => { (window as any).kittyGotResponse = false; (window as any).term.onData(() => { (window as any).kittyGotResponse = true; }); }); await ctx.proxy.write(`\x1b_Ga=p\x1b\\`); await timeout(100); strictEqual(await ctx.page.evaluate('window.kittyGotResponse'), false); }); test('places at specified column/row size (c/r)', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=212;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); await ctx.proxy.write(`\x1b_Ga=p,i=212,c=5,r=3\x1b\\`); await timeout(200); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getCursor(), [5, 2]); }); test('cursor advances past placed image (default C=0)', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=213;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); deepStrictEqual(await getCursor(), [0, 0]); await ctx.proxy.write(`\x1b_Ga=p,i=213\x1b\\`); await timeout(100); deepStrictEqual(await getCursor(), [1, 0]); }); test('cursor does not move when C=1', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=214;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); deepStrictEqual(await getCursor(), [0, 0]); await ctx.proxy.write(`\x1b_Ga=p,i=214,c=5,r=3,C=1\x1b\\`); await timeout(200); deepStrictEqual(await getCursor(), [0, 0]); }); test('supports z-index (negative = bottom layer)', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=215;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); await ctx.proxy.write(`\x1b_Ga=p,i=215,z=-1\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).layer`), 'bottom'); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).zIndex`), -1); }); test('supports source crop via x/y', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=216;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); // Crop to the Orange rectangle (x=20, y=0, 20x50) await ctx.proxy.write(`\x1b_Ga=p,i=216,x=20,y=0,w=20,h=50\x1b\\`); await timeout(200); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [20, 50]); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 128, 0, 255]); }); test('supports sub-cell offset via X/Y', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=217;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); await ctx.proxy.write(`\x1b_Ga=p,i=217,X=5,Y=3\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [0, 0, 0, 0]); deepStrictEqual(await getPixel(0, 0, 4, 2), [0, 0, 0, 0]); deepStrictEqual(await getPixel(0, 0, 5, 3), [0, 0, 0, 255]); }); test('multiple placements of same image create separate displays', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=218;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 0); // First placement await ctx.proxy.write(`\x1b_Ga=p,i=218,p=1\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); // Move cursor down and place again with different placement ID await ctx.proxy.write('\x1b[3;1H'); await ctx.proxy.write(`\x1b_Ga=p,i=218,p=2\x1b\\`); await timeout(100); // Both placements should be in shared storage strictEqual(await getImageStorageLength(), 2); }); test('image data remains available after placement for future placements', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=219;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); // Place three times await ctx.proxy.write(`\x1b_Ga=p,i=219\x1b\\`); await timeout(100); await ctx.proxy.write('\x1b[2;1H'); await ctx.proxy.write(`\x1b_Ga=p,i=219\x1b\\`); await timeout(100); await ctx.proxy.write('\x1b[3;1H'); await ctx.proxy.write(`\x1b_Ga=p,i=219\x1b\\`); await timeout(100); // Image data should still be available strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(219)`), true); }); test('OK response suppressed by q=1', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=220;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); await ctx.page.evaluate(() => { (window as any).kittyGotResponse = false; (window as any).term.onData(() => { (window as any).kittyGotResponse = true; }); }); await ctx.proxy.write(`\x1b_Ga=p,i=220,q=1\x1b\\`); await timeout(100); strictEqual(await ctx.page.evaluate('window.kittyGotResponse'), false); strictEqual(await getImageStorageLength(), 1); }); test('ENOENT error suppressed by q=2', async () => { await ctx.page.evaluate(() => { (window as any).kittyGotResponse = false; (window as any).term.onData(() => { (window as any).kittyGotResponse = true; }); }); await ctx.proxy.write(`\x1b_Ga=p,i=9998,q=2\x1b\\`); await timeout(100); strictEqual(await ctx.page.evaluate('window.kittyGotResponse'), false); }); test('ENOENT still reported when q=1 (only suppresses OK)', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Ga=p,i=9997,q=1\x1b\\`); await timeout(100); const response: string = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=9997;ENOENT:image not found\x1b\\'); }); test('renders pixels correctly when placing a PNG image', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=221;${KITTY_RGB_3X1_BASE64}\x1b\\`); await timeout(100); await ctx.proxy.write(`\x1b_Ga=p,i=221\x1b\\`); await timeout(100); const pixels = await getPixels(0, 0, 0, 0, 3, 1); deepStrictEqual(pixels?.slice(0, 4), [255, 0, 0, 255]); deepStrictEqual(pixels?.slice(4, 8), [0, 255, 0, 255]); deepStrictEqual(pixels?.slice(8, 12), [0, 0, 255, 255]); }); test('renders pixels correctly when placing raw RGB image', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=24,s=3,v=1,i=222;${RAW_RGB_3X1}\x1b\\`); await timeout(100); await ctx.proxy.write(`\x1b_Ga=p,i=222\x1b\\`); await timeout(100); const pixels = await getPixels(0, 0, 0, 0, 3, 1); deepStrictEqual(pixels?.slice(0, 4), [255, 0, 0, 255]); deepStrictEqual(pixels?.slice(4, 8), [0, 255, 0, 255]); deepStrictEqual(pixels?.slice(8, 12), [0, 0, 255, 255]); }); test('renders pixels correctly when placing raw RGBA image', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=32,s=3,v=1,i=223;${RAW_RGBA_3X1}\x1b\\`); await timeout(100); await ctx.proxy.write(`\x1b_Ga=p,i=223\x1b\\`); await timeout(100); const pixels = await getPixels(0, 0, 0, 0, 3, 1); deepStrictEqual(pixels?.slice(0, 4), [255, 0, 0, 255]); deepStrictEqual(pixels?.slice(4, 8), [0, 255, 0, 255]); deepStrictEqual(pixels?.slice(8, 12), [0, 0, 255, 255]); }); test('response includes placement id when p is specified', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=224;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Ga=p,i=224,p=42\x1b\\`); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=224,p=42;OK\x1b\\'); }); test('only c specified computes r from aspect ratio', async () => { // 200x100 image (2:1 aspect) with c=10. // Per spec: r = ceil((h/w) * c * cw / ch) = ceil(0.5 * 10 * cw / ch) await ctx.proxy.write(`\x1b_Ga=t,f=100,i=225;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); await ctx.proxy.write(`\x1b_Ga=p,i=225,c=10\x1b\\`); await timeout(200); const cursor = await getCursor(); const cellDims: number[] = await ctx.page.evaluate(() => { const d = (window as any).term._core._renderService.dimensions.css.cell; return [d.width, d.height]; }); const expectedR = Math.ceil((100 / 200) * 10 * cellDims[0] / cellDims[1]); deepStrictEqual(cursor, [10, expectedR - 1]); }); test('only r specified computes c from aspect ratio', async () => { // 200x100 image (2:1 aspect) with r=5. // Per spec: c = ceil((w/h) * r * ch / cw) = ceil(2 * 5 * ch / cw) await ctx.proxy.write(`\x1b_Ga=t,f=100,i=226;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(100); await ctx.proxy.write(`\x1b_Ga=p,i=226,r=5\x1b\\`); await timeout(200); const cursor = await getCursor(); const cellDims: number[] = await ctx.page.evaluate(() => { const d = (window as any).term._core._renderService.dimensions.css.cell; return [d.width, d.height]; }); const expectedC = Math.ceil((200 / 100) * 5 * cellDims[1] / cellDims[0]); deepStrictEqual(cursor, [expectedC, 4]); }); }); test.describe('Cursor positioning', () => { // NOTE: Current tests document ACTUAL behavior (MVP - cursor doesn't move) // Per Kitty spec: cursor placed at first column after last image column, // on the last row of the image. C=1 means don't move cursor. test('cursor advances past 1x1 image', async () => { const cursorBefore = await getCursor(); const seq = `\x1b_Ga=T,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`; await ctx.proxy.write(seq); await timeout(100); const cursorAfter = await getCursor(); deepStrictEqual(cursorBefore, [0, 0]); // 1x1 pixel image occupies 1 column, cursor advances past it deepStrictEqual(cursorAfter, [1, 0]); }); test('cursor advances with text before image', async () => { await ctx.proxy.write('Hello'); deepStrictEqual(await getCursor(), [5, 0]); await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); // Cursor advances 1 column past the image deepStrictEqual(await getCursor(), [6, 0]); }); test('cursor advances with text after image', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); // Cursor at column 1 (past 1-col image) deepStrictEqual(await getCursor(), [1, 0]); await ctx.proxy.write('World'); deepStrictEqual(await getCursor(), [6, 0]); }); test('cursor position with multiple images on same line', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(50); deepStrictEqual(await getCursor(), [1, 0]); await ctx.proxy.write('###'); deepStrictEqual(await getCursor(), [4, 0]); // 3x1 pixel image: ceil(3/cellWidth)=1 column await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_RGB_3X1_BASE64}\x1b\\`); await timeout(50); deepStrictEqual(await getCursor(), [5, 0]); }); test('cursor advances on newline after image', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); deepStrictEqual(await getCursor(), [1, 0]); await ctx.proxy.write('\n'); deepStrictEqual(await getCursor(), [1, 1]); }); test('cursor should move right by cols when c specified', async () => { // c=5, r computed from 1x1 aspect ratio: r = ceil((1/1) * (5*cw) / ch) // With 1:1 aspect, image height in pixels = 5*cw, so r = ceil(5*cw/ch) await ctx.proxy.write(`\x1b_Ga=T,f=100,c=5;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const cursor = await getCursor(); strictEqual(cursor[0], 5); // r is computed from aspect ratio, so y > 0 for a square image at c=5 }); test('cursor should move down by rows when r specified', async () => { // r=3, c computed from 1x1 aspect ratio: c = ceil((1/1) * (3*ch) / cw) await ctx.proxy.write(`\x1b_Ga=T,f=100,r=3;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const cursor = await getCursor(); strictEqual(cursor[1], 2); // c is computed from aspect ratio, so x > 1 for a square image at r=3 }); test('cursor should move by cols AND rows when both specified', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,c=4,r=2;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); // cursor at (4, 1): past 4 columns, on last row (row 1) deepStrictEqual(await getCursor(), [4, 1]); }); test('cursor should NOT move when C=1 is specified', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,c=5,r=3,C=1;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); // C=1: cursor stays at origin deepStrictEqual(await getCursor(), [0, 0]); }); test('cursor should calculate cols/rows from image size when not specified', async () => { const dim = await getDimensions(); // 3x1 pixel image: cols = ceil(3/cellWidth), rows = ceil(1/cellHeight) await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_RGB_3X1_BASE64}\x1b\\`); await timeout(100); const expectedCols = Math.ceil(3 / dim.cellWidth); const cursor = await getCursor(); // Cursor advances past image columns, stays on row 0 (single row image) strictEqual(cursor[0], expectedCols, 'cursor should advance by image columns'); strictEqual(cursor[1], 0, 'cursor should stay on row 0 for single-row image'); }); }); test.describe('Z-index layer placement', () => { test('default placement (no z key) stores image on top layer', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).layer`), 'top'); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).zIndex`), 0); }); test('z=0 stores image on top layer', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,z=0;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).layer`), 'top'); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).zIndex`), 0); }); test('z=1 (positive) stores image on top layer', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,z=1;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).layer`), 'top'); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).zIndex`), 1); }); test('z=-1 uses bottom layer even when allowTransparency is disabled', async () => { await ctx.page.evaluate(`window.term.options.allowTransparency = false`); await ctx.proxy.write(`\x1b_Ga=T,f=100,z=-1;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).layer`), 'bottom'); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).zIndex`), -1); }); test('z=-1 (negative) stores image on bottom layer when allowTransparency is enabled', async () => { await ctx.page.evaluate(`window.term.options.allowTransparency = true`); await ctx.proxy.write(`\x1b_Ga=T,f=100,z=-1;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).layer`), 'bottom'); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).zIndex`), -1); }); test('z=-100 (large negative) stores image on bottom layer when allowTransparency is enabled', async () => { await ctx.page.evaluate(`window.term.options.allowTransparency = true`); await ctx.proxy.write(`\x1b_Ga=T,f=100,z=-100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).layer`), 'bottom'); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.get(1).zIndex`), -100); }); test('top layer canvas has correct CSS class', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const hasClass = await ctx.page.evaluate(() => { const el = document.querySelector('.xterm-image-layer-top'); return el !== null; }); strictEqual(hasClass, true); }); test('bottom layer canvas has correct CSS class', async () => { await ctx.page.evaluate(`window.term.options.allowTransparency = true`); await ctx.proxy.write(`\x1b_Ga=T,f=100,z=-1;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const hasClass = await ctx.page.evaluate(() => { const el = document.querySelector('.xterm-image-layer-bottom'); return el !== null; }); strictEqual(hasClass, true); }); test('bottom layer canvas is before text canvas in DOM order', async () => { await ctx.page.evaluate(`window.term.options.allowTransparency = true`); await ctx.proxy.write(`\x1b_Ga=T,f=100,z=-1;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); const isFirst = await ctx.page.evaluate(() => { const screen = document.querySelector('.xterm-screen'); return screen?.firstElementChild?.classList.contains('xterm-image-layer-bottom') ?? false; }); strictEqual(isFirst, true); }); }); test.describe('Pixel verification', () => { test('renders 1x1 black PNG at cursor position', async () => { const seq = `\x1b_Ga=T,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`; await ctx.proxy.write(seq); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [0, 0, 0, 255]); }); test('renders 3x1 RGB PNG (red, green, blue pixels)', async () => { const seq = `\x1b_Ga=T,f=100;${KITTY_RGB_3X1_BASE64}\x1b\\`; await ctx.proxy.write(seq); await timeout(100); const pixels = await getPixels(0, 0, 0, 0, 3, 1); deepStrictEqual(pixels?.slice(0, 4), [255, 0, 0, 255]); deepStrictEqual(pixels?.slice(4, 8), [0, 255, 0, 255]); deepStrictEqual(pixels?.slice(8, 12), [0, 0, 255, 255]); }); }); test.describe('Larger image (200x100 multicolor PNG)', () => { test.describe('Basic transmission and storage', () => { test('stores 200x100 PNG with a=T', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [200, 100]); }); test('transmit only (a=t) stores 200x100 image without display', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.size`), 1); }); test('stores with specified image ID', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=400;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(400)`), true); }); }); test.describe('Chunked transmission', () => { test('handles 2-chunk transmission', async () => { const half = Math.floor(KITTY_MULTICOLOR_200X100_BASE64.length / 2); const part1 = KITTY_MULTICOLOR_200X100_BASE64.substring(0, half); const part2 = KITTY_MULTICOLOR_200X100_BASE64.substring(half); await ctx.proxy.write(`\x1b_Ga=T,f=100,i=500,m=1;${part1}\x1b\\`); await timeout(50); strictEqual(await getImageStorageLength(), 0); await ctx.proxy.write(`\x1b_Ga=T,f=100,i=500;${part2}\x1b\\`); await timeout(200); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [200, 100]); }); test('handles 3-chunk transmission', async () => { const third = Math.floor(KITTY_MULTICOLOR_200X100_BASE64.length / 3); const p1 = KITTY_MULTICOLOR_200X100_BASE64.substring(0, third); const p2 = KITTY_MULTICOLOR_200X100_BASE64.substring(third, third * 2); const p3 = KITTY_MULTICOLOR_200X100_BASE64.substring(third * 2); await ctx.proxy.write(`\x1b_Ga=T,f=100,i=501,m=1;${p1}\x1b\\`); await timeout(50); await ctx.proxy.write(`\x1b_Ga=T,f=100,i=501,m=1;${p2}\x1b\\`); await timeout(50); await ctx.proxy.write(`\x1b_Ga=T,f=100,i=501;${p3}\x1b\\`); await timeout(200); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [200, 100]); }); test('verifies chunked data assembles correctly', async () => { const half = Math.floor(KITTY_MULTICOLOR_200X100_BASE64.length / 2); const part1 = KITTY_MULTICOLOR_200X100_BASE64.substring(0, half); const part2 = KITTY_MULTICOLOR_200X100_BASE64.substring(half); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=502,m=1;${part1}\x1b\\`); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=502;${part2}\x1b\\`); await timeout(200); const storedData = await ctx.page.evaluate(async () => { const blob = (window as any).imageAddon._handlers.get('kitty').images.get(502).data; const buffer = await blob.arrayBuffer(); return Array.from(new Uint8Array(buffer)); }); deepStrictEqual(storedData, KITTY_MULTICOLOR_200X100_BYTES); }); }); test.describe('Cursor positioning', () => { test('cursor advances past multi-cell image', async () => { const dim = await getDimensions(); await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); const expectedCols = Math.ceil(200 / dim.cellWidth); const expectedRows = Math.ceil(100 / dim.cellHeight) - 1; const cursor = await getCursor(); strictEqual(cursor[0], expectedCols, 'cursor should advance by image columns'); strictEqual(cursor[1], expectedRows, 'cursor should be on last row of image'); }); test('cursor does not move with C=1', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,C=1;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); deepStrictEqual(await getCursor(), [0, 0]); }); test('cursor uses explicit c and r over image dimensions', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,c=10,r=5;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); deepStrictEqual(await getCursor(), [10, 4]); }); }); test.describe('Pixel verification', () => { // The 200x100 image has 20 colored rectangles in a 10x2 grid. // Each rectangle is 20px wide x 50px tall. // Top row (y=0..49): Red, Orange, Yellow, Lime, Green, Cyan, SkyBlue, Blue, Purple, Magenta // Bottom row (y=50..99): Pink, Brown, Maroon, Olive, Teal, Navy, Gray, DarkGray, LightGray, White test('renders red rectangle at top-left origin (0,0)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); // Pixel (0,0) is in the first rectangle: Red deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 0, 0, 255]); }); test('renders top row colors at rectangle centers', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); // Sample center of each top-row rectangle (y=25, x=10,30,50,...,190) // All within the first cell row, so we read from the canvas at cell (0,0) // Red at x=10 deepStrictEqual(await getPixel(0, 0, 10, 25), [255, 0, 0, 255]); // Orange at x=30 deepStrictEqual(await getPixel(0, 0, 30, 25), [255, 128, 0, 255]); // Yellow at x=50 deepStrictEqual(await getPixel(0, 0, 50, 25), [255, 255, 0, 255]); // Lime at x=70 deepStrictEqual(await getPixel(0, 0, 70, 25), [0, 255, 0, 255]); // Green at x=90 deepStrictEqual(await getPixel(0, 0, 90, 25), [0, 128, 0, 255]); }); test('renders bottom row colors at rectangle centers', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); // Bottom row starts at y=50. Center at y=75. // Pink at x=10 deepStrictEqual(await getPixel(0, 0, 10, 75), [255, 192, 203, 255]); // Brown at x=30 deepStrictEqual(await getPixel(0, 0, 30, 75), [165, 42, 42, 255]); // Maroon at x=50 deepStrictEqual(await getPixel(0, 0, 50, 75), [128, 0, 0, 255]); // Olive at x=70 deepStrictEqual(await getPixel(0, 0, 70, 75), [128, 128, 0, 255]); // Teal at x=90 deepStrictEqual(await getPixel(0, 0, 90, 75), [0, 128, 128, 255]); }); test('renders correct colors at rectangle boundaries', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); // Last pixel of first rectangle (x=19, y=0): still Red deepStrictEqual(await getPixel(0, 0, 19, 0), [255, 0, 0, 255]); // First pixel of second rectangle (x=20, y=0): Orange deepStrictEqual(await getPixel(0, 0, 20, 0), [255, 128, 0, 255]); // Last pixel of top row (x=199, y=49): Magenta deepStrictEqual(await getPixel(0, 0, 199, 49), [255, 0, 255, 255]); // First pixel of bottom row (x=0, y=50): Pink deepStrictEqual(await getPixel(0, 0, 0, 50), [255, 192, 203, 255]); }); test('renders correct color at bottom-right corner', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); // Bottom-right corner (x=199, y=99): White deepStrictEqual(await getPixel(0, 0, 199, 99), [255, 255, 255, 255]); }); test('renders a strip of top-row pixels via getPixels', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); // Read 3 pixels starting at x=18 y=0, spanning the Red/Orange boundary const pixels = await getPixels(0, 0, 18, 0, 3, 1); // x=18,19 -> Red; x=20 -> Orange deepStrictEqual(pixels?.slice(0, 4), [255, 0, 0, 255]); // x=18: Red deepStrictEqual(pixels?.slice(4, 8), [255, 0, 0, 255]); // x=19: Red deepStrictEqual(pixels?.slice(8, 12), [255, 128, 0, 255]); // x=20: Orange }); test('applies source crop via x/y/w/h before display', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,x=20,y=0,w=20,h=50;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); deepStrictEqual(await getOrigSize(1), [20, 50]); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 128, 0, 255]); deepStrictEqual(await getPixel(0, 0, 19, 49), [255, 128, 0, 255]); }); test('scales cropped source region to c/r placement rectangle', async () => { // Firefox's createImageBitmap uses different resize sampling, producing // slightly off pixel values compared to Chromium, so skip on Firefox. if (ctx.browser.browserType().name() === 'firefox') { test.skip(); } await ctx.proxy.write(`\x1b_Ga=T,f=100,x=1,y=0,w=1,h=1,c=4,r=2;${KITTY_RGB_3X1_BASE64}\x1b\\`); await timeout(200); deepStrictEqual(await getCursor(), [4, 1]); const left = await getPixel(0, 0, 2, 10); const right = await getPixel(0, 0, 25, 10); deepStrictEqual(left, [0, 255, 0, 255]); deepStrictEqual(right, [0, 255, 0, 255]); }); test('applies sub-cell offset via X/Y within first cell', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,X=5,Y=3;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [0, 0, 0, 0]); deepStrictEqual(await getPixel(0, 0, 4, 2), [0, 0, 0, 0]); deepStrictEqual(await getPixel(0, 0, 5, 3), [0, 0, 0, 255]); }); test('w=0 is treated as unset (displays full width)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,w=0;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); deepStrictEqual(await getOrigSize(1), [200, 100]); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 0, 0, 255]); deepStrictEqual(await getPixel(0, 0, 199, 99), [255, 255, 255, 255]); }); test('h=0 is treated as unset (displays full height)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,h=0;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); deepStrictEqual(await getOrigSize(1), [200, 100]); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 0, 0, 255]); deepStrictEqual(await getPixel(0, 0, 0, 50), [255, 192, 203, 255]); }); test('x exceeding image width produces no display', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,x=999;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); strictEqual(await getImageStorageLength(), 0); }); test('negative x/y values are clamped to 0', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,x=-10,y=-10;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); deepStrictEqual(await getOrigSize(1), [200, 100]); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 0, 0, 255]); }); test('combined crop and sub-cell offset', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,x=20,y=0,w=20,h=50,X=5,Y=3;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); deepStrictEqual(await getPixel(0, 0, 0, 0), [0, 0, 0, 0]); deepStrictEqual(await getPixel(0, 0, 4, 2), [0, 0, 0, 0]); deepStrictEqual(await getPixel(0, 0, 5, 3), [255, 128, 0, 255]); }); test('sub-cell offset with explicit c/r advances cursor correctly', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,X=5,Y=3,c=4,r=2;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); deepStrictEqual(await getCursor(), [4, 1]); }); }); test.describe('Query support', () => { test('responds with OK for valid 200x100 PNG query', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=600,a=q,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=600;OK\x1b\\'); }); test('query does not store the 200x100 image', async () => { await ctx.page.evaluate(() => { (window as any).term.onData(() => { /* consume response */ }); }); await ctx.proxy.write(`\x1b_Gi=601,a=q,f=100;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(601)`), false); }); }); test.describe('Delete commands', () => { test('delete removes 200x100 image by id', async () => { await ctx.proxy.write(`\x1b_Ga=t,f=100,i=700;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await timeout(200); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(700)`), true); await ctx.proxy.write(`\x1b_Ga=d,d=i,i=700\x1b\\`); await timeout(50); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(700)`), false); }); }); }); test.describe('Raw RGB pixel format (f=24)', () => { test.describe('Pixel verification', () => { test('renders 1x1 black pixel with alpha set to 255', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,s=1,v=1;${RAW_RGB_1X1_BLACK}\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [0, 0, 0, 255]); }); test('renders 1x1 red pixel with alpha set to 255', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,s=1,v=1;${RAW_RGB_1X1_RED}\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 0, 0, 255]); }); test('renders 3x1 strip (red, green, blue)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,s=3,v=1;${RAW_RGB_3X1}\x1b\\`); await timeout(100); const pixels = await getPixels(0, 0, 0, 0, 3, 1); deepStrictEqual(pixels?.slice(0, 4), [255, 0, 0, 255]); deepStrictEqual(pixels?.slice(4, 8), [0, 255, 0, 255]); deepStrictEqual(pixels?.slice(8, 12), [0, 0, 255, 255]); }); test('renders 2x2 grid with correct pixel layout', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,s=2,v=2;${RAW_RGB_2X2}\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 0, 0, 255]); deepStrictEqual(await getPixel(0, 0, 1, 0), [0, 255, 0, 255]); deepStrictEqual(await getPixel(0, 0, 0, 1), [0, 0, 255, 255]); deepStrictEqual(await getPixel(0, 0, 1, 1), [255, 255, 0, 255]); }); test('renders 5x1 row with block+remainder pixel layout', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,s=5,v=1;${RAW_RGB_5X1}\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 0, 0, 255]); deepStrictEqual(await getPixel(0, 0, 1, 0), [0, 255, 0, 255]); deepStrictEqual(await getPixel(0, 0, 2, 0), [0, 0, 255, 255]); deepStrictEqual(await getPixel(0, 0, 3, 0), [255, 255, 0, 255]); deepStrictEqual(await getPixel(0, 0, 4, 0), [255, 0, 255, 255]); }); test('renders 4x2 grid with multi-block pixel layout', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,s=4,v=2;${RAW_RGB_4X2}\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 0, 0, 255]); deepStrictEqual(await getPixel(0, 0, 1, 0), [0, 255, 0, 255]); deepStrictEqual(await getPixel(0, 0, 2, 0), [0, 0, 255, 255]); deepStrictEqual(await getPixel(0, 0, 3, 0), [255, 255, 0, 255]); deepStrictEqual(await getPixel(0, 0, 0, 1), [255, 0, 255, 255]); deepStrictEqual(await getPixel(0, 0, 1, 1), [0, 255, 255, 255]); deepStrictEqual(await getPixel(0, 0, 2, 1), [128, 128, 128, 255]); deepStrictEqual(await getPixel(0, 0, 3, 1), [255, 255, 255, 255]); }); }); test.describe('Storage and dimensions', () => { test('stores image with correct original dimensions (3x1)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,s=3,v=1;${RAW_RGB_3X1}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [3, 1]); }); test('stores image with correct original dimensions (2x2)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,s=2,v=2;${RAW_RGB_2X2}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [2, 2]); }); test('stores image with correct original dimensions (5x1)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,s=5,v=1;${RAW_RGB_5X1}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [5, 1]); }); test('stores image with correct original dimensions (4x2)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,s=4,v=2;${RAW_RGB_4X2}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [4, 2]); }); }); test.describe('Validation', () => { test('does not render without width (s=)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,v=1;${RAW_RGB_1X1_BLACK}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 0); }); test('does not render without height (v=)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,s=1;${RAW_RGB_1X1_BLACK}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 0); }); test('does not render without either dimension', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24;${RAW_RGB_1X1_BLACK}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 0); }); test('does not render with insufficient byte count', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=24,s=2,v=2;${RAW_RGB_1X1_BLACK}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 0); }); test('query returns EINVAL without dimensions', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=200,a=q,f=24;${RAW_RGB_1X1_BLACK}\x1b\\`); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=200;EINVAL:width and height required for raw pixel data\x1b\\'); }); test('query returns EINVAL for insufficient pixel data', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=201,a=q,f=24,s=2,v=2;${RAW_RGB_1X1_BLACK}\x1b\\`); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=201;EINVAL:insufficient pixel data\x1b\\'); }); test('query returns OK for valid RGB data with correct dimensions', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=202,a=q,f=24,s=1,v=1;${RAW_RGB_1X1_RED}\x1b\\`); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=202;OK\x1b\\'); }); }); }); test.describe('Raw RGBA pixel format (f=32)', () => { test.describe('Pixel verification', () => { test('renders 1x1 opaque white pixel', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32,s=1,v=1;${RAW_RGBA_1X1_WHITE}\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 255, 255, 255]); }); test('renders 1x1 opaque red pixel', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32,s=1,v=1;${RAW_RGBA_1X1_RED}\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 0, 0, 255]); }); test('preserves full transparency (alpha=0)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32,s=1,v=1;${RAW_RGBA_1X1_TRANSPARENT}\x1b\\`); await timeout(100); const pixel = await getPixel(0, 0, 0, 0); strictEqual(pixel?.[3], 0); }); test('renders 3x1 strip (red, green, blue opaque)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32,s=3,v=1;${RAW_RGBA_3X1}\x1b\\`); await timeout(100); const pixels = await getPixels(0, 0, 0, 0, 3, 1); deepStrictEqual(pixels?.slice(0, 4), [255, 0, 0, 255]); deepStrictEqual(pixels?.slice(4, 8), [0, 255, 0, 255]); deepStrictEqual(pixels?.slice(8, 12), [0, 0, 255, 255]); }); test('renders 2x2 grid with correct pixel layout', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32,s=2,v=2;${RAW_RGBA_2X2}\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 0, 0, 255]); deepStrictEqual(await getPixel(0, 0, 1, 0), [0, 255, 0, 255]); deepStrictEqual(await getPixel(0, 0, 0, 1), [0, 0, 255, 255]); deepStrictEqual(await getPixel(0, 0, 1, 1), [255, 255, 0, 255]); }); test('renders 5x1 row with zero-copy pixel layout', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32,s=5,v=1;${RAW_RGBA_5X1}\x1b\\`); await timeout(100); deepStrictEqual(await getPixel(0, 0, 0, 0), [255, 0, 0, 255]); deepStrictEqual(await getPixel(0, 0, 1, 0), [0, 255, 0, 255]); deepStrictEqual(await getPixel(0, 0, 2, 0), [0, 0, 255, 255]); deepStrictEqual(await getPixel(0, 0, 3, 0), [255, 255, 0, 255]); deepStrictEqual(await getPixel(0, 0, 4, 0), [255, 0, 255, 255]); }); }); test.describe('Storage and dimensions', () => { test('stores image with correct original dimensions (3x1)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32,s=3,v=1;${RAW_RGBA_3X1}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [3, 1]); }); test('stores image with correct original dimensions (2x2)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32,s=2,v=2;${RAW_RGBA_2X2}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [2, 2]); }); test('stores image with correct original dimensions (5x1)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32,s=5,v=1;${RAW_RGBA_5X1}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); deepStrictEqual(await getOrigSize(1), [5, 1]); }); }); test.describe('Validation', () => { test('does not render without width (s=)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32,v=1;${RAW_RGBA_1X1_RED}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 0); }); test('does not render without height (v=)', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32,s=1;${RAW_RGBA_1X1_RED}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 0); }); test('does not render without either dimension', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32;${RAW_RGBA_1X1_RED}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 0); }); test('does not render with insufficient byte count', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=32,s=2,v=2;${RAW_RGBA_1X1_RED}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 0); }); test('query returns EINVAL without dimensions', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=300,a=q,f=32;${RAW_RGBA_1X1_RED}\x1b\\`); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=300;EINVAL:width and height required for raw pixel data\x1b\\'); }); test('query returns EINVAL for insufficient pixel data', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=301,a=q,f=32,s=2,v=2;${RAW_RGBA_1X1_RED}\x1b\\`); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=301;EINVAL:insufficient pixel data\x1b\\'); }); test('query returns OK for valid RGBA data with correct dimensions', async () => { await ctx.page.evaluate(() => { (window as any).kittyResponse = ''; (window as any).term.onData((data: string) => { (window as any).kittyResponse = data; }); }); await ctx.proxy.write(`\x1b_Gi=302,a=q,f=32,s=1,v=1;${RAW_RGBA_1X1_RED}\x1b\\`); await timeout(100); const response = await ctx.page.evaluate('window.kittyResponse'); strictEqual(response, '\x1b_Gi=302;OK\x1b\\'); }); }); }); test.describe('Eviction and memory leak prevention', () => { test('re-transmit with same i= cleans up old storage entry', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,i=50;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(50)`), true); const oldStorageId = await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty')._kittyIdToStorageId.get(50)`); ok(oldStorageId !== undefined); await ctx.proxy.write(`\x1b_Ga=T,f=100,i=50;${KITTY_RGB_3X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(50)`), true); const newStorageId = await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty')._kittyIdToStorageId.get(50)`); ok(newStorageId !== undefined); ok(newStorageId !== oldStorageId); }); test('memory limit eviction cleans Kitty handler maps', async () => { // Resize terminal to fit 7 non-overlapping 200x100 images without scrolling. // Each image ≈ 29 cols × 8 rows at default cell size. await ctx.page.evaluate(` window.term.reset(); window.imageAddon?.dispose(); window.term.resize(80, 48); window.imageAddon = new ImageAddon({ storageLimit: 0.5 }); window.term.loadAddon(window.imageAddon); `); // storageLimit 0.5 MB = 125,000 pixels. Each 200x100 image = 20,000 pixels. // 6 images = 120K pixels (under limit). 7th triggers eviction (140K > 125K). // Place non-overlapping so tile-count eviction doesn't interfere. const positions = [[1, 1], [30, 1], [1, 9], [30, 9], [1, 17], [30, 17]]; for (let n = 0; n < 6; n++) { const [c, r] = positions[n]; const id = 60 + n; await ctx.proxy.write(`\x1b[${r};${c}H\x1b_Ga=T,f=100,i=${id},C=1;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); } await pollFor(ctx.page, 'window.imageAddon._storage._images.size', 6); // 7th image pushes total past 125K pixels — oldest evicted await ctx.proxy.write(`\x1b[25;1H\x1b_Ga=T,f=100,i=66,C=1;${KITTY_MULTICOLOR_200X100_BASE64}\x1b\\`); await pollFor(ctx.page, `window.imageAddon._handlers.get('kitty').images.has(60)`, false); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty')._kittyIdToStorageId.has(60)`), false); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(66)`), true); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty')._kittyIdToStorageId.has(66)`), true); // Restore terminal size await ctx.page.evaluate('window.term.resize(80, 24)'); }); test('scrollback eviction cleans Kitty handler maps', async () => { await ctx.page.evaluate(` window.term.reset(); window.imageAddon?.dispose(); window.imageAddon = new ImageAddon(); window.term.loadAddon(window.imageAddon); `); await ctx.proxy.write(`\x1b_Ga=T,f=100,i=70;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty').images.has(70)`), true); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty')._kittyIdToStorageId.has(70)`), true); // Scroll past scrollback + viewport to push image's marker off the buffer await ctx.page.evaluate(() => new Promise(res => { const term = (window as any).term; const amount: number = (term.options.scrollback as number) + (term.rows as number) + 10; term.write('\n'.repeat(amount), res); })); await pollFor(ctx.page, `window.imageAddon._handlers.get('kitty').images.has(70)`, false); strictEqual(await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty')._kittyIdToStorageId.has(70)`), false); }); test('re-transmit with a=t then a=T cleans old storage before display', async () => { await ctx.proxy.write(`\x1b_Ga=T,f=100,i=80;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await getImageStorageLength(), 1); const oldStorageId = await ctx.page.evaluate(`window.imageAddon._handlers.get('kitty')._kittyIdToStorageId.get(80)`); ok(oldStorageId !== undefined); await ctx.proxy.write(`\x1b_Ga=t,f=100,i=80;${KITTY_RGB_3X1_BASE64}\x1b\\`); await timeout(100); strictEqual(await ctx.page.evaluate(`window.imageAddon._storage._images.has(${oldStorageId})`), false); }); }); test.describe('onImageAdded callback', () => { test('onImageAdded fires for each kitty image', async () => { await ctx.page.evaluate(` window._imageAddedCount = 0; window.imageAddon.onImageAdded(() => { window._imageAddedCount++; }); `); await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`); await pollFor(ctx.page, 'window._imageAddedCount', 1); await ctx.proxy.write(`\x1b_Ga=T,f=100;${KITTY_RGB_3X1_BASE64}\x1b\\`); await pollFor(ctx.page, 'window._imageAddedCount', 2); }); }); }); /** * Helper functions */ async function getDimensions(): Promise { const dimensions: any = await ctx.page.evaluate(`term.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 ctx.page.evaluate('[window.term.buffer.active.cursorX, window.term.buffer.active.cursorY]'); } async function getImageStorageLength(): Promise { return ctx.page.evaluate('window.imageAddon._storage._images.size'); } async function getOrigSize(id: number): Promise<[number, number]> { return ctx.page.evaluate(`[ window.imageAddon._storage._images.get(${id}).orig.width, window.imageAddon._storage._images.get(${id}).orig.height ]`); } async function getPixel(col: number, row: number, x: number, y: number): Promise { return ctx.page.evaluate(([col, row, x, y]: number[]) => { const canvas = (window as any).imageAddon.getImageAtBufferCell(col, row); if (!canvas) return null; const ctx2d = canvas.getContext('2d'); if (!ctx2d) return null; return Array.from(ctx2d.getImageData(x, y, 1, 1).data); }, [col, row, x, y]); } async function getPixels(col: number, row: number, x: number, y: number, w: number, h: number): Promise { return ctx.page.evaluate(([col, row, x, y, w, h]: number[]) => { const canvas = (window as any).imageAddon.getImageAtBufferCell(col, row); if (!canvas) return null; const ctx2d = canvas.getContext('2d'); if (!ctx2d) return null; return Array.from(ctx2d.getImageData(x, y, w, h).data); }, [col, row, x, y, w, h]); } ================================================ FILE: addons/addon-image/test/playwright.config.ts ================================================ import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { testDir: '.', timeout: 10000, projects: [ { name: 'Chromium', use: { browserName: 'chromium' } }, { name: 'FirefoxStable', use: { browserName: 'firefox' } }, { name: 'WebKit', use: { browserName: 'webkit' } } ], reporter: 'list', webServer: { command: 'npm run start', port: 3000, timeout: 120000, reuseExistingServer: !process.env.CI } }; export default config; ================================================ FILE: addons/addon-image/test/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "ESNext", "lib": [ "es2021", ], "rootDir": ".", "outDir": "../out-test", "sourceMap": true, "removeComments": true, "paths": { "common/*": [ "../../../src/common/*" ], "browser/*": [ "../../../src/browser/*" ], "*": [ "./*" ] }, "strict": true, "types": [ "../../../node_modules/@types/node" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" }, { "path": "../../../test/playwright" } ] } ================================================ FILE: addons/addon-image/tsconfig.json ================================================ { "files": [], "include": [], "references": [ { "path": "./src" }, { "path": "./test" } ] } ================================================ FILE: addons/addon-image/typings/addon-image.d.ts ================================================ /** * Copyright (c) 2022 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal, ITerminalAddon, IEvent } from '@xterm/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; /** * Kitty graphics protocol settings */ /** Whether Kitty graphics protocol is enabled (default is true). */ kittySupport?: boolean; /** Kitty image size limit in bytes (default 20000000 bytes). */ kittySizeLimit?: 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; /** * Event fired whenever a new image is added to storage. */ public readonly onImageAdded: IEvent; /** * 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; } } ================================================ FILE: addons/addon-image/webpack.config.js ================================================ /** * Copyright (c) 2020 The xterm.js authors. All rights reserved. * @license MIT */ const path = require('path'); const addonName = 'ImageAddon'; const mainFile = '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/ } ] }, resolve: { modules: ['./node_modules'], extensions: [ '.js' ], alias: { common: path.resolve('../../out/common'), browser: path.resolve('../../out/browser'), vs: path.resolve('../../out/vs') } }, output: { filename: mainFile, path: path.resolve('./lib'), library: addonName, libraryTarget: 'umd', // Force usage of globalThis instead of global / self. (This is cross-env compatible) globalObject: 'globalThis', }, mode: 'production' }; module.exports = [addon]; ================================================ FILE: addons/addon-ligatures/.gitignore ================================================ node_modules/ .nyc_output/ coverage/ lib/ .env .vscode/ *.swp *.tgz npm-debug.log* ================================================ FILE: addons/addon-ligatures/.npmignore ================================================ # Blacklist - exclude everything except npm defaults such as LICENSE, etc * !*/ # Whitelist - lib/ !lib/**/*.d.ts !lib/**/*.js !lib/**/*.js.map !lib/**/*.mjs !lib/**/*.mjs.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 ================================================ FILE: addons/addon-ligatures/LICENSE ================================================ Copyright (c) 2019, 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. --- The code that analyzes font ligatures is forked from https://github.com/princjef/font-ligatures with this license: MIT License Copyright (c) 2018 Jeffrey Principe 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. ================================================ FILE: addons/addon-ligatures/README.md ================================================ ## @xterm/addon-ligatures Add support for programming ligatures to [xterm.js] when running in environments with access to [Node.js] APIs (such as [Electron]). ### Requirements * [Node.js] 8.x or higher (present in [Electron] 1.8.3 or higher) * [xterm.js] 4.0.0 or higher using the default canvas renderer ### Install ```bash npm install --save @xterm/addon-ligatures ``` ### Usage ```ts import { Terminal } from '@xterm/xterm'; import { LigaturesAddon } from '@xterm/addon-ligatures'; const terminal = new Terminal(); const ligaturesAddon = new LigaturesAddon(); terminal.open(containerElement); terminal.loadAddon(ligaturesAddon); ``` ### How It Works In a browser environment, font ligature information is read directly by the web browser and used to render text correctly without any intervention from the developer. As of version 3, xterm.js uses the canvas to render characters individually, resulting in a significant performance boost. However, this means that it can no longer lean on the browser to determine when to draw font ligatures. This package locates the font file on disk for the font currently in use by the terminal and parses the ligature information out of it (via the [font-ligatures] package). As text is rendered in xterm.js, this package annotates it with the locations of ligatures, allowing xterm.js to render it correctly. 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 [xterm.js]: https://github.com/xtermjs/xterm.js [Electron]: https://electronjs.org/ [Node.js]: https://nodejs.org/ [font-ligatures]: https://github.com/princjef/font-ligatures [Fira Code]: https://github.com/tonsky/FiraCode [Fira Code License]: https://github.com/tonsky/FiraCode/blob/master/LICENSE [Iosevka]: https://github.com/be5invis/Iosevka [Iosevka License]: https://github.com/be5invis/Iosevka/blob/master/LICENSE.md ================================================ FILE: addons/addon-ligatures/package.json ================================================ { "name": "@xterm/addon-ligatures", "version": "0.10.0", "description": "Add support for programming ligatures to xterm.js", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, "main": "lib/addon-ligatures.js", "module": "lib/addon-ligatures.mjs", "types": "typings/addon-ligatures.d.ts", "repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-ligatures", "engines": { "node": ">8.0.0" }, "scripts": { "build": "tsgo -p src", "watch": "tsgo -w -p src", "prepackage": "npm run build", "package": "webpack", "pretest": "npm run build", "test": "nyc mocha out/**/*.test.js", "prepublish": "npm run package" }, "keywords": [ "font", "ligature", "terminal", "xterm", "xterm.js" ], "license": "MIT", "dependencies": { "lru-cache": "^6.0.0", "opentype.js": "^0.8.0" }, "devDependencies": { "@types/lru-cache": "^5.1.0", "@types/opentype.js": "^0.7.0", "axios": "^1.6.0", "font-finder": "^1.1.0", "mkdirp": "0.5.5", "yauzl": "^3.2.1" } } ================================================ FILE: addons/addon-ligatures/src/LigaturesAddon.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import type { Terminal } from '@xterm/xterm'; import type { LigaturesAddon as ILigaturesApi } from '@xterm/addon-ligatures'; import { enableLigatures } from '.'; import { ILigatureOptions } from './Types'; export interface ITerminalAddon { activate(terminal: Terminal): void; dispose(): void; } export class LigaturesAddon implements ITerminalAddon, ILigaturesApi { private readonly _fallbackLigatures: string[]; private readonly _fontFeatureSettings?: string; private _terminal: Terminal | undefined; private _characterJoinerId: number | undefined; constructor(options?: Partial) { // Source: calt set from https://github.com/be5invis/Iosevka?tab=readme-ov-file#ligations this._fallbackLigatures = (options?.fallbackLigatures ?? [ '<--', '<---', '<<-', '<-', '->', '->>', '-->', '--->', '<==', '<===', '<<=', '<=', '=>', '=>>', '==>', '===>', '>=', '>>=', '<->', '<-->', '<--->', '<---->', '<=>', '<==>', '<===>', '<====>', '::', ':::', '<~~', '', '/>', '~~>', '==', '!=', '/=', '~=', '<>', '===', '!==', '!===', '<:', ':=', '*=', '*+', '<*', '<*>', '*>', '<|', '<|>', '|>', '+*', '=*', '=:', ':>', '/*', '*/', '+++', '', [1614, 1614, 1063], [[0, 3]]), fira('->>', [1614, 1614, 1065], [[0, 3]]), fira('>->', [1614, 1614, 1493], [[0, 3]]), fira('<=<', [1614, 1614, 1519], [[0, 3]]), fira('<<=', [1614, 1614, 1523], [[0, 3]]), fira('<==', [1614, 1614, 1517], [[0, 3]]), fira('<=>', [1614, 1614, 1518], [[0, 3]]), fira('=>', [1614, 1488], [[0, 2]]), fira('==>', [1614, 1614, 1487], [[0, 3]]), fira('=>>', [1614, 1614, 1489], [[0, 3]]), fira('>=>', [1614, 1614, 1495], [[0, 3]]), fira('>>=', [1614, 1614, 1498], [[0, 3]]), fira('>>-', [1614, 1614, 1497], [[0, 3]]), fira('>-', [1614, 1492], [[0, 2]]), fira('<~>', [1614, 1614, 1526], [[0, 3]]), fira('-<', [1614, 1066], [[0, 2]]), fira('-<<', [1614, 1614, 1067], [[0, 3]]), fira('=<<', [1614, 1614, 1490], [[0, 3]]), fira('<~~', [1614, 1614, 1527], [[0, 3]]), fira('<~', [1614, 1525], [[0, 2]]), fira('~~', [1614, 1534], [[0, 2]]), fira('~>', [1614, 1533], [[0, 2]]), fira('~~>', [1614, 1614, 1535], [[0, 3]]), fira('<<<', [1614, 1614, 1524], [[0, 3]]), fira('<<', [1614, 1521], [[0, 2]]), fira('<=', [1614, 1516], [[0, 2]]), fira('<>', [1614, 1520], [[0, 2]]), fira('>=', [1614, 1494], [[0, 2]]), fira('>>', [1614, 1496], [[0, 2]]), fira('>>>', [1614, 1614, 1499], [[0, 3]]), fira('{.', [1001, 977], [[0, 2]]), fira('{|', [1614, 1049], [[0, 2]]), fira('[|', [1614, 1050], [[0, 2]]), fira('<:', [1614, 1506], [[0, 2]]), fira(':>', [1614, 1056], [[0, 2]]), fira('|]', [1614, 1474], [[0, 2]]), fira('|}', [1614, 1473], [[0, 2]]), fira('.}', [977, 1002], [[0, 2]]), fira('<|||', [1614, 1614, 1614, 1504], [[0, 4]]), fira('<||', [1614, 1614, 1503], [[0, 3]]), fira('<|', [1614, 1502], [[0, 2]]), fira('<|>', [1614, 1614, 1505], [[0, 3]]), fira('|>', [1614, 1477], [[0, 2]]), fira('||>', [1614, 1614, 1472], [[0, 3]]), fira('|||>', [1614, 1614, 1614, 1470], [[0, 4]]), fira('<$', [1614, 1507], [[0, 2]]), fira('<$>', [1614, 1614, 1508], [[0, 3]]), fira('$>', [1614, 1479], [[0, 2]]), fira('<+', [1614, 1514], [[0, 2]]), fira('<+>', [1614, 1614, 1515], [[0, 3]]), fira('+>', [1614, 1482], [[0, 2]]), fira('<*', [1614, 1500], [[0, 2]]), fira('<*>', [1614, 1614, 1501], [[0, 3]]), fira('*>', [1614, 1047], [[0, 2]]), fira('/*', [1614, 1092], [[0, 2]]), fira('*/', [1614, 1048], [[0, 2]]), fira('///', [1614, 1614, 1097], [[0, 3]]), fira('//', [1614, 1096], [[0, 2]]), fira('', [1614, 1614, 1529], [[0, 3]]), fira('/>', [1614, 1095], [[0, 2]]), fira('0xff', [895, 270, 166, 166], [[0, 3]]), fira('10x10', [896, 895, 270, 896, 895], [[1, 4]]), fira('9:45', [904, 998, 899, 900], [[0, 2]]), fira('[:]', [1003, 998, 1004], [[0, 2]]), fira(';;', [1614, 1091], [[0, 2]]), fira('::', [1614, 1052], [[0, 2]]), fira(':::', [1614, 1614, 1053], [[0, 3]]), fira('..', [1614, 1082], [[0, 2]]), fira('...', [1614, 1614, 1085], [[0, 3]]), fira('..<', [1614, 1614, 1084], [[0, 3]]), fira('!!', [1614, 1057], [[0, 2]]), fira('??', [1614, 1090], [[0, 2]]), fira('%%', [1614, 1536], [[0, 2]]), fira('&&', [1614, 1468], [[0, 2]]), fira('||', [1614, 1469], [[0, 2]]), fira('?.', [1614, 1089], [[0, 2]]), fira('?:', [1614, 1087], [[0, 2]]), fira('++', [1614, 1480], [[0, 2]]), fira('+++', [1614, 1614, 1481], [[0, 3]]), fira('--', [1614, 1061], [[0, 2]]), fira('---', [1614, 1614, 1062], [[0, 3]]), fira('**', [1614, 1045], [[0, 2]]), fira('***', [1614, 1614, 1046], [[0, 3]]), fira('~=', [1614, 1532], [[0, 2]]), fira('~-', [1614, 1531], [[0, 2]]), fira('www', [1614, 1614, 271], [[0, 3]]), fira('-~', [1614, 1068], [[0, 2]]), fira('~@', [1614, 1530], [[0, 2]]), fira('^=', [1614, 1478], [[0, 2]]), fira('?=', [1614, 1088], [[0, 2]]), fira('/=', [1614, 1093], [[0, 2]]), fira('/==', [1614, 1614, 1094], [[0, 3]]), fira('-|', [1614, 1060], [[0, 2]]), fira('_|_', [1614, 1614, 1098], [[0, 3]]), fira('|-', [1614, 1475], [[0, 2]]), fira('|=', [1614, 1476], [[0, 2]]), fira('||=', [1614, 1614, 1471], [[0, 3]]), fira('#!', [1614, 1071], [[0, 2]]), fira('#=', [1614, 1075], [[0, 2]]), fira('##', [1614, 1072], [[0, 2]]), fira('###', [1614, 1614, 1073], [[0, 3]]), fira('####', [1614, 1614, 1614, 1074], [[0, 4]]), fira('#{', [1614, 1069], [[0, 2]]), fira('#[', [1614, 1070], [[0, 2]]), fira(']#', [1614, 1051], [[0, 2]]), fira('#(', [1614, 1076], [[0, 2]]), fira('#?', [1614, 1077], [[0, 2]]), fira('#_', [1614, 1078], [[0, 2]]), fira('#_(', [1614, 1614, 1079], [[0, 3]]), fira('::=', [1614, 1614, 1054], [[0, 3]]), fira('.?', [1614, 1086], [[0, 2]]), fira('===>', [1614, 1614, 1486, 1148], [[0, 4]]) ]; const iosevkaCases: ITestCase[] = [ iosevka('<-', [31, 3127], [[0, 2]]), iosevka('<--', [31, 3129, 3139], [[0, 3]]), iosevka('<---', [31, 3129, 3150, 3139], [[0, 4]]), iosevka('<-----', [31, 3129, 3150, 3139, 3151, 3151], [[0, 6]]), iosevka('->', [3126, 33], [[0, 2]]), iosevka('-->', [3140, 3128, 33], [[0, 3]]), iosevka('--->', [3140, 3150, 3128, 33], [[0, 4]]), iosevka('----->', [3153, 3153, 3140, 3150, 3128, 33], [[0, 6]]), iosevka('<->', [31, 3149, 33], [[0, 3]]), iosevka('<-->', [31, 3129, 3128, 33], [[0, 4]]), iosevka('<--->', [31, 3129, 3150, 3128, 33], [[0, 5]]), iosevka('<----->', [31, 3129, 3150, 3150, 3150, 3128, 33], [[0, 7]]), iosevka('<=', [3094, 3095], [[0, 2]]), iosevka('<==', [31, 3158, 3168], [[0, 3]]), iosevka('<===', [31, 3158, 3179, 3168], [[0, 4]]), iosevka('<=====', [31, 3158, 3179, 3168, 3180, 3180], [[0, 6]]), iosevka('=>', [3155, 33], [[0, 2]]), iosevka('==>', [3169, 3157, 33], [[0, 3]]), iosevka('===>', [3169, 3179, 3157, 33], [[0, 4]]), iosevka('=====>', [3182, 3182, 3169, 3179, 3157, 33], [[0, 6]]), iosevka('<=>', [31, 3178, 33], [[0, 3]]), iosevka('<==>', [31, 3158, 3157, 33], [[0, 4]]), iosevka('<===>', [31, 3158, 3179, 3157, 33], [[0, 5]]), iosevka('<=====>', [31, 3158, 3179, 3179, 3179, 3157, 33], [[0, 7]]), iosevka('', [779, 779, 628], [[0, 3]]), monoid('<--', [776, 776, 627], [[0, 3]]), monoid('->>', [780, 780, 626], [[0, 3]]), monoid('<<-', [777, 777, 625], [[0, 3]]), monoid('->', [781, 623], [[0, 2]]), monoid('<-', [778, 624], [[0, 2]]), monoid('=>', [793, 666], [[0, 2]]), monoid('<=>', [785, 785, 760], [[0, 3]]), monoid('<==>', [786, 786, 786, 771], [[0, 4]]), monoid('==>', [787, 787, 672], [[0, 3]]), monoid('<==', [788, 788, 671], [[0, 3]]), monoid('>>=', [791, 791, 758], [[0, 3]]), monoid('=<<', [792, 792, 759], [[0, 3]]), monoid('--', [667, 667], [[0, 2]]), monoid(':=', [29, 761], [[0, 2]]), monoid('=:=', [789, 789, 665], [[0, 3]]), monoid('==', [794, 641], [[0, 2]]), monoid('!==', [782, 782, 646], [[0, 3]]), monoid('!=', [783, 629], [[0, 2]]), monoid('<=', [790, 630], [[0, 2]]), monoid('>=', [792, 631], [[0, 2]]), monoid('//', [621, 664], [[0, 2]]), monoid('/**', [18, 753, 753], [[0, 3]]), monoid('/*', [18, 753], [[0, 2]]), monoid('*/', [754, 18], [[0, 2]]), monoid('&&', [633, 775], [[0, 2]]), monoid('.&', [17, 755], [[0, 2]]), monoid('||', [634, 635], [[0, 2]]), monoid('!!', [769, 770], [[0, 2]]), monoid('::', [772, 773], [[0, 2]]), monoid('>>', [637, 638], [[0, 2]]), monoid('<<', [639, 640], [[0, 2]]), monoid('¯\\_(ツ)_/¯', [113, 765, 66, 767, 613, 768, 66, 766, 113], [[0, 3], [3, 6], [6, 9]]), monoid('__', [763, 764], [[0, 2]]) ]; const ubuntuCases: ITestCase[] = [ ubuntu('==>', [32, 32, 33], []) ]; const fontPaths: Record = { 'Fira Code': path.join(__dirname, '../../fonts/FiraCode-Regular.otf'), 'Iosevka': path.join(__dirname, '../../fonts/iosevka-regular.ttf'), 'Monoid': path.join(__dirname, '../../fonts/Monoid-Regular.ttf'), 'Ubuntu Mono': path.join(__dirname, '../../fonts/UbuntuMono-Regular.ttf') }; const fontCache: Map = new Map(); function loadFont(fontName: string): IFont { let font = fontCache.get(fontName); if (!font) { const fontPath = fontPaths[fontName]; const buffer = fs.readFileSync(fontPath); font = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)); fontCache.set(fontName, font); } return font; } describe('addon-ligatures - index', () => { describe('findLigatures', () => { for (const { font: fontName, input, glyphs, ranges } of [...firaCases, ...iosevkaCases, ...monoidCases, ...ubuntuCases]) { it(`${fontName}: '${input}'`, () => { const font = loadFont(fontName); const result = font.findLigatures(input); assert.deepEqual(result.outputGlyphs, glyphs); assert.deepEqual(result.contextRanges, ranges); }); } }); describe('findLigatureRanges', () => { for (const { font: fontName, input, ranges } of [...firaCases, ...iosevkaCases, ...monoidCases, ...ubuntuCases]) { it(`${fontName}: '${input}'`, () => { const font = loadFont(fontName); const result = font.findLigatureRanges(input); assert.deepEqual(result, ranges); }); } }); describe('caching', () => { it('findLigatures caches successive calls correctly', () => { const fontPath = fontPaths['Fira Code']; const buffer = fs.readFileSync(fontPath); const font = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength), { cacheSize: 100 }); const result1 = font.findLigatures('in --> out'); const result2 = font.findLigatures('in --> out'); assert.deepEqual(result1, result2); }); it('findLigatureRanges caches successive calls correctly', () => { const fontPath = fontPaths['Fira Code']; const buffer = fs.readFileSync(fontPath); const font = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength), { cacheSize: 100 }); const result1 = font.findLigatureRanges('in --> out'); const result2 = font.findLigatureRanges('in --> out'); assert.deepEqual(result1, result2); }); it('caches calls to findLigatures after findLigatureRanges correctly', () => { const fontPath = fontPaths['Fira Code']; const buffer = fs.readFileSync(fontPath); const uncached = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)); const uncachedResult1 = uncached.findLigatureRanges('in --> out'); const uncachedResult2 = uncached.findLigatures('in --> out'); const font = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength), { cacheSize: 100 }); const result1 = font.findLigatureRanges('in --> out'); const result2 = font.findLigatures('in --> out'); assert.deepEqual(result1, uncachedResult1); assert.deepEqual(result2, uncachedResult2); assert.deepEqual(result1, result2.contextRanges); }); it('caches calls to findLigatureRanges after findLigatures correctly', () => { const fontPath = fontPaths['Fira Code']; const buffer = fs.readFileSync(fontPath); const uncached = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)); const uncachedResult1 = uncached.findLigatures('in --> out'); const uncachedResult2 = uncached.findLigatureRanges('in --> out'); const font = loadBuffer(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength), { cacheSize: 100 }); const result1 = font.findLigatures('in --> out'); const result2 = font.findLigatureRanges('in --> out'); assert.deepEqual(result1, uncachedResult1); assert.deepEqual(result2, uncachedResult2); assert.deepEqual(result1.contextRanges, result2); }); }); }); ================================================ FILE: addons/addon-ligatures/src/fontLigatures/index.ts ================================================ import * as opentype from 'opentype.js'; import LRUCache = require('lru-cache'); import { IFont, ILigatureData, IFlattenedLookupTree, ILookupTree, IOptions } from './types'; import mergeTrees from './merge'; import walkTree from './walk'; import mergeRange from './mergeRange'; import buildTreeGsubType6Format1 from './processors/6-1'; import buildTreeGsubType6Format2 from './processors/6-2'; import buildTreeGsubType6Format3 from './processors/6-3'; import buildTreeGsubType8Format1 from './processors/8-1'; import flatten from './flatten'; class FontImpl implements IFont { private _font: opentype.Font; private _lookupTrees: { tree: IFlattenedLookupTree, processForward: boolean }[] = []; private _glyphLookups: { [glyphId: string]: number[] } = {}; private _cache?: LRUCache; constructor(font: opentype.Font, options: Required) { this._font = font; if (options.cacheSize > 0) { this._cache = new LRUCache({ max: options.cacheSize, length: ((val: ILigatureData | [number, number][], key: string) => key.length) as any }); } const caltFeatures = this._font.tables.gsub && this._font.tables.gsub.features.filter((f: { tag: string }) => f.tag === 'calt') || []; const lookupIndices: number[] = caltFeatures .reduce((acc: number[], val: { feature: { lookupListIndexes: number[] } }) => [...acc, ...val.feature.lookupListIndexes], []); const allLookups = this._font.tables.gsub && this._font.tables.gsub.lookups || []; const lookupGroups = allLookups.filter((l: unknown, i: number) => lookupIndices.some(idx => idx === i)); for (const [index, lookup] of lookupGroups.entries()) { const trees: ILookupTree[] = []; switch (lookup.lookupType) { case 6: for (const [index, table] of lookup.subtables.entries()) { switch (table.substFormat) { case 1: trees.push(buildTreeGsubType6Format1(table, allLookups, index)); break; case 2: trees.push(buildTreeGsubType6Format2(table, allLookups, index)); break; case 3: trees.push(buildTreeGsubType6Format3(table, allLookups, index)); break; } } break; case 8: for (const [index, table] of lookup.subtables.entries()) { trees.push(buildTreeGsubType8Format1(table, index)); } break; } const tree = flatten(mergeTrees(trees)); this._lookupTrees.push({ tree, processForward: lookup.lookupType !== 8 }); for (const glyphId of Object.keys(tree)) { if (!this._glyphLookups[glyphId]) { this._glyphLookups[glyphId] = []; } this._glyphLookups[glyphId].push(index); } } } public findLigatures(text: string): ILigatureData { const cached = this._cache && this._cache.get(text); if (cached && !Array.isArray(cached)) { return cached; } const glyphIds: number[] = []; for (const char of text) { glyphIds.push(this._font.charToGlyphIndex(char)); } // If there are no lookup groups, there's no point looking for // replacements. This gives us a minor performance boost for fonts with // no ligatures if (this._lookupTrees.length === 0) { return { inputGlyphs: glyphIds, outputGlyphs: glyphIds, contextRanges: [] }; } const result = this._findInternal(glyphIds.slice()); const finalResult: ILigatureData = { inputGlyphs: glyphIds, outputGlyphs: result.sequence, contextRanges: result.ranges }; if (this._cache) { this._cache.set(text, finalResult); } return finalResult; } public findLigatureRanges(text: string): [number, number][] { // Short circuit the process if there are no possible ligatures in the // font if (this._lookupTrees.length === 0) { return []; } const cached = this._cache && this._cache.get(text); if (cached) { return Array.isArray(cached) ? cached : cached.contextRanges; } const glyphIds: number[] = []; for (const char of text) { glyphIds.push(this._font.charToGlyphIndex(char)); } const result = this._findInternal(glyphIds); if (this._cache) { this._cache.set(text, result.ranges); } return result.ranges; } private _findInternal(sequence: number[]): { sequence: number[], ranges: [number, number][] } { const ranges: [number, number][] = []; let nextLookup = this._getNextLookup(sequence, 0); while (nextLookup.index !== null) { const lookup = this._lookupTrees[nextLookup.index]; if (lookup.processForward) { let lastGlyphIndex = nextLookup.last; for (let i = nextLookup.first; i < lastGlyphIndex; i++) { const result = walkTree(lookup.tree, sequence, i, i); if (result) { for (let j = 0; j < result.substitutions.length; j++) { const sub = result.substitutions[j]; if (sub !== null) { sequence[i + j] = sub; } } mergeRange( ranges, result.contextRange[0] + i, result.contextRange[1] + i ); // Substitutions can end up extending the search range if (i + result.length >= lastGlyphIndex) { lastGlyphIndex = i + result.length + 1; } i += result.length - 1; } } } else { // We don't need to do the lastGlyphIndex tracking here because // reverse processing isn't allowed to replace more than one // character at a time. for (let i = nextLookup.last - 1; i >= nextLookup.first; i--) { const result = walkTree(lookup.tree, sequence, i, i); if (result) { for (let j = 0; j < result.substitutions.length; j++) { const sub = result.substitutions[j]; if (sub !== null) { sequence[i + j] = sub; } } mergeRange( ranges, result.contextRange[0] + i, result.contextRange[1] + i ); i -= result.length - 1; } } } nextLookup = this._getNextLookup(sequence, nextLookup.index + 1); } return { sequence, ranges }; } /** * Returns the lookup and glyph range for the first lookup that might * contain a match. * * @param sequence Input glyph sequence * @param start The first input to try */ private _getNextLookup(sequence: number[], start: number): { index: number | null, first: number, last: number } { const result: { index: number | null, first: number, last: number } = { index: null, first: Infinity, last: -1 }; // Loop through each glyph and find the first valid lookup for it for (let i = 0; i < sequence.length; i++) { const lookups = this._glyphLookups[sequence[i]]; if (!lookups) { continue; } for (let j = 0; j < lookups.length; j++) { const lookupIndex = lookups[j]; if (lookupIndex >= start) { // Update the lookup information if it's the one we're // storing or earlier than it. if (result.index === null || lookupIndex <= result.index) { result.index = lookupIndex; if (result.first > i) { result.first = i; } result.last = i + 1; } break; } } } return result; } } /** * Load the font from it's binary data. The returned value can be used to find * ligatures for the font. * * @param buffer ArrayBuffer of the font to load */ export function loadBuffer(buffer: ArrayBuffer, options?: IOptions): IFont { const font = opentype.parse(buffer); return new FontImpl(font, { cacheSize: 0, ...options }); } export { IFont as Font, ILigatureData as LigatureData, IOptions as Options }; ================================================ FILE: addons/addon-ligatures/src/fontLigatures/merge.test.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import mergeTrees from './merge'; interface ILookupResult { contextRange: [number, number]; index: number; subIndex: number; length: number; substitutions: number[]; } function lookup(substitutionGlyph: number, index?: number, subIndex?: number): ILookupResult { return { contextRange: [0, 1], index: index || 0, subIndex: subIndex || 0, length: 1, substitutions: [substitutionGlyph] }; } describe('addon-ligatures - merge', () => { describe('mergeTrees', () => { it('combines disjoint trees', () => { const result = mergeTrees([ { individual: { '1': { lookup: lookup(1) } }, range: [] }, { individual: {}, range: [{ entry: { lookup: lookup(2) }, range: [2, 4] }] }, { individual: { '5': { lookup: lookup(3) } }, range: [] }, { individual: {}, range: [{ entry: { lookup: lookup(4) }, range: [8, 10] }] } ]); assert.deepEqual(result, { individual: { '1': { lookup: lookup(1) }, '5': { lookup: lookup(3) } }, range: [{ entry: { lookup: lookup(2) }, range: [2, 4] }, { entry: { lookup: lookup(4) }, range: [8, 10] }] }); }); it('merges matching individual glyphs', () => { const result = mergeTrees([ { individual: { '1': { lookup: lookup(1, 1) } }, range: [] }, { individual: { '1': { lookup: lookup(2, 0) } }, range: [] }, { individual: { '1': { lookup: lookup(3, 2) } }, range: [] } ]); assert.deepEqual(result, { individual: { '1': { lookup: lookup(2, 0) } }, range: [] }); }); it('merges range glyphs overlapping individual glyphs', () => { const result = mergeTrees([ { individual: { '1': { lookup: lookup(1, 0) } }, range: [] }, { individual: {}, range: [{ entry: { lookup: lookup(2, 1) }, range: [0, 4] }] } ]); assert.deepEqual(result, { individual: { '0': { lookup: lookup(2, 1) }, '1': { lookup: lookup(1, 0) } }, range: [{ entry: { lookup: lookup(2, 1) }, range: [2, 4] }] }); }); it('merges individual glyphs overlapping range glyphs', () => { const result = mergeTrees([ { individual: {}, range: [{ entry: { lookup: lookup(2, 1) }, range: [0, 4] }] }, { individual: { '1': { lookup: lookup(1, 0) } }, range: [] } ]); assert.deepEqual(result, { individual: { '0': { lookup: lookup(2, 1) }, '1': { lookup: lookup(1, 0) } }, range: [{ entry: { lookup: lookup(2, 1) }, range: [2, 4] }] }); }); it('merges multiple overlapping ranges', () => { const result = mergeTrees([ { individual: {}, range: [{ entry: { lookup: lookup(1, 2) }, range: [0, 3] }, { entry: { lookup: lookup(2, 1) }, range: [6, 12] }, { entry: { lookup: lookup(5, 3) }, range: [15, 20] }, { entry: { lookup: lookup(7, 4) }, range: [20, 22] }] }, { individual: {}, range: [{ entry: { lookup: lookup(3, 0) }, range: [2, 8] }, { entry: { lookup: lookup(4, 0) }, range: [10, 13] }, { entry: { lookup: lookup(6, 0) }, range: [16, 21] }] } ]); assert.deepEqual(result, { individual: { '2': { lookup: lookup(3, 0) }, '12': { lookup: lookup(4, 0) }, '15': { lookup: lookup(5, 3) }, '20': { lookup: lookup(6, 0) }, '21': { lookup: lookup(7, 4) } }, range: [{ entry: { lookup: lookup(1, 2) }, range: [0, 2] }, { entry: { lookup: lookup(3, 0) }, range: [6, 8] }, { entry: { lookup: lookup(3, 0) }, range: [3, 6] }, { entry: { lookup: lookup(2, 1) }, range: [8, 10] }, { entry: { lookup: lookup(4, 0) }, range: [10, 12] }, { entry: { lookup: lookup(6, 0) }, range: [16, 20] }] }); }); }); }); ================================================ FILE: addons/addon-ligatures/src/fontLigatures/merge.ts ================================================ import { ILookupTree, ILookupTreeEntry } from './types'; /** * Merges the provided trees into a single lookup tree. When conflicting lookups * are encountered between two trees, the one with the lower index, then the * lower subindex is chosen. * * @param trees Array of trees to merge. Entries in earlier trees are favored * over those in later trees when there is a choice. */ export default function mergeTrees(trees: ILookupTree[]): ILookupTree { const result: ILookupTree = { individual: {}, range: [] }; const mergedEntries = new WeakMap>(); for (const tree of trees) { mergeSubtree(result, tree, mergedEntries); } return result; } /** * Recursively merges the data for the mergeTree into the mainTree. * * @param mainTree The tree where the values should be merged * @param mergeTree The tree to be merged into the mainTree * @param mergedEntries WeakMap to track already merged entry pairs */ function mergeSubtree(mainTree: ILookupTree, mergeTree: ILookupTree, mergedEntries: WeakMap>): void { // Need to fix this recursively (and handle lookups) for (const [glyphId, value] of Object.entries(mergeTree.individual)) { // The main tree is guaranteed to have no overlaps between the // individual and range values, so if we match an invididual, there // must not be a range if (mainTree.individual[glyphId]) { mergeTreeEntry(mainTree.individual[glyphId], value, mergedEntries); } else { let matched = false; for (const [index, { range, entry }] of mainTree.range.entries()) { const overlap = getIndividualOverlap(Number(glyphId), range); // Don't overlap if (overlap.both === null) { continue; } matched = true; // If they overlap, we have to split the range and then // merge the overlap mainTree.individual[glyphId] = value; mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry), mergedEntries); // When there's an overlap, we also have to fix up the range // that we had already processed mainTree.range.splice(index, 1); for (const glyph of overlap.second) { if (Array.isArray(glyph)) { mainTree.range.push({ range: glyph, entry: cloneEntry(entry) }); } else { mainTree.individual[glyph] = cloneEntry(entry); } } } if (!matched) { mainTree.individual[glyphId] = value; } } } for (const { range, entry } of mergeTree.range) { // Ranges are more complicated, because they can overlap with // multiple things, individual and range alike. We start by // eliminating ranges that are already present in another range let remainingRanges: (number | [number, number])[] = [range]; for (let index = 0; index < mainTree.range.length; index++) { const { range, entry: resultEntry } = mainTree.range[index]; for (const [remainingIndex, remainingRange] of remainingRanges.entries()) { if (Array.isArray(remainingRange)) { const overlap = getRangeOverlap(remainingRange, range); if (overlap.both === null) { continue; } mainTree.range.splice(index, 1); index--; const entryToMerge: ILookupTreeEntry = cloneEntry(resultEntry); if (Array.isArray(overlap.both)) { mainTree.range.push({ range: overlap.both, entry: entryToMerge }); } else { mainTree.individual[overlap.both] = entryToMerge; } mergeTreeEntry(entryToMerge, cloneEntry(entry), mergedEntries); for (const second of overlap.second) { if (Array.isArray(second)) { mainTree.range.push({ range: second, entry: cloneEntry(resultEntry) }); } else { mainTree.individual[second] = cloneEntry(resultEntry); } } remainingRanges = overlap.first; } else { const overlap = getIndividualOverlap(remainingRange, range); if (overlap.both === null) { continue; } // If they overlap, we have to split the range and then // merge the overlap mainTree.individual[remainingRange] = cloneEntry(entry); mergeTreeEntry(mainTree.individual[remainingRange], cloneEntry(resultEntry), mergedEntries); // When there's an overlap, we also have to fix up the range // that we had already processed mainTree.range.splice(index, 1); index--; for (const glyph of overlap.second) { if (Array.isArray(glyph)) { mainTree.range.push({ range: glyph, entry: cloneEntry(resultEntry) }); } else { mainTree.individual[glyph] = cloneEntry(resultEntry); } } remainingRanges.splice(remainingIndex, 1, ...overlap.first); break; } } } // Next, we run the same against any individual glyphs for (const glyphId of Object.keys(mainTree.individual)) { for (const [remainingIndex, remainingRange] of remainingRanges.entries()) { if (Array.isArray(remainingRange)) { const overlap = getIndividualOverlap(Number(glyphId), remainingRange); if (overlap.both === null) { continue; } // If they overlap, we have to merge the overlap mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry), mergedEntries); // Update the remaining ranges remainingRanges.splice(remainingIndex, 1, ...overlap.second); break; } else { if (Number(glyphId) === remainingRange) { mergeTreeEntry(mainTree.individual[glyphId], cloneEntry(entry), mergedEntries); break; } } } } // Any remaining ranges should just be added directly for (const remainingRange of remainingRanges) { if (Array.isArray(remainingRange)) { mainTree.range.push({ range: remainingRange, entry: cloneEntry(entry) }); } else { mainTree.individual[remainingRange] = cloneEntry(entry); } } } } /** * Recursively merges the entry forr the mergeTree into the mainTree * * @param mainTree The entry where the values should be merged * @param mergeTree The entry to merge into the mainTree * @param mergedEntries WeakMap to track already merged entry pairs */ function mergeTreeEntry(mainTree: ILookupTreeEntry, mergeTree: ILookupTreeEntry, mergedEntries: WeakMap>): void { // Check if we've already merged this pair let mergedSet = mergedEntries.get(mainTree); if (mergedSet?.has(mergeTree)) { return; } if (!mergedSet) { mergedSet = new Set(); mergedEntries.set(mainTree, mergedSet); } mergedSet.add(mergeTree); if ( mergeTree.lookup && ( !mainTree.lookup || mainTree.lookup.index > mergeTree.lookup.index || (mainTree.lookup.index === mergeTree.lookup.index && mainTree.lookup.subIndex > mergeTree.lookup.subIndex) ) ) { mainTree.lookup = mergeTree.lookup; } if (mergeTree.forward) { if (!mainTree.forward) { mainTree.forward = mergeTree.forward; } else { mergeSubtree(mainTree.forward, mergeTree.forward, mergedEntries); } } if (mergeTree.reverse) { if (!mainTree.reverse) { mainTree.reverse = mergeTree.reverse; } else { mergeSubtree(mainTree.reverse, mergeTree.reverse, mergedEntries); } } } interface IOverlap { first: (number | [number, number])[]; second: (number | [number, number])[]; both: number | [number, number] | null; } /** * Determines the overlap (if any) between two ranges. Returns the distinct * ranges for each range and the overlap (if any). * * @param first First range * @param second Second range */ function getRangeOverlap(first: [number, number], second: [number, number]): IOverlap { const result: IOverlap = { first: [], second: [], both: null }; // Both if (first[0] < second[1] && second[0] < first[1]) { const start = Math.max(first[0], second[0]); const end = Math.min(first[1], second[1]); result.both = rangeOrIndividual(start, end); } // Before if (first[0] < second[0]) { const start = first[0]; const end = Math.min(second[0], first[1]); result.first.push(rangeOrIndividual(start, end)); } else if (second[0] < first[0]) { const start = second[0]; const end = Math.min(second[1], first[0]); result.second.push(rangeOrIndividual(start, end)); } // After if (first[1] > second[1]) { const start = Math.max(first[0], second[1]); const end = first[1]; result.first.push(rangeOrIndividual(start, end)); } else if (second[1] > first[1]) { const start = Math.max(first[1], second[0]); const end = second[1]; result.second.push(rangeOrIndividual(start, end)); } return result; } /** * Determines the overlap (if any) between the individual glyph and the range * provided. Returns the glyphs and/or ranges that are unique to each provided * and the overlap (if any). * * @param first Individual glyph * @param second Range */ function getIndividualOverlap(first: number, second: [number, number]): IOverlap { // Disjoint if (first < second[0] || first > second[1]) { return { first: [first], second: [second], both: null }; } const result: IOverlap = { first: [], second: [], both: first }; if (second[0] < first) { result.second.push(rangeOrIndividual(second[0], first)); } if (second[1] > first) { result.second.push(rangeOrIndividual(first + 1, second[1])); } return result; } /** * Returns an individual glyph if the range is of size one or a range if it is * larger. * * @param start Beginning of the range (inclusive) * @param end End of the range (exclusive) */ function rangeOrIndividual(start: number, end: number): number | [number, number] { if (end - start === 1) { return start; } return [start, end]; } /** * Clones an individual lookup tree entry. * * @param entry Lookup tree entry to clone * @param visited Map to track already cloned entries (prevents infinite loops) */ function cloneEntry(entry: ILookupTreeEntry, visited: Map = new Map()): ILookupTreeEntry { if (visited.has(entry)) { return visited.get(entry)!; } const result: ILookupTreeEntry = {}; visited.set(entry, result); if (entry.forward) { result.forward = cloneTree(entry.forward, visited); } if (entry.reverse) { result.reverse = cloneTree(entry.reverse, visited); } if (entry.lookup) { result.lookup = { contextRange: entry.lookup.contextRange.slice() as [number, number], index: entry.lookup.index, length: entry.lookup.length, subIndex: entry.lookup.subIndex, substitutions: entry.lookup.substitutions.slice() }; } return result; } /** * Clones a lookup tree. * * @param tree Lookup tree to clone * @param visited Map to track already cloned entries (prevents infinite loops) */ function cloneTree(tree: ILookupTree, visited: Map = new Map()): ILookupTree { const individual: { [glyphId: string]: ILookupTreeEntry } = {}; for (const [glyphId, entry] of Object.entries(tree.individual)) { individual[glyphId] = cloneEntry(entry, visited); } return { individual, range: tree.range.map(({ range, entry }) => ({ range: range.slice() as [number, number], entry: cloneEntry(entry, visited) })) }; } ================================================ FILE: addons/addon-ligatures/src/fontLigatures/mergeRange.test.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import mergeRange from './mergeRange'; describe('addon-ligatures - mergeRange', () => { it('inserts a new range before the existing ones', () => { const result = mergeRange([[1, 2], [2, 3]], 0, 1); assert.deepEqual(result, [[0, 1], [1, 2], [2, 3]]); }); it('inserts in between two ranges', () => { const result = mergeRange([[0, 2], [4, 6]], 2, 4); assert.deepEqual(result, [[0, 2], [2, 4], [4, 6]]); }); it('inserts after the last range', () => { const result = mergeRange([[0, 2], [4, 6]], 6, 8); assert.deepEqual(result, [[0, 2], [4, 6], [6, 8]]); }); it('extends the beginning of a range', () => { const result = mergeRange([[0, 2], [4, 6]], 3, 5); assert.deepEqual(result, [[0, 2], [3, 6]]); }); it('extends the end of a range', () => { const result = mergeRange([[0, 2], [4, 6]], 1, 4); assert.deepEqual(result, [[0, 4], [4, 6]]); }); it('extends the last range', () => { const result = mergeRange([[0, 2], [4, 6]], 5, 7); assert.deepEqual(result, [[0, 2], [4, 7]]); }); it('connects two ranges', () => { const result = mergeRange([[0, 2], [4, 6]], 1, 5); assert.deepEqual(result, [[0, 6]]); }); it('connects more than two ranges', () => { const result = mergeRange([[0, 2], [4, 6], [8, 10], [12, 14]], 1, 10); assert.deepEqual(result, [[0, 10], [12, 14]]); }); }); ================================================ FILE: addons/addon-ligatures/src/fontLigatures/mergeRange.ts ================================================ /** * Merges the range defined by the provided start and end into the list of * existing ranges. The merge is done in place on the existing range for * performance and is also returned. * * @param ranges Existing range list * @param newRangeStart Start position of the range to merge, inclusive * @param newRangeEnd End position of range to merge, exclusive */ export default function mergeRange(ranges: [number, number][], newRangeStart: number, newRangeEnd: number): [number, number][] { let inRange = false; for (let i = 0; i < ranges.length; i++) { const range = ranges[i]; if (!inRange) { if (newRangeEnd <= range[0]) { // Case 1: New range is before the search range ranges.splice(i, 0, [newRangeStart, newRangeEnd]); return ranges; } if (newRangeEnd <= range[1]) { // Case 2: New range is either wholly contained within the // search range or overlaps with the front of it range[0] = Math.min(newRangeStart, range[0]); return ranges; } if (newRangeStart < range[1]) { // Case 3: New range either wholly contains the search range // or overlaps with the end of it range[0] = Math.min(newRangeStart, range[0]); inRange = true; } else { // Case 4: New range starts after the search range continue; } } else { if (newRangeEnd <= range[0]) { // Case 5: New range extends from previous range but doesn't // reach the current one ranges[i - 1][1] = newRangeEnd; return ranges; } if (newRangeEnd <= range[1]) { // Case 6: New range extends from prvious range into the // current range ranges[i - 1][1] = Math.max(newRangeEnd, range[1]); ranges.splice(i, 1); inRange = false; return ranges; } // Case 7: New range extends from previous range past the // end of the current range ranges.splice(i, 1); i--; } } if (inRange) { // Case 8: New range extends past the last existing range ranges[ranges.length - 1][1] = newRangeEnd; } else { // Case 9: New range starts after the last existing range ranges.push([newRangeStart, newRangeEnd]); } return ranges; } ================================================ FILE: addons/addon-ligatures/src/fontLigatures/processors/6-1.ts ================================================ import { ChainingContextualSubstitutionTable, Lookup } from '../tables'; import { ILookupTree } from '../types'; import { listGlyphsByIndex } from './coverage'; import { processInputPosition, processLookaheadPosition, processBacktrackPosition, getInputTree, IEntryMeta } from './helper'; /** * Build lookup tree for GSUB lookup table 6, format 1. * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#61-chaining-context-substitution-format-1-simple-glyph-contexts * * @param table JSON representation of the table * @param lookups List of lookup tables * @param tableIndex Index of this table in the overall lookup */ export default function buildTree(table: ChainingContextualSubstitutionTable.IFormat1, lookups: Lookup[], tableIndex: number): ILookupTree { const result: ILookupTree = { individual: {}, range: [] }; const firstGlyphs = listGlyphsByIndex(table.coverage); for (const { glyphId, index } of firstGlyphs) { const chainRuleSet = table.chainRuleSets[index]; // If the chain rule set is null there's nothing to do with this table. if (!chainRuleSet) { continue; } for (const [subIndex, subTable] of chainRuleSet.entries()) { let currentEntries: IEntryMeta[] = getInputTree( result, subTable.lookupRecords, lookups, 0, glyphId ).map(({ entry, substitution }) => ({ entry, substitutions: [substitution] })); // We walk forward, then backward for (const [index, glyph] of subTable.input.entries()) { currentEntries = processInputPosition( [glyph], index + 1, currentEntries, subTable.lookupRecords, lookups ); } for (const glyph of subTable.lookahead) { currentEntries = processLookaheadPosition( [glyph], currentEntries ); } for (const glyph of subTable.backtrack) { currentEntries = processBacktrackPosition( [glyph], currentEntries ); } // When we get to the end, insert the lookup information for (const { entry, substitutions } of currentEntries) { entry.lookup = { substitutions, length: subTable.input.length + 1, index: tableIndex, subIndex, contextRange: [ -1 * subTable.backtrack.length, 1 + subTable.input.length + subTable.lookahead.length ] }; } } } return result; } ================================================ FILE: addons/addon-ligatures/src/fontLigatures/processors/6-2.ts ================================================ import { ChainingContextualSubstitutionTable, Lookup } from '../tables'; import { ILookupTree } from '../types'; import mergeTrees from '../merge'; import { listGlyphsByIndex } from './coverage'; import getGlyphClass, { listClassGlyphs } from './classDef'; import { processInputPosition, processLookaheadPosition, processBacktrackPosition, getInputTree, IEntryMeta } from './helper'; /** * Build lookup tree for GSUB lookup table 6, format 2. * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#62-chaining-context-substitution-format-2-class-based-glyph-contexts * * @param table JSON representation of the table * @param lookups List of lookup tables * @param tableIndex Index of this table in the overall lookup */ export default function buildTree(table: ChainingContextualSubstitutionTable.IFormat2, lookups: Lookup[], tableIndex: number): ILookupTree { const results: ILookupTree[] = []; const firstGlyphs = listGlyphsByIndex(table.coverage); for (const { glyphId } of firstGlyphs) { const firstInputClass = getGlyphClass(table.inputClassDef, glyphId); for (const [glyphId, inputClass] of firstInputClass.entries()) { // istanbul ignore next - invalid font if (inputClass === null) { continue; } const classSet = table.chainClassSet[inputClass]; // If the class set is null there's nothing to do with this table. if (!classSet) { continue; } for (const [subIndex, subTable] of classSet.entries()) { const result: ILookupTree = { individual: {}, range: [] }; let currentEntries: IEntryMeta[] = getInputTree( result, subTable.lookupRecords, lookups, 0, glyphId ).map(({ entry, substitution }) => ({ entry, substitutions: [substitution] })); for (const [index, classNum] of subTable.input.entries()) { currentEntries = processInputPosition( listClassGlyphs(table.inputClassDef, classNum), index + 1, currentEntries, subTable.lookupRecords, lookups ); } for (const classNum of subTable.lookahead) { currentEntries = processLookaheadPosition( listClassGlyphs(table.lookaheadClassDef, classNum), currentEntries ); } for (const classNum of subTable.backtrack) { currentEntries = processBacktrackPosition( listClassGlyphs(table.backtrackClassDef, classNum), currentEntries ); } // When we get to the end, all of the entries we've accumulated // should have a lookup defined for (const { entry, substitutions } of currentEntries) { entry.lookup = { substitutions, index: tableIndex, subIndex, length: subTable.input.length + 1, contextRange: [ -1 * subTable.backtrack.length, 1 + subTable.input.length + subTable.lookahead.length ] }; } results.push(result); } } } return mergeTrees(results); } ================================================ FILE: addons/addon-ligatures/src/fontLigatures/processors/6-3.ts ================================================ import { ChainingContextualSubstitutionTable, Lookup } from '../tables'; import { ILookupTree } from '../types'; import { listGlyphsByIndex } from './coverage'; import { processInputPosition, processLookaheadPosition, processBacktrackPosition, getInputTree, IEntryMeta } from './helper'; /** * Build lookup tree for GSUB lookup table 6, format 3. * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#63-chaining-context-substitution-format-3-coverage-based-glyph-contexts * * @param table JSON representation of the table * @param lookups List of lookup tables * @param tableIndex Index of this table in the overall lookup */ export default function buildTree(table: ChainingContextualSubstitutionTable.IFormat3, lookups: Lookup[], tableIndex: number): ILookupTree { const result: ILookupTree = { individual: {}, range: [] }; const firstGlyphs = listGlyphsByIndex(table.inputCoverage[0]); for (const { glyphId } of firstGlyphs) { let currentEntries: IEntryMeta[] = getInputTree( result, table.lookupRecords, lookups, 0, glyphId ).map(({ entry, substitution }) => ({ entry, substitutions: [substitution] })); for (const [index, coverage] of table.inputCoverage.slice(1).entries()) { currentEntries = processInputPosition( listGlyphsByIndex(coverage).map(glyph => glyph.glyphId), index + 1, currentEntries, table.lookupRecords, lookups ); } for (const coverage of table.lookaheadCoverage) { currentEntries = processLookaheadPosition( listGlyphsByIndex(coverage).map(glyph => glyph.glyphId), currentEntries ); } for (const coverage of table.backtrackCoverage) { currentEntries = processBacktrackPosition( listGlyphsByIndex(coverage).map(glyph => glyph.glyphId), currentEntries ); } // When we get to the end, all of the entries we've accumulated // should have a lookup defined for (const { entry, substitutions } of currentEntries) { entry.lookup = { substitutions, index: tableIndex, subIndex: 0, length: table.inputCoverage.length, contextRange: [ -1 * table.backtrackCoverage.length, table.inputCoverage.length + table.lookaheadCoverage.length ] }; } } return result; } ================================================ FILE: addons/addon-ligatures/src/fontLigatures/processors/8-1.ts ================================================ import { IReverseChainingContextualSingleSubstitutionTable } from '../tables'; import { ILookupTree, ILookupTreeEntry } from '../types'; import { listGlyphsByIndex } from './coverage'; import { processLookaheadPosition, processBacktrackPosition, IEntryMeta } from './helper'; /** * Build lookup tree for GSUB lookup table 8, format 1. * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#81-reverse-chaining-contextual-single-substitution-format-1-coverage-based-glyph-contexts * * @param table JSON representation of the table * @param tableIndex Index of this table in the overall lookup */ export default function buildTree(table: IReverseChainingContextualSingleSubstitutionTable, tableIndex: number): ILookupTree { const result: ILookupTree = { individual: {}, range: [] }; const glyphs = listGlyphsByIndex(table.coverage); for (const { glyphId, index } of glyphs) { const initialEntry: ILookupTreeEntry = {}; if (Array.isArray(glyphId)) { result.range.push({ entry: initialEntry, range: glyphId }); } else { result.individual[glyphId] = initialEntry; } let currentEntries: IEntryMeta[] = [{ entry: initialEntry, substitutions: [table.substitutes[index]] }]; // We walk forward, then backward for (const coverage of table.lookaheadCoverage) { currentEntries = processLookaheadPosition( listGlyphsByIndex(coverage).map(glyph => glyph.glyphId), currentEntries ); } for (const coverage of table.backtrackCoverage) { currentEntries = processBacktrackPosition( listGlyphsByIndex(coverage).map(glyph => glyph.glyphId), currentEntries ); } // When we get to the end, insert the lookup information for (const { entry, substitutions } of currentEntries) { entry.lookup = { substitutions, index: tableIndex, subIndex: 0, length: 1, contextRange: [ -1 * table.backtrackCoverage.length, 1 + table.lookaheadCoverage.length ] }; } } return result; } ================================================ FILE: addons/addon-ligatures/src/fontLigatures/processors/classDef.ts ================================================ import { ClassDefTable } from '../tables'; /** * Get the number of the class to which the glyph belongs, or null if it doesn't * belong to any of them. * * @param table JSON representation of the class def table * @param glyphId Index of the glyph to look for */ export default function getGlyphClass(table: ClassDefTable, glyphId: number | [number, number]): Map { switch (table.format) { // https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-2 case 2: if (Array.isArray(glyphId)) { return getRangeGlyphClass(table, glyphId); } return new Map([[ glyphId, getIndividualGlyphClass(table, glyphId) ]]); // https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1 default: return new Map([[glyphId, null]]); } } function getRangeGlyphClass(table: ClassDefTable.IFormat2, glyphId: [number, number]): Map { const classStart: number = glyphId[0]; const currentClass: number | null = getIndividualGlyphClass(table, classStart); let search: number = glyphId[0] + 1; const result = new Map<[number, number] | number, number | null>(); while (search < glyphId[1]) { const clazz = getIndividualGlyphClass(table, search); if (clazz !== currentClass) { if (search - classStart <= 1) { result.set(classStart, currentClass); } else { result.set([classStart, search], currentClass); } } search++; } if (search - classStart <= 1) { result.set(classStart, currentClass); } else { result.set([classStart, search], currentClass); } return result; } function getIndividualGlyphClass(table: ClassDefTable.IFormat2, glyphId: number): number | null { for (const range of table.ranges) { if (range.start <= glyphId && range.end >= glyphId) { return range.classId; } } return null; } export function listClassGlyphs(table: ClassDefTable, index: number): (number | [number, number])[] { switch (table.format) { case 2: const results: (number | [number, number])[] = []; for (const range of table.ranges) { if (range.classId !== index) { continue; } if (range.end === range.start) { results.push(range.start); } else { results.push([range.start, range.end + 1]); } } return results; default: return []; } } ================================================ FILE: addons/addon-ligatures/src/fontLigatures/processors/coverage.ts ================================================ import { CoverageTable } from '../tables'; /** * Get the index of the given glyph in the coverage table, or null if it is not * present in the table. * * @param table JSON representation of the coverage table * @param glyphId Index of the glyph to look for */ export default function getCoverageGlyphIndex(table: CoverageTable, glyphId: number): number | null { switch (table.format) { // https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-1 case 1: const index = table.glyphs.indexOf(glyphId); return index !== -1 ? index : null; // https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-2 case 2: const range = table.ranges .find(range => range.start <= glyphId && range.end >= glyphId); return range ? range.index : null; } } export function listGlyphsByIndex(table: CoverageTable): { glyphId: number | [number, number], index: number }[] { switch (table.format) { case 1: return table.glyphs.map((glyphId, index) => ({ glyphId, index })); case 2: const results: { glyphId: number | [number, number], index: number }[] = []; for (const [index, range] of table.ranges.entries()) { if (range.end === range.start) { results.push({ glyphId: range.start, index }); } else { results.push({ glyphId: [range.start, range.end + 1], index }); } } return results; } } ================================================ FILE: addons/addon-ligatures/src/fontLigatures/processors/helper.ts ================================================ import { ILookupTreeEntry, ILookupTree } from '../types'; import { ISubstitutionLookupRecord, Lookup } from '../tables'; import { getIndividualSubstitutionGlyph, getRangeSubstitutionGlyphs } from './substitution'; export interface IEntryMeta { entry: ILookupTreeEntry; substitutions: (number | null)[]; } export function processInputPosition( glyphs: (number | [number, number])[], position: number, currentEntries: IEntryMeta[], lookupRecords: ISubstitutionLookupRecord[], lookups: Lookup[] ): IEntryMeta[] { const nextEntries: IEntryMeta[] = []; for (const currentEntry of currentEntries) { currentEntry.entry.forward = { individual: {}, range: [] }; for (const glyph of glyphs) { nextEntries.push(...getInputTree( currentEntry.entry.forward, lookupRecords, lookups, position, glyph ).map(({ entry, substitution }) => ({ entry, substitutions: [...currentEntry.substitutions, substitution] }))); } } return nextEntries; } export function processLookaheadPosition( glyphs: (number | [number, number])[], currentEntries: IEntryMeta[] ): IEntryMeta[] { const nextEntries: IEntryMeta[] = []; const processedEntries = new Set(); for (const currentEntry of currentEntries) { // Skip if we've already processed this entry object if (processedEntries.has(currentEntry.entry)) { continue; } processedEntries.add(currentEntry.entry); currentEntry.entry.forward ??= { individual: {}, range: [] }; // All glyphs at this position share ONE entry - lookahead just needs to match, // all paths lead to the same result const sharedEntry: ILookupTreeEntry = {}; for (const glyph of glyphs) { if (Array.isArray(glyph)) { currentEntry.entry.forward.range.push({ entry: sharedEntry, range: glyph }); } else { currentEntry.entry.forward.individual[glyph] = sharedEntry; } } nextEntries.push({ entry: sharedEntry, substitutions: currentEntry.substitutions }); } return nextEntries; } export function processBacktrackPosition( glyphs: (number | [number, number])[], currentEntries: IEntryMeta[] ): IEntryMeta[] { const nextEntries: IEntryMeta[] = []; const processedEntries = new Set(); for (const currentEntry of currentEntries) { // Skip if we've already processed this entry object if (processedEntries.has(currentEntry.entry)) { continue; } processedEntries.add(currentEntry.entry); currentEntry.entry.reverse ??= { individual: {}, range: [] }; // All glyphs at this position share ONE entry - backtrack just needs to match, // all paths lead to the same result const sharedEntry: ILookupTreeEntry = {}; for (const glyph of glyphs) { if (Array.isArray(glyph)) { currentEntry.entry.reverse.range.push({ entry: sharedEntry, range: glyph }); } else { currentEntry.entry.reverse.individual[glyph] = sharedEntry; } } nextEntries.push({ entry: sharedEntry, substitutions: currentEntry.substitutions }); } return nextEntries; } export function getInputTree(tree: ILookupTree, substitutions: ISubstitutionLookupRecord[], lookups: Lookup[], inputIndex: number, glyphId: number | [number, number]): { entry: ILookupTreeEntry, substitution: number | null }[] { const result: { entry: ILookupTreeEntry, substitution: number | null }[] = []; if (!Array.isArray(glyphId)) { tree.individual[glyphId] = {}; result.push({ entry: tree.individual[glyphId], substitution: getSubstitutionAtPosition(substitutions, lookups, inputIndex, glyphId) }); } else { const subs = getSubstitutionAtPositionRange(substitutions, lookups, inputIndex, glyphId); for (const [range, substitution] of subs) { const entry: ILookupTreeEntry = {}; if (Array.isArray(range)) { tree.range.push({ range, entry }); } else { tree.individual[range] = {}; } result.push({ entry, substitution }); } } return result; } function getSubstitutionAtPositionRange(substitutions: ISubstitutionLookupRecord[], lookups: Lookup[], index: number, range: [number, number]): Map { for (const substitution of substitutions.filter(s => s.sequenceIndex === index)) { for (const substitutionTable of (lookups[substitution.lookupListIndex] as Lookup.IType1).subtables) { const sub = getRangeSubstitutionGlyphs( substitutionTable, range ); if (!Array.from(sub.values()).every(val => val !== null)) { return sub; } } } return new Map([[range, null]]); } function getSubstitutionAtPosition(substitutions: ISubstitutionLookupRecord[], lookups: Lookup[], index: number, glyphId: number): number | null { for (const substitution of substitutions.filter(s => s.sequenceIndex === index)) { for (const substitutionTable of (lookups[substitution.lookupListIndex] as Lookup.IType1).subtables) { const sub = getIndividualSubstitutionGlyph( substitutionTable, glyphId ); if (sub !== null) { return sub; } } } return null; } ================================================ FILE: addons/addon-ligatures/src/fontLigatures/processors/substitution.ts ================================================ import { SubstitutionTable } from '../tables'; import getCoverageGlyphIndex from './coverage'; /** * Get the substitution glyph for the givne glyph, or null if the glyph was not * found in the table. * * @param table JSON representation of the substitution table * @param glyphId The index of the glpyh to find substitutions for */ export function getRangeSubstitutionGlyphs(table: SubstitutionTable, glyphId: [number, number]): Map<[number, number] | number, number | null> { const replacementStart: number = glyphId[0]; const currentReplacement: number | null = getIndividualSubstitutionGlyph(table, replacementStart); let search: number = glyphId[0] + 1; const result = new Map<[number, number] | number, number | null>(); while (search < glyphId[1]) { const sub = getIndividualSubstitutionGlyph(table, search); if (sub !== currentReplacement) { if (search - replacementStart <= 1) { result.set(replacementStart, currentReplacement); } else { result.set([replacementStart, search], currentReplacement); } } search++; } if (search - replacementStart <= 1) { result.set(replacementStart, currentReplacement); } else { result.set([replacementStart, search], currentReplacement); } return result; } export function getIndividualSubstitutionGlyph(table: SubstitutionTable, glyphId: number): number | null { const coverageIndex = getCoverageGlyphIndex(table.coverage, glyphId); // istanbul ignore next - invalid font if (coverageIndex === null) { return null; } switch (table.substFormat) { // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#11-single-substitution-format-1 case 1: // TODO: determine if there's a rhyme or reason to the 16-bit // wraparound and if it can ever be a different number return (glyphId + table.deltaGlyphId) % (2 ** 16); // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#12-single-substitution-format-2 case 2: return table.substitute[coverageIndex] ?? null; } } ================================================ FILE: addons/addon-ligatures/src/fontLigatures/tables.ts ================================================ export type SubstitutionTable = SubstitutionTable.IFormat1 | SubstitutionTable.IFormat2; export namespace SubstitutionTable { export interface IFormat1 { substFormat: 1; coverage: CoverageTable; deltaGlyphId: number; } export interface IFormat2 { substFormat: 2; coverage: CoverageTable; substitute: number[]; } } export type CoverageTable = CoverageTable.IFormat1 | CoverageTable.IFormat2; export namespace CoverageTable { export interface IFormat1 { format: 1; glyphs: number[]; } export interface IFormat2 { format: 2; ranges: { start: number; end: number; index: number; }[]; } } export type ChainingContextualSubstitutionTable = ChainingContextualSubstitutionTable.IFormat1 | ChainingContextualSubstitutionTable.IFormat2 | ChainingContextualSubstitutionTable.IFormat3; export namespace ChainingContextualSubstitutionTable { export interface IFormat1 { substFormat: 1; coverage: CoverageTable; chainRuleSets: ChainSubRuleTable[][]; } export interface IFormat2 { substFormat: 2; coverage: CoverageTable; backtrackClassDef: ClassDefTable; inputClassDef: ClassDefTable; lookaheadClassDef: ClassDefTable; chainClassSet: (null | IChainSubClassRuleTable[])[]; } export interface IFormat3 { substFormat: 3; backtrackCoverage: CoverageTable[]; inputCoverage: CoverageTable[]; lookaheadCoverage: CoverageTable[]; lookupRecords: ISubstitutionLookupRecord[]; } } export interface IReverseChainingContextualSingleSubstitutionTable { substFormat: 1; coverage: CoverageTable; backtrackCoverage: CoverageTable[]; lookaheadCoverage: CoverageTable[]; substitutes: number[]; } export type ClassDefTable = ClassDefTable.IFormat2; export namespace ClassDefTable { export interface IFormat2 { format: 2; ranges: { start: number; end: number; classId: number; }[]; } } export interface ISubstitutionLookupRecord { sequenceIndex: number; lookupListIndex: number; } export type ChainSubRuleTable = IChainSubClassRuleTable; export interface IChainSubClassRuleTable { backtrack: number[]; input: number[]; lookahead: number[]; lookupRecords: ISubstitutionLookupRecord[]; } export type Lookup = Lookup.IType1 | Lookup.IType6 | Lookup.IType8; export namespace Lookup { export interface IType1 { lookupType: 1; lookupFlag: number; subtables: SubstitutionTable[]; } export interface IType6 { lookupType: 6; lookupFlag: number; subtables: ChainingContextualSubstitutionTable[]; } export interface IType8 { lookupType: 8; lookupFlag: number; subtables: IReverseChainingContextualSingleSubstitutionTable[]; } } ================================================ FILE: addons/addon-ligatures/src/fontLigatures/types.ts ================================================ export interface ISubstitutionResult { index: number; contextRange: [number, number]; } /** * Information about ligatures found in a sequence of text */ export interface ILigatureData { /** * The list of font glyphs in the input text. */ inputGlyphs: number[]; /** * The list of font glyphs after performing replacements for font ligatures. */ outputGlyphs: number[]; /** * Sorted array of ranges that must be rendered together to produce the * ligatures in the output sequence. The ranges are inclusive on the left and * exclusive on the right. */ contextRanges: [number, number][]; } export interface IFont { /** * Scans the provided text for font ligatures, returning an object with * metadata about the text and any ligatures found. * * @param text String to search for ligatures */ findLigatures(text: string): ILigatureData; /** * Scans the provided text for font ligatures, returning an array of ranges * where ligatures are located. * * @param text String to search for ligatures */ findLigatureRanges(text: string): [number, number][]; } export interface IOptions { /** * Optional size of previous results to store, measured in total number of * characters from input strings. Defaults to no cache (0) */ cacheSize?: number; } export interface ILookupTree { individual: { [glyphId: string]: ILookupTreeEntry; }; range: { range: [number, number]; entry: ILookupTreeEntry; }[]; } export interface ILookupTreeEntry { lookup?: ILookupResult; forward?: ILookupTree; reverse?: ILookupTree; } export interface ILookupResult { substitutions: (number | null)[]; length: number; index: number; subIndex: number; contextRange: [number, number]; } export interface IFlattenedLookupTree { [glyphId: string]: IFlattenedLookupTreeEntry; } export interface IFlattenedLookupTreeEntry { lookup?: ILookupResult; forward?: IFlattenedLookupTree; reverse?: IFlattenedLookupTree; } ================================================ FILE: addons/addon-ligatures/src/fontLigatures/walk.ts ================================================ import { IFlattenedLookupTree, ILookupResult } from './types'; export default function walkTree(tree: IFlattenedLookupTree, sequence: number[], startIndex: number, index: number): ILookupResult | undefined { const glyphId = sequence[index]; const subtree = tree[glyphId]; if (!subtree) { return undefined; } let lookup = subtree.lookup; if (subtree.reverse) { const reverseLookup = walkReverse(subtree.reverse, sequence, startIndex); if ( (!lookup && reverseLookup) || ( reverseLookup && lookup && ( lookup.index > reverseLookup.index || (lookup.index === reverseLookup.index && lookup.subIndex > reverseLookup.subIndex) ) ) ) { lookup = reverseLookup; } } if (++index >= sequence.length || !subtree.forward) { return lookup; } const forwardLookup = walkTree(subtree.forward, sequence, startIndex, index); if ( (!lookup && forwardLookup) || ( forwardLookup && lookup && ( lookup.index > forwardLookup.index || (lookup.index === forwardLookup.index && lookup.subIndex > forwardLookup.subIndex) ) ) ) { lookup = forwardLookup; } return lookup; } function walkReverse(tree: IFlattenedLookupTree, sequence: number[], index: number): ILookupResult | undefined { let subtree = tree[sequence[--index]]; let lookup: ILookupResult | undefined = subtree && subtree.lookup; while (subtree) { if ( (!lookup && subtree.lookup) || (subtree.lookup && lookup && lookup.index > subtree.lookup.index) ) { lookup = subtree.lookup; } if (--index < 0 || !subtree.reverse) { break; } subtree = subtree.reverse[sequence[index]]; } return lookup; } ================================================ FILE: addons/addon-ligatures/src/index.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import type { Terminal } from '@xterm/xterm'; import { Font } from './fontLigatures/index'; import load from './font'; const enum LoadingState { UNLOADED, LOADING, LOADED, FAILED } // Caches 100K characters worth of ligatures. In practice this works out to // about 650 KB worth of cache, when a moderate number of ligatures are present. const CACHE_SIZE = 100000; /** * Enable ligature support for the provided Terminal instance. To function * properly, this must be called after `open()` is called on the therminal. If * the font currently in use supports ligatures, the terminal will automatically * start to render them. * @param term Terminal instance from xterm.js */ export function enableLigatures(term: Terminal, fallbackLigatures: string[] = []): number { let currentFontName: string | undefined = undefined; let font: Font | undefined = undefined; let loadingState: LoadingState = LoadingState.UNLOADED; let loadError: unknown = undefined; return term.registerCharacterJoiner((text: string): [number, number][] => { // If the font hasn't been loaded yet, load it and return an empty result const termFont = term.options.fontFamily; if ( termFont && (loadingState === LoadingState.UNLOADED || currentFontName !== termFont) ) { font = undefined; loadingState = LoadingState.LOADING; currentFontName = termFont; const currentCallFontName = currentFontName; load(currentCallFontName, CACHE_SIZE) .then(f => { // Another request may have come in while we were waiting, so make // sure our font is still vaild. if (currentCallFontName === term.options.fontFamily) { loadingState = LoadingState.LOADED; font = f; // Only refresh things if we actually found a font if (f) { term.refresh(0, term.rows - 1); } } }) .catch(e => { // Another request may have come in while we were waiting, so make // sure our font is still vaild. if (currentCallFontName === term.options.fontFamily) { loadingState = LoadingState.FAILED; if (term.options.logLevel === 'debug') { console.debug(loadError, new Error('Failure while loading font')); } font = undefined; loadError = e; } }); } if (font && loadingState === LoadingState.LOADED) { // We clone the entries to avoid the internal cache of the ligature finder // getting messed up. return font.findLigatureRanges(text).map<[number, number]>( range => [range[0], range[1]] ); } return getFallbackRanges(text, fallbackLigatures); }); } function getFallbackRanges(text: string, fallbackLigatures: string[]): [number, number][] { const ranges: [number, number][] = []; for (let i = 0; i < text.length; i++) { for (let j = 0; j < fallbackLigatures.length; j++) { if (text.startsWith(fallbackLigatures[j], i)) { ranges.push([i, i + fallbackLigatures[j].length]); i += fallbackLigatures[j].length - 1; break; } } } return ranges; } ================================================ FILE: addons/addon-ligatures/src/parse.test.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import parse from './parse'; // TODO: integrate tests from http://test.csswg.org/suites/css-fonts-4_dev/nightly-unstable/ describe('addon-ligatures - parse', () => { it('parses individual families', () => { assert.deepEqual(parse('monospace'), ['monospace']); }); it('parses multiple families', () => { assert.deepEqual(parse('Arial, Verdana, serif'), ['Arial', 'Verdana', 'serif']); }); it('parses quoted families', () => { assert.deepEqual(parse('"Times New Roman", serif'), ['Times New Roman', 'serif']); }); it('parses single quoted families', () => { assert.deepEqual(parse('\'Times New Roman\', serif'), ['Times New Roman', 'serif']); }); it('parses families with spaces in their names', () => { assert.deepEqual(parse('Times New Roman, serif'), ['Times New Roman', 'serif']); }); it('collapses multiple spaces together in identifiers', () => { assert.deepEqual(parse('Times New Roman, serif'), ['Times New Roman', 'serif']); }); it('does not collapse multiple spaces together in quoted strings', () => { assert.deepEqual(parse('"Times New Roman", serif'), ['Times New Roman', 'serif']); }); it('handles escaped characters in strings', () => { assert.deepEqual(parse('"quote \\" slash \\\\ slashquote \\\\\\"", serif'), ['quote " slash \\ slashquote \\"', 'serif']); }); it('fails if a family has an unterminated string', () => { assert.throws(() => parse('"Unterminated, serif')); }); it('handles unicode escape sequences', () => { assert.deepEqual(parse('"space\\20 between", serif'), ['space between', 'serif']); }); it('swallows only the first space after a unicode escape', () => { assert.deepEqual(parse('"two-space\\20 between", serif'), ['two-space between', 'serif']); }); it('automatically ends the unicode escape after six digits', () => { assert.deepEqual(parse('space\\000020between, serif'), ['space between', 'serif']); }); it('handles unicode escapes at the end of the family', () => { assert.deepEqual(parse('endswithbrace \\7b, serif'), ['endswithbrace {', 'serif']); }); it('handles unicode escapes at the end of the input', () => { assert.deepEqual(parse('endswithbrace \\7b'), ['endswithbrace {']); }); it('handles other escaped characters in identifiers', () => { assert.deepEqual(parse('has\\,comma'), ['has,comma']); }); it('swallows escaped newlines in strings', () => { assert.deepEqual(parse('"multi \\\nline", serif'), ['multi line', 'serif']); }); }); ================================================ FILE: addons/addon-ligatures/src/parse.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ interface IParseContext { input: string; offset: number; } /** * Parses a CSS font family value, returning the component font families * contained within. * * @param family The CSS font family input string to parse */ export default function parse(family: string): string[] { if (typeof family !== 'string') { throw new Error('Font family must be a string'); } const context: IParseContext = { input: family, offset: 0 }; const families = []; let currentFamily = ''; // Work through the input character by character until there are none left. // This lexing and parsing in one pass. while (context.offset < context.input.length) { const char = context.input[context.offset++]; switch (char) { // String case '\'': case '"': currentFamily += parseString(context, char); break; // End of family case ',': families.push(currentFamily); currentFamily = ''; break; default: // Identifiers (whitespace between families is swallowed) if (!/\s/.test(char)) { context.offset--; currentFamily += parseIdentifier(context); families.push(currentFamily); currentFamily = ''; } } } return families; } /** * Parse a CSS string. * * @param context Parsing input and offset * @param quoteChar The quote character for the string (' or ") */ function parseString(context: IParseContext, quoteChar: '\'' | '"'): string { let str = ''; let escaped = false; while (context.offset < context.input.length) { const char = context.input[context.offset++]; if (escaped) { if (/[\dA-Fa-f]/.test(char)) { // Unicode escape context.offset--; str += parseUnicode(context); } else if (char !== '\n') { // Newlines are ignored if escaped. Other characters are used as is. str += char; } escaped = false; } else { switch (char) { // Terminated quote case quoteChar: return str; // Begin escape case '\\': escaped = true; break; // Add character to string default: str += char; } } } throw new Error('Unterminated string'); } /** * Parse a CSS custom identifier. * * @param context Parsing input and offset */ function parseIdentifier(context: IParseContext): string { let str = ''; let escaped = false; while (context.offset < context.input.length) { const char = context.input[context.offset++]; if (escaped) { if (/[\dA-Fa-f]/.test(char)) { // Unicode escape context.offset--; str += parseUnicode(context); } else { // Everything else is used as is str += char; } escaped = false; } else { switch (char) { // Begin escape case '\\': escaped = true; break; // Terminate identifier case ',': return str; default: if (/\s/.test(char)) { // Whitespace is collapsed into a single space within an identifier if (!str.endsWith(' ')) { str += ' '; } } else { // Add other characters directly str += char; } } } } return str; } /** * Parse a CSS unicode escape. * * @param context Parsing input and offset */ function parseUnicode(context: IParseContext): string { let str = ''; while (context.offset < context.input.length) { const char = context.input[context.offset++]; if (/\s/.test(char)) { // The first whitespace character after a unicode escape indicates the end // of the escape and is swallowed. return unicodeToString(str); } if (str.length >= 6 || !/[\dA-Fa-f]/.test(char)) { // If the next character is not a valid hex digit or we have reached the // maximum of 6 digits in the escape, terminate the escape. context.offset--; return unicodeToString(str); } // Otherwise, just add it to the escape str += char; } return unicodeToString(str); } /** * Convert a unicode code point from a hex string to a utf8 string. * * @param codePoint Unicode code point represented as a hex string */ function unicodeToString(codePoint: string): string { return String.fromCodePoint(parseInt(codePoint, 16)); } ================================================ FILE: addons/addon-ligatures/src/tsconfig.json ================================================ { "compilerOptions": { "target": "es2017", "module": "commonjs", "sourceMap": true, "outDir": "../out", "rootDir": ".", "strict": true, "noUnusedLocals": true, "preserveWatchOutput": true, "types": [ "../../../node_modules/@types/mocha", // HACK: src shouldn't use node types but it's needed for index.test.ts "../../../node_modules/@types/node" ], "paths": { "@xterm/addon-ligatures" : [ "../typings/addon-ligatures.d.ts" ] } }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ] } ================================================ FILE: addons/addon-ligatures/test/LigaturesAddon.test.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import * as path from 'path'; import { assert } from 'chai'; // Use require to get a mutable module object (ESM imports create read-only bindings) const fontFinder = require('font-finder'); const ligatureSupport = require('../out-esbuild/index'); const originalList = fontFinder.list; describe('LigaturesAddon', () => { let onRefresh: { called: boolean, callCount: number, (...args: any[]): void }; let term: MockTerminal; const input = 'a -> b www c'; before(() => { fontFinder.list = () => Promise.resolve({ 'Fira Code': [{ path: path.join(__dirname, '../fonts/firaCode.otf'), style: fontFinder.Style.Regular, type: fontFinder.Type.Monospace, weight: 400 }], 'Iosevka': [{ path: path.join(__dirname, '../fonts/iosevka.ttf'), style: fontFinder.Style.Regular, type: fontFinder.Type.Monospace, weight: 400 }], 'Nonexistant Font': [{ path: path.join(__dirname, '../fonts/nonexistant.ttf'), style: fontFinder.Style.Regular, type: fontFinder.Type.Monospace, weight: 400 }] }); }); after(() => { fontFinder.list = originalList; }); beforeEach(() => { onRefresh = Object.assign((..._args: any[]) => { onRefresh.called = true; onRefresh.callCount++; }, { called: false, callCount: 0 }); term = new MockTerminal(onRefresh); ligatureSupport.enableLigatures(term as any); }); it('registers itself correctly', () => { const term = new MockTerminal(() => {}); assert.isUndefined(term.joiner); ligatureSupport.enableLigatures(term as any); assert.isFunction(term.joiner); }); it('registers itself correctly when called directly', () => { const term = new MockTerminal(() => {}); assert.isUndefined(term.joiner); ligatureSupport.enableLigatures(term as any); assert.isFunction(term.joiner); }); it('returns an empty set of ranges on the first call while the font is loading', () => { assert.deepEqual(term.joiner!(input), []); }); it('fails if it finds but cannot load the font', async () => { term.options.fontFamily = 'Nonexistant Font, monospace'; assert.deepEqual(term.joiner!(input), []); await delay(500); assert.strictEqual(onRefresh.callCount, 0); }); it('returns nothing if the font is not present on the system', async () => { term.options.fontFamily = 'notinstalled'; assert.deepEqual(term.joiner!(input), []); await delay(500); assert.strictEqual(onRefresh.callCount, 0); assert.deepEqual(term.joiner!(input), []); }); it('returns nothing if no specific font is specified', async () => { term.options.fontFamily = 'monospace'; assert.deepEqual(term.joiner!(input), []); await delay(500); assert.strictEqual(onRefresh.callCount, 0); assert.deepEqual(term.joiner!(input), []); }); it('returns nothing if no fonts are provided', async () => { term.options.fontFamily = ''; assert.deepEqual(term.joiner!(input), []); await delay(500); assert.strictEqual(onRefresh.callCount, 0); assert.deepEqual(term.joiner!(input), []); }); it('fails when given malformed inputs', async () => { term.options.fontFamily = {} as any; assert.deepEqual(term.joiner!(input), []); await delay(500); assert.strictEqual(onRefresh.callCount, 0); }); }); class MockTerminal { private _options: { [name: string]: string | number } = { fontFamily: 'Fira Code, monospace', rows: 50 }; public joiner?: (text: string) => [number, number][]; public refresh: (start: number, end: number) => void; constructor(onRefresh: (start: number, end: number) => void) { this.refresh = onRefresh; } public registerCharacterJoiner(handler: (text: string) => [number, number][]): number { this.joiner = handler; return 1; } public deregisterCharacterJoiner(id: number): void { this.joiner = undefined; } public get options(): { [name: string]: string | number } { return this._options; } public set options(options: { [name: string]: string | number }) { for (const key in this._options) { this._options[key] = options[key]; } } } function delay(delayMs: number): Promise { return new Promise(resolve => setTimeout(resolve, delayMs)); } ================================================ FILE: addons/addon-ligatures/test/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "ESNext", "lib": [ "es2021" ], "rootDir": ".", "outDir": "../out-esbuild-test", "sourceMap": true, "removeComments": true, "strict": true, "paths": { "*": [ "./*" ] }, "types": [ "../../../node_modules/@types/mocha", "../../../node_modules/@types/node" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ] } ================================================ FILE: addons/addon-ligatures/tsconfig.json ================================================ { "files": [], "include": [], "references": [ { "path": "./src" }, { "path": "./test" } ] } ================================================ FILE: addons/addon-ligatures/typings/addon-ligatures.d.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT * * This contains the type declarations for the @xterm/addon-ligatures library. * Note that some interfaces may differ between this file and the actual * implementation in src/, that's because this file declares the *public* API * which is intended to be stable and consumed by external programs. */ import { Terminal, ITerminalAddon } from '@xterm/xterm'; declare module '@xterm/addon-ligatures' { /** * An xterm.js addon that enables web links. */ export class LigaturesAddon implements ITerminalAddon { /** * Creates a new ligatures addon. * * @param options Options for the ligatures addon. */ constructor(options?: Partial); /** * Activates the addon. Note that if webgl is also being used, that addon * should be reactivated after ligatures is activated in order to apply * {@link ILigatureOptions.fontFeatureSettings} to the texture atlas. * * * @param terminal The terminal the addon is being loaded in. */ public activate(terminal: Terminal): void; /** * Disposes the addon. */ public dispose(): void; } /** * Options for the ligatures addon. */ export interface ILigatureOptions { /** * Fallback ligatures to use when the font access API is either not * supported by the browser or access is denied. The default set of * ligatures is taken from Iosevka's default "calt" ligation set: * https://typeof.net/Iosevka/ * * ``` * <-- <--- <<- <- -> ->> --> ---> * <== <=== <<= <= => =>> ==> ===> >= >>= * <-> <--> <---> <----> <=> <==> <===> <====> :: ::: * <~~ /> ~~> == != /= ~= <> === !== !=== * <: := *= *+ <* <*> *> <| <|> |> +* =* =: :> * /* +++
';

    let foreground = '#000000';
    let background = '#ffffff';
    if (this._options.includeGlobalBackground ?? false) {
      foreground = this._terminal.options.theme?.foreground ?? '#ffffff';
      background = this._terminal.options.theme?.background ?? '#000000';
    }

    const globalStyleDefinitions = [];
    globalStyleDefinitions.push('color: ' + foreground + ';');
    globalStyleDefinitions.push('background-color: ' + background + ';');
    globalStyleDefinitions.push('font-family: ' + this._terminal.options.fontFamily + ';');
    globalStyleDefinitions.push('font-size: ' + this._terminal.options.fontSize + 'px;');
    this._htmlContent += '
'; } protected _afterSerialize(): void { this._htmlContent += '
'; this._htmlContent += '
'; } protected _rowEnd(row: number, isLastRow: boolean): void { this._htmlContent += '
' + this._currentRow + '
'; this._currentRow = ''; } private _getHexColor(cell: IBufferCell, isFg: boolean): string | undefined { const color = isFg ? cell.getFgColor() : cell.getBgColor(); if (isFg ? cell.isFgRGB() : cell.isBgRGB()) { const rgb = [ (color >> 16) & 255, (color >> 8) & 255, (color ) & 255 ]; return '#' + rgb.map(x => x.toString(16).padStart(2, '0')).join(''); } if (isFg ? cell.isFgPalette() : cell.isBgPalette()) { return this._ansiColors[color].css; } return undefined; } private _getUnderlineColor(cell: IBufferCell): string | undefined { if (cell.isUnderlineColorDefault()) { return undefined; } const color = cell.getUnderlineColor(); if (cell.isUnderlineColorRGB()) { const rgb = [ (color >> 16) & 255, (color >> 8) & 255, (color ) & 255 ]; return '#' + rgb.map(x => x.toString(16).padStart(2, '0')).join(''); } // Palette color return this._ansiColors[color].css; } private _getUnderlineStyle(cell: IBufferCell): string { switch (cell.getUnderlineStyle()) { case UnderlineStyle.SINGLE: return 'underline'; case UnderlineStyle.DOUBLE: return 'underline double'; case UnderlineStyle.CURLY: return 'underline wavy'; case UnderlineStyle.DOTTED: return 'underline dotted'; case UnderlineStyle.DASHED: return 'underline dashed'; default: return 'underline'; } } private _diffStyle(cell: IBufferCell, oldCell: IBufferCell): string[] | undefined { const content: string[] = []; if (attributesEquals(cell, oldCell)) { return undefined; } const fgChanged = !equalFg(cell, oldCell); const bgChanged = !equalBg(cell, oldCell); const flagsChanged = !equalFlags(cell, oldCell); if (fgChanged || bgChanged || flagsChanged) { const fgHexColor = this._getHexColor(cell, true); if (fgHexColor) { content.push('color: ' + fgHexColor + ';'); } const bgHexColor = this._getHexColor(cell, false); if (bgHexColor) { content.push('background-color: ' + bgHexColor + ';'); } if (cell.isInverse()) { content.push('color: #000000; background-color: #BFBFBF;'); } if (cell.isBold()) { content.push('font-weight: bold;'); } // Handle text-decoration (underline, overline, strikethrough, blink) const decorations: string[] = []; if (cell.isUnderline()) { decorations.push(this._getUnderlineStyle(cell)); } if (cell.isOverline()) { decorations.push('overline'); } if (cell.isStrikethrough()) { decorations.push('line-through'); } if (cell.isBlink()) { decorations.push('blink'); } if (decorations.length > 0) { content.push('text-decoration: ' + decorations.join(' ') + ';'); } // Handle underline color if (cell.isUnderline()) { const underlineColor = this._getUnderlineColor(cell); if (underlineColor) { content.push('text-decoration-color: ' + underlineColor + ';'); } } if (cell.isInvisible()) { content.push('visibility: hidden;'); } if (cell.isItalic()) { content.push('font-style: italic;'); } if (cell.isDim()) { content.push('opacity: 0.5;'); } return content; } return undefined; } protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { // a width 0 cell don't need to be count because it is just a placeholder after a CJK character; const isPlaceHolderCell = cell.getWidth() === 0; if (isPlaceHolderCell) { return; } // this cell don't have content const isEmptyCell = cell.getChars() === ''; const styleDefinitions = this._diffStyle(cell, oldCell); // handles style change if (styleDefinitions) { this._currentRow += styleDefinitions.length === 0 ? '' : ''; } // handles actual content if (isEmptyCell) { this._currentRow += ' '; } else { this._currentRow += escapeHTMLChar(cell.getChars()); } } protected _serializeString(): string { return this._htmlContent; } } ================================================ FILE: addons/addon-serialize/src/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es2021", "lib": [ "dom", "es2015" ], "rootDir": ".", "outDir": "../out", "sourceMap": true, "removeComments": true, "paths": { "common/*": [ "../../../src/common/*" ], "browser/*": [ "../../../src/browser/*" ], "@xterm/addon-serialize": [ "../typings/addon-serialize.d.ts" ] }, "strict": true, "skipLibCheck": true, "types": [ "../../../node_modules/@types/mocha" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" } ] } ================================================ FILE: addons/addon-serialize/test/SerializeAddon.test.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import test from '@playwright/test'; import { deepStrictEqual, notDeepStrictEqual, strictEqual } from 'assert'; import { readFile } from 'fs'; import { resolve } from 'path'; import { ITestContext, createTestContext, openTerminal, timeout, writeSync } from '../../../test/playwright/TestUtils'; const writeRawSync = (page: any, str: string): Promise => writeSync(ctx.page, `' +` + JSON.stringify(str) + `+ '`); const testNormalScreenEqual = async (page: any, str: string): Promise => { await writeRawSync(ctx.page, str); const originalBuffer = await ctx.page.evaluate(`inspectBuffer(term.buffer.normal);`); const result = await ctx.page.evaluate(`window.serialize.serialize();`) as string; await ctx.page.evaluate(`term.reset();`); await writeRawSync(ctx.page, result); const newBuffer = await ctx.page.evaluate(`inspectBuffer(term.buffer.normal);`); deepStrictEqual(JSON.stringify(originalBuffer), JSON.stringify(newBuffer)); }; async function testSerializeEquals(writeContent: string, expectedSerialized: string): Promise { await writeRawSync(ctx.page, writeContent); const result = await ctx.page.evaluate(`window.serialize.serialize();`) as string; strictEqual(result, expectedSerialized); } let ctx: ITestContext; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); await openTerminal(ctx, { rows: 10, cols: 10 }); }); test.afterAll(async () => await ctx.page.close()); test.describe('SerializeAddon', () => { test.beforeEach(async () => { await ctx.page.evaluate(` window.term.reset() window.serialize?.dispose(); window.serialize = new SerializeAddon(); window.term.loadAddon(window.serialize); window.inspectBuffer = (buffer) => { const lines = []; for (let i = 0; i < buffer.length; i++) { // Do this intentionally to get content of underlining source const bufferLine = buffer.getLine(i)._line; lines.push(JSON.stringify(bufferLine)); } return { x: buffer.cursorX, y: buffer.cursorY, data: lines }; } `); }); test.beforeEach(async () => { await ctx.proxy.reset(); }); test('produce different output when we call test util with different text', async function(): Promise { await writeRawSync(ctx.page, '12345'); const buffer1 = await ctx.page.evaluate(`inspectBuffer(term.buffer.normal);`); await ctx.page.evaluate(`term.reset();`); await writeRawSync(ctx.page, '67890'); const buffer2 = await ctx.page.evaluate(`inspectBuffer(term.buffer.normal);`); notDeepStrictEqual(JSON.stringify(buffer1), JSON.stringify(buffer2)); }); test('produce different output when we call test util with different line wrap', async function(): Promise { await writeRawSync(ctx.page, '1234567890\r\n12345'); const buffer3 = await ctx.page.evaluate(`inspectBuffer(term.buffer.normal);`); await ctx.page.evaluate(`term.reset();`); await writeRawSync(ctx.page, '123456789012345'); const buffer4 = await ctx.page.evaluate(`inspectBuffer(term.buffer.normal);`); notDeepStrictEqual(JSON.stringify(buffer3), JSON.stringify(buffer4)); }); test('empty content', async function(): Promise { strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), ''); }); test('unwrap wrapped line', async function(): Promise { const lines = ['123456789123456789']; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('does not unwrap non-wrapped line', async function(): Promise { const lines = [ '123456789', '123456789' ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('preserve last empty lines', async function(): Promise { const cols = 10; const lines = [ '', '', digitsString(cols), digitsString(cols), '', '', digitsString(cols), digitsString(cols), '', '', '' ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('digits content', async function(): Promise { const rows = 10; const cols = 10; const digitsLine = digitsString(cols); const lines = newArray(digitsLine, rows); await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('serialize with half of scrollback', async function(): Promise { const rows = 20; const scrollback = rows - 10; const halfScrollback = scrollback / 2; const cols = 10; const lines = newArray((index: number) => digitsString(cols, index), rows); await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize({ scrollback: ${halfScrollback} });`), lines.slice(halfScrollback, rows).join('\r\n')); }); test('serialize 0 rows of scrollback', async function(): Promise { const rows = 20; const cols = 10; const lines = newArray((index: number) => digitsString(cols, index), rows); await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize({ scrollback: 0 });`), lines.slice(rows - 10, rows).join('\r\n')); }); test('serialize exclude modes', async () => { await ctx.proxy.write('before\x1b[?1hafter'); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), 'beforeafter\x1b[?1h'); strictEqual(await ctx.page.evaluate(`window.serialize.serialize({ excludeModes: true });`), 'beforeafter'); }); test('serialize exclude alt buffer', async () => { await ctx.proxy.write('normal\x1b[?1049h\x1b[Halt'); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), 'normal\x1b[?1049h\x1b[Halt'); strictEqual(await ctx.page.evaluate(`window.serialize.serialize({ excludeAltBuffer: true });`), 'normal'); }); test('serialize all rows of content with color16', async function(): Promise { const cols = 10; const color16 = [ 30, 31, 32, 33, 34, 35, 36, 37, // Set foreground color 90, 91, 92, 93, 94, 95, 96, 97, 40, 41, 42, 43, 44, 45, 46, 47, // Set background color 100, 101, 103, 104, 105, 106, 107 ]; const rows = color16.length; const lines = newArray( (index: number) => digitsString(cols, index, `\x1b[${color16[index % color16.length]}m`), rows ); await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('serialize all rows of content with fg/bg flags', async function(): Promise { const cols = 10; const line = '+'.repeat(cols); const lines: string[] = [ sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags a the end, serialize will use \x1b[0m to clear instead of the sepcific disable sequence sgr(INVERSE) + line, sgr(BOLD) + line, sgr(UNDERLINED) + line, sgr(BLINK) + line, sgr(INVISIBLE) + line, sgr(STRIKETHROUGH) + line, sgr(NO_INVERSE) + line, sgr(NO_BOLD) + line, sgr(NO_UNDERLINED) + line, sgr(NO_BLINK) + line, sgr(NO_INVISIBLE) + line, sgr(NO_STRIKETHROUGH) + line ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('buffer cell attributesEquals compares underline style and color', async () => { await ctx.proxy.write(`${sgr(UNDERLINE_DOUBLE, UNDERLINE_COLOR_RED)}A${sgr(UNDERLINE_DOUBLE, UNDERLINE_COLOR_RED)}B${sgr(NORMAL)}`); const sameAttributes = await ctx.page.evaluate(`(() => { const line = window.term.buffer.active.getLine(0); const cellA = line?.getCell(0); const cellB = line?.getCell(1); if (!cellA || !cellB) { return undefined; } return cellA.attributesEquals(cellB); })()`); strictEqual(sameAttributes, true); await ctx.page.evaluate(`window.term.reset()`); await ctx.proxy.write(`${sgr(UNDERLINE_DOUBLE, UNDERLINE_COLOR_RED)}A${sgr(UNDERLINE_DOUBLE, UNDERLINE_COLOR_GREEN)}B${sgr(NORMAL)}`); const differentColor = await ctx.page.evaluate(`(() => { const line = window.term.buffer.active.getLine(0); const cellA = line?.getCell(0); const cellB = line?.getCell(1); if (!cellA || !cellB) { return undefined; } return cellA.attributesEquals(cellB); })()`); strictEqual(differentColor, false); await ctx.page.evaluate(`window.term.reset()`); await ctx.proxy.write(`${sgr(UNDERLINE_DOUBLE, UNDERLINE_COLOR_RED)}A${sgr(UNDERLINED, UNDERLINE_COLOR_RED)}B${sgr(NORMAL)}`); const differentStyle = await ctx.page.evaluate(`(() => { const line = window.term.buffer.active.getLine(0); const cellA = line?.getCell(0); const cellB = line?.getCell(1); if (!cellA || !cellB) { return undefined; } return cellA.attributesEquals(cellB); })()`); strictEqual(differentStyle, false); }); test('serialize all rows of content with color256', async function(): Promise { const rows = 32; const cols = 10; const lines = newArray( (index: number) => digitsString(cols, index, `\x1b[38;5;${16 + index}m`), rows ); await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('serialize all rows of content with overline', async () => { const cols = 10; const line = '+'.repeat(cols); const lines: string[] = [ sgr(OVERLINED) + line, // Overlined sgr(UNDERLINED) + line, // Overlined, Underlined sgr(NORMAL) + line // Normal ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('serialize all rows of content with color16 and style separately', async function(): Promise { const cols = 10; const line = '+'.repeat(cols); const lines: string[] = [ sgr(FG_P16_RED) + line, // fg Red, sgr(UNDERLINED) + line, // fg Red, Underlined sgr(FG_P16_GREEN) + line, // fg Green, Underlined sgr(INVERSE) + line, // fg Green, Underlined, Inverse sgr(NO_INVERSE) + line, // fg Green, Underlined sgr(INVERSE) + line, // fg Green, Underlined, Inverse sgr(BG_P16_YELLOW) + line, // fg Green, bg Yellow, Underlined, Inverse sgr(FG_RESET) + line, // bg Yellow, Underlined, Inverse sgr(BG_RESET) + line, // Underlined, Inverse sgr(NORMAL) + line // Back to normal ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('serialize all rows of content with color16 and style together', async function(): Promise { const cols = 10; const line = '+'.repeat(cols); const lines: string[] = [ sgr(FG_P16_RED) + line, // fg Red sgr(FG_P16_GREEN, BG_P16_YELLOW) + line, // fg Green, bg Yellow sgr(UNDERLINED, ITALIC) + line, // fg Green, bg Yellow, Underlined, Italic sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green, bg Yellow sgr(FG_RESET, ITALIC) + line, // bg Yellow, Italic sgr(BG_RESET) + line, // Italic sgr(NORMAL) + line, // Back to normal sgr(FG_P16_RED) + line, // fg Red sgr(FG_P16_GREEN, BG_P16_YELLOW) + line, // fg Green, bg Yellow sgr(UNDERLINED, ITALIC) + line, // fg Green, bg Yellow, Underlined, Italic sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green, bg Yellow sgr(FG_RESET, ITALIC) + line, // bg Yellow, Italic sgr(BG_RESET) + line // Italic ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('serialize all rows of content with color256 and style separately', async function(): Promise { const cols = 10; const line = '+'.repeat(cols); const lines: string[] = [ sgr(FG_P256_RED) + line, // fg Red 256, sgr(UNDERLINED) + line, // fg Red 256, Underlined sgr(FG_P256_GREEN) + line, // fg Green 256, Underlined sgr(INVERSE) + line, // fg Green 256, Underlined, Inverse sgr(NO_INVERSE) + line, // fg Green 256, Underlined sgr(INVERSE) + line, // fg Green 256, Underlined, Inverse sgr(BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256, Underlined, Inverse sgr(FG_RESET) + line, // bg Yellow 256, Underlined, Inverse sgr(BG_RESET) + line, // Underlined, Inverse sgr(NORMAL) + line // Back to normal ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('serialize all rows of content with color256 and style together', async function(): Promise { const cols = 10; const line = '+'.repeat(cols); const lines: string[] = [ sgr(FG_P256_RED) + line, // fg Red 256 sgr(FG_P256_GREEN, BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256 sgr(UNDERLINED, ITALIC) + line, // fg Green 256, bg Yellow 256, Underlined, Italic sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green 256, bg Yellow 256 sgr(FG_RESET, ITALIC) + line, // bg Yellow 256, Italic sgr(BG_RESET) + line, // Italic sgr(NORMAL) + line, // Back to normal sgr(FG_P256_RED) + line, // fg Red 256 sgr(FG_P256_GREEN, BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256 sgr(UNDERLINED, ITALIC) + line, // fg Green 256, bg Yellow 256, Underlined, Italic sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green 256, bg Yellow 256 sgr(FG_RESET, ITALIC) + line, // bg Yellow 256, Italic sgr(BG_RESET) + line // Italic ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('serialize all rows of content with colorRGB and style separately', async function(): Promise { const cols = 10; const line = '+'.repeat(cols); const lines: string[] = [ sgr(FG_RGB_RED) + line, // fg Red RGB, sgr(UNDERLINED) + line, // fg Red RGB, Underlined sgr(FG_RGB_GREEN) + line, // fg Green RGB, Underlined sgr(INVERSE) + line, // fg Green RGB, Underlined, Inverse sgr(NO_INVERSE) + line, // fg Green RGB, Underlined sgr(INVERSE) + line, // fg Green RGB, Underlined, Inverse sgr(BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB, Underlined, Inverse sgr(FG_RESET) + line, // bg Yellow RGB, Underlined, Inverse sgr(BG_RESET) + line, // Underlined, Inverse sgr(NORMAL) + line // Back to normal ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('serialize all rows of content with colorRGB and style together', async function(): Promise { const cols = 10; const line = '+'.repeat(cols); const lines: string[] = [ sgr(FG_RGB_RED) + line, // fg Red RGB sgr(FG_RGB_GREEN, BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB sgr(UNDERLINED, ITALIC) + line, // fg Green RGB, bg Yellow RGB, Underlined, Italic sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green RGB, bg Yellow RGB sgr(FG_RESET, ITALIC) + line, // bg Yellow RGB, Italic sgr(BG_RESET) + line, // Italic sgr(NORMAL) + line, // Back to normal sgr(FG_RGB_RED) + line, // fg Red RGB sgr(FG_RGB_GREEN, BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB sgr(UNDERLINED, ITALIC) + line, // fg Green RGB, bg Yellow RGB, Underlined, Italic sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green RGB, bg Yellow RGB sgr(FG_RESET, ITALIC) + line, // bg Yellow RGB, Italic sgr(BG_RESET) + line // Italic ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('serialize tabs correctly', async () => { const lines = [ 'a\tb', 'aa\tc', 'aaa\td' ]; const expected = [ 'a\x1b[7Cb', 'aa\x1b[6Cc', 'aaa\x1b[5Cd' ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), expected.join('\r\n')); }); test('serialize CJK correctly', async () => { const lines = [ '中文中文', '12中文', '中文12', // This line is going to be wrapped at last character // because it has line length of 11 (1+2*5). // We concat it back without the null cell currently. // But this may be incorrect. // see also #3097 '1中文中文中' ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), lines.join('\r\n')); }); test('serialize CJK Mixed with tab correctly', async () => { const lines = [ '中文\t12' // CJK mixed with tab ]; const expected = [ '中文\x1b[4C12' ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.serialize.serialize();`), expected.join('\r\n')); }); test('serialize with alt screen correctly', async () => { const SMCUP = '\u001b[?1049h'; const CUP = '\u001b[H'; const lines = [ `1${SMCUP}${CUP}2` ]; const expected = [ `1${SMCUP}${CUP}2` ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.term.buffer.active.type`), 'alternate'); strictEqual(JSON.stringify(await ctx.page.evaluate(`window.serialize.serialize();`)), JSON.stringify(expected.join('\r\n'))); }); test('serialize without alt screen correctly', async () => { const SMCUP = '\u001b[?1049h'; const RMCUP = '\u001b[?1049l'; const lines = [ `1${SMCUP}2${RMCUP}` ]; const expected = [ `1` ]; await ctx.proxy.write(lines.join('\r\n')); strictEqual(await ctx.page.evaluate(`window.term.buffer.active.type`), 'normal'); strictEqual(JSON.stringify(await ctx.page.evaluate(`window.serialize.serialize();`)), JSON.stringify(expected.join('\r\n'))); }); test('serialize with background', async () => { const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`; const lines = [ `1\u001b[44m${CLEAR_RIGHT(5)}`, `2${CLEAR_RIGHT(9)}` ]; await testNormalScreenEqual(ctx.page, lines.join('\r\n')); }); test('cause the BCE on scroll', async () => { const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`; const padLines = newArray( (index: number) => digitsString(10, index), 10 ); const lines = [ ...padLines, `\u001b[44m${CLEAR_RIGHT(5)}1111111111111111` ]; await testNormalScreenEqual(ctx.page, lines.join('\r\n')); }); test('handle invalid wrap before scroll', async () => { const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`; const MOVE_UP = (l: number): string => `\u001b[${l}A`; const MOVE_DOWN = (l: number): string => `\u001b[${l}B`; const MOVE_LEFT = (l: number): string => `\u001b[${l}D`; // A line wrap happened after current line. // But there is no content. // so wrap shouldn't even be able to happen. const segments = [ `123456789012345`, MOVE_UP(1), CLEAR_RIGHT(5), MOVE_DOWN(1), MOVE_LEFT(5), CLEAR_RIGHT(5), MOVE_UP(1), '1' ]; await testNormalScreenEqual(ctx.page, segments.join('')); }); test('handle invalid wrap after scroll', async () => { const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`; const MOVE_UP = (l: number): string => `\u001b[${l}A`; const MOVE_DOWN = (l: number): string => `\u001b[${l}B`; const MOVE_LEFT = (l: number): string => `\u001b[${l}D`; const padLines = newArray( (index: number) => digitsString(10, index), 10 ); // A line wrap happened after current line. // But there is no content. // so wrap shouldn't even be able to happen. const lines = [ padLines.join('\r\n'), '\r\n', `123456789012345`, MOVE_UP(1), CLEAR_RIGHT(5), MOVE_DOWN(1), MOVE_LEFT(5), CLEAR_RIGHT(5), MOVE_UP(1), '1' ]; await testNormalScreenEqual(ctx.page, lines.join('')); }); test.describe('handle modes', () => { test('applicationCursorKeysMode', async () => { await testSerializeEquals('test\u001b[?1h', 'test\u001b[?1h'); await testSerializeEquals('\u001b[?1l', 'test'); }); test('applicationKeypadMode', async () => { await testSerializeEquals('test\u001b[?66h', 'test\u001b[?66h'); await testSerializeEquals('\u001b[?66l', 'test'); }); test('bracketedPasteMode', async () => { await testSerializeEquals('test\u001b[?2004h', 'test\u001b[?2004h'); await testSerializeEquals('\u001b[?2004l', 'test'); }); test('insertMode', async () => { await testSerializeEquals('test\u001b[4h', 'test\u001b[4h'); await testSerializeEquals('\u001b[4l', 'test'); }); test('mouseTrackingMode', async () => { await testSerializeEquals('test\u001b[?9h', 'test\u001b[?9h'); await testSerializeEquals('\u001b[?9l', 'test'); await testSerializeEquals('\u001b[?1000h', 'test\u001b[?1000h'); await testSerializeEquals('\u001b[?1000l', 'test'); await testSerializeEquals('\u001b[?1002h', 'test\u001b[?1002h'); await testSerializeEquals('\u001b[?1002l', 'test'); await testSerializeEquals('\u001b[?1003h', 'test\u001b[?1003h'); await testSerializeEquals('\u001b[?1003l', 'test'); }); test('originMode', async () => { // origin mode moves cursor to (0,0) await testSerializeEquals('test\u001b[?6h', 'test\u001b[4D\u001b[?6h'); await testSerializeEquals('\u001b[?6l', 'test\u001b[4D'); }); test('reverseWraparoundMode', async () => { await testSerializeEquals('test\u001b[?45h', 'test\u001b[?45h'); await testSerializeEquals('\u001b[?45l', 'test'); }); test('sendFocusMode', async () => { await testSerializeEquals('test\u001b[?1004h', 'test\u001b[?1004h'); await testSerializeEquals('\u001b[?1004l', 'test'); }); test('wraparoundMode', async () => { await testSerializeEquals('test\u001b[?7l', 'test\u001b[?7l'); await testSerializeEquals('\u001b[?7h', 'test'); }); }); }); function newArray(initial: T | ((index: number) => T), count: number): T[] { const array: T[] = new Array(count); for (let i = 0; i < array.length; i++) { if (typeof initial === 'function') { array[i] = (initial as (index: number) => T)(i); } else { array[i] = initial as T; } } return array; } function digitsString(length: number, from: number = 0, sgr: string = ''): string { let s = sgr; for (let i = 0; i < length; i++) { s += `${(from++) % 10}`; } return s; } function sgr(...seq: string[]): string { return `\x1b[${seq.join(';')}m`; } const NORMAL = '0'; const FG_P16_RED = '31'; const FG_P16_GREEN = '32'; const FG_P16_YELLOW = '33'; const FG_P256_RED = '38;5;196'; const FG_P256_GREEN = '38;5;46'; const FG_P256_YELLOW = '38;5;226'; const FG_RGB_RED = '38;2;255;0;0'; const FG_RGB_GREEN = '38;2;0;255;0'; const FG_RGB_YELLOW = '38;2;255;255;0'; const FG_RESET = '39'; const BG_P16_RED = '41'; const BG_P16_GREEN = '42'; const BG_P16_YELLOW = '43'; const BG_P256_RED = '48;5;196'; const BG_P256_GREEN = '48;5;46'; const BG_P256_YELLOW = '48;5;226'; const BG_RGB_RED = '48;2;255;0;0'; const BG_RGB_GREEN = '48;2;0;255;0'; const BG_RGB_YELLOW = '48;2;255;255;0'; const BG_RESET = '49'; const BOLD = '1'; const DIM = '2'; const ITALIC = '3'; const UNDERLINED = '4'; const UNDERLINE_DOUBLE = '4:2'; const UNDERLINE_COLOR_RED = '58;5;196'; const UNDERLINE_COLOR_GREEN = '58;5;46'; const BLINK = '5'; const INVERSE = '7'; const INVISIBLE = '8'; const STRIKETHROUGH = '9'; const OVERLINED = '53'; const NO_BOLD = '22'; const NO_DIM = '22'; const NO_ITALIC = '23'; const NO_UNDERLINED = '24'; const NO_BLINK = '25'; const NO_INVERSE = '27'; const NO_INVISIBLE = '28'; const NO_STRIKETHROUGH = '29'; const NO_OVERLINED = '55'; ================================================ FILE: addons/addon-serialize/test/playwright.config.ts ================================================ import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { testDir: '.', timeout: 10000, projects: [ { name: 'Chromium', use: { browserName: 'chromium' } }, { name: 'FirefoxStable', use: { browserName: 'firefox' } }, { name: 'WebKit', use: { browserName: 'webkit' } } ], reporter: 'list', webServer: { command: 'npm run start', port: 3000, timeout: 120000, reuseExistingServer: !process.env.CI } }; export default config; ================================================ FILE: addons/addon-serialize/test/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "ESNext", "lib": [ "es2021", ], "rootDir": ".", "outDir": "../out-test", "sourceMap": true, "removeComments": true, "paths": { "common/*": [ "../../../src/common/*" ], "browser/*": [ "../../../src/browser/*" ] }, "strict": true, "types": [ "../../../node_modules/@types/node" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" }, { "path": "../../../test/playwright" } ] } ================================================ FILE: addons/addon-serialize/tsconfig.json ================================================ { "files": [], "include": [], "references": [ { "path": "./src" }, { "path": "./test" }, { "path": "./benchmark" } ] } ================================================ FILE: addons/addon-serialize/typings/addon-serialize.d.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal, ITerminalAddon, IMarker, IBufferRange } from '@xterm/xterm'; declare module '@xterm/addon-serialize' { /** * An xterm.js addon that enables serialization of terminal contents. */ export class SerializeAddon implements ITerminalAddon { constructor(); /** * Activates the addon. * @param terminal The terminal the addon is being loaded in. */ public activate(terminal: Terminal): void; /** * Serializes terminal rows into a string that can be written back to the terminal to restore * the state. The cursor will also be positioned to the correct cell. When restoring a terminal * it is best to do before `Terminal.open` is called to avoid wasting CPU cycles rendering * incomplete frames. * * It's recommended that you write the serialized data into a terminal of the same size in which * it originated from and then resize it after if needed. * * @param options Custom options to allow control over what gets serialized. */ public serialize(options?: ISerializeOptions): string; /** * Serializes terminal content as HTML, which can be written to the clipboard using the * `text/html` mimetype. For applications that support it, the pasted text should then retain * its colors/styles. * * @param options Custom options to allow control over what gets serialized. */ public serializeAsHTML(options?: Partial): string; /** * Disposes the addon. */ public dispose(): void; } export interface ISerializeOptions { /** * The row range to serialize. The an explicit range is specified, the cursor will get its final * repositioning. */ range?: ISerializeRange; /** * The number of rows in the scrollback buffer to serialize, starting from the bottom of the * scrollback buffer. When not specified, all available rows in the scrollback buffer will be * serialized. This will be ignored if {@link range} is specified. */ scrollback?: number; /** * Whether to exclude the terminal modes from the serialization. False by default. */ excludeModes?: boolean; /** * Whether to exclude the alt buffer from the serialization. False by default. */ excludeAltBuffer?: boolean; } export interface IHTMLSerializeOptions { /** * The number of rows in the scrollback buffer to serialize, starting from the bottom of the * scrollback buffer. When not specified, all available rows in the scrollback buffer will be * serialized. This setting is ignored if {@link IHTMLSerializeOptions.onlySelection} is true. */ scrollback: number; /** * Whether to only serialize the selection. If false, the whole active buffer is serialized in HTML. * False by default. */ onlySelection: boolean; /** * Whether to include the global background of the terminal. False by default. */ includeGlobalBackground: boolean; /** * The range to serialize. This is prioritized over {@link onlySelection}. */ range?: ISerializeBufferRange; } export interface ISerializeBufferRange { startLine: number; endLine: number; startCol: number; } export interface ISerializeRange { /** * The line to start serializing (inclusive). */ start: IMarker | number; /** * The line to end serializing (inclusive). */ end: IMarker | number; } } ================================================ FILE: addons/addon-serialize/webpack.config.js ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ const path = require('path'); const addonName = 'SerializeAddon'; const mainFile = 'addon-serialize.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', // Force usage of globalThis instead of global / self. (This is cross-env compatible) globalObject: 'globalThis', }, mode: 'production' }; ================================================ FILE: addons/addon-unicode-graphemes/.gitignore ================================================ lib node_modules out-benchmark ================================================ FILE: addons/addon-unicode-graphemes/.npmignore ================================================ # Blacklist - exclude everything except npm defaults such as LICENSE, etc * !*/ # Whitelist - lib/ !lib/**/*.d.ts !lib/**/*.js !lib/**/*.js.map !lib/**/*.mjs !lib/**/*.mjs.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 ================================================ FILE: addons/addon-unicode-graphemes/LICENSE ================================================ Copyright (c) 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. ================================================ FILE: addons/addon-unicode-graphemes/README.md ================================================ ## @xterm/addon-unicode-graphemes ⚠️ **This addon is currently experimental and may introduce unexpected and non-standard behavior** An addon providing enhanced Unicode support (include grapheme clustering) for xterm.js. The file `src/UnicodeProperties.ts` is generated and depends on the Unicode version. See [the unicode-properties project](https://github.com/PerBothner/unicode-properties) for credits and re-generation instructions. ### Install This addon is not yet published to npm ### Usage ```ts import { Terminal } from '@xterm/xterm'; import { UnicodeGraphemesAddon } from '@xterm/addon-unicode-graphemes'; const terminal = new Terminal(); const unicodeGraphemesAddon = new UnicodeGraphemesAddon(); terminal.loadAddon(unicodeGraphemesAddon); ``` ================================================ FILE: addons/addon-unicode-graphemes/benchmark/UnicodeGraphemeAddon.benchmark.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark'; import { spawn } from 'node-pty'; import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder'; import { CoreBrowserTerminal } from 'browser/CoreBrowserTerminal'; import { UnicodeGraphemeProvider } from 'UnicodeGraphemeProvider'; function fakedAddonLoad(terminal: any): void { // resembles what UnicodeGraphemesAddon.activate does under the hood terminal.unicodeService.register(new UnicodeGraphemeProvider()); terminal.unicodeService.activeVersion = '15-graphemes'; } perfContext('Terminal: ls -lR /usr/lib', () => { let content = ''; let contentUtf8: Uint8Array; before(async () => { // grab output from "ls -lR /usr" const p = spawn('ls', ['--color=auto', '-lR', '/usr/lib'], { name: 'xterm-256color', cols: 80, rows: 25, cwd: process.env.HOME, env: process.env, encoding: (null as unknown as string) // needs to be fixed in node-pty }); const chunks: Buffer[] = []; let length = 0; p.onData(data => { chunks.push(data as unknown as Buffer); length += data.length; }); await new Promise(resolve => p.onExit(() => resolve())); contentUtf8 = Buffer.concat(chunks, length); // translate to content string const buffer = new Uint32Array(contentUtf8.length); const decoder = new Utf8ToUtf32(); const codepoints = decoder.decode(contentUtf8, buffer); for (let i = 0; i < codepoints; ++i) { content += stringFromCodePoint(buffer[i]); // peek into content to force flat repr in v8 if (!(i % 10000000)) { content[i]; } } }); perfContext('write/string/async', () => { let terminal: CoreBrowserTerminal; before(() => { terminal = new CoreBrowserTerminal({ cols: 80, rows: 25, scrollback: 1000 }); fakedAddonLoad(terminal); }); new ThroughputRuntimeCase('', async () => { await new Promise(res => terminal.write(content, res)); return { payloadSize: contentUtf8.length }; }, { fork: false }).showAverageThroughput(); }); perfContext('write/Utf8/async', () => { let terminal: CoreBrowserTerminal; before(() => { terminal = new CoreBrowserTerminal({ cols: 80, rows: 25, scrollback: 1000 }); }); new ThroughputRuntimeCase('', async () => { await new Promise(res => terminal.write(content, res)); return { payloadSize: contentUtf8.length }; }, { fork: false }).showAverageThroughput(); }); }); ================================================ FILE: addons/addon-unicode-graphemes/benchmark/benchmark.json ================================================ { "APP_PATH": ".benchmark", "evalConfig": { "tolerance": { "*": [0.75, 1.5], "*.dev": [0.01, 1.5], "*.cv": [0.01, 1.5], "EscapeSequenceParser.benchmark.js.*.averageThroughput.mean": [0.9, 5] }, "skip": [ "*.median", "*.runs", "*.dev", "*.cv", "EscapeSequenceParser.benchmark.js.*.averageRuntime", "Terminal.benchmark.js.*.averageRuntime" ] } } ================================================ FILE: addons/addon-unicode-graphemes/benchmark/tsconfig.json ================================================ { "compilerOptions": { "lib": ["dom", "es6"], "rootDir": "..", "outDir": "../out-benchmark", "types": ["../../../node_modules/@types/node"], "moduleResolution": "node", "strict": false, "target": "es2015", "module": "commonjs", "paths": { "common/*": ["../../../src/common/*"], "browser/*": ["../../../src/browser/*"], "UnicodeGraphemeProvider": ["../src/UnicodeGraphemeProvider"], "@xterm/addon-unicode-graphemes": [ "../typings/addon-unicode-graphemes.d.ts" ] } }, "include": ["../**/*", "../../../typings/xterm.d.ts"], "exclude": ["../../../**/*test.ts", "../../**/*api.ts"], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" } ] } ================================================ FILE: addons/addon-unicode-graphemes/package.json ================================================ { "name": "@xterm/addon-unicode-graphemes", "version": "0.4.0", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, "main": "lib/addon-unicode-graphemes.js", "module": "lib/addon-unicode-graphemes.mjs", "types": "typings/addon-unicode-graphemes.d.ts", "repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-unicode-graphemes", "license": "MIT", "keywords": [ "terminal", "xterm", "xterm.js" ], "scripts": { "build": "../../node_modules/.bin/tsgo -p .", "prepackage": "npm run build", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", "start": "node ../../demo/start", "benchmark": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json out-benchmark/benchmark/*benchmark.js", "benchmark-baseline": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/benchmark/*benchmark.js", "benchmark-eval": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js" } } ================================================ FILE: addons/addon-unicode-graphemes/src/UnicodeGraphemeProvider.ts ================================================ /** * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT */ import { IUnicodeVersionProvider } from '@xterm/xterm'; import { UnicodeCharProperties, UnicodeCharWidth } from 'common/services/Services'; import { UnicodeService } from 'common/services/UnicodeService'; import * as UC from './third-party/UnicodeProperties'; export class UnicodeGraphemeProvider implements IUnicodeVersionProvider { public readonly version; public ambiguousCharsAreWide: boolean = false; public readonly handleGraphemes: boolean; constructor(handleGraphemes: boolean = true) { this.version = handleGraphemes ? '15-graphemes' : '15'; this.handleGraphemes = handleGraphemes; } private static readonly _plainNarrowProperties: UnicodeCharProperties = UnicodeService.createPropertyValue(UC.GRAPHEME_BREAK_Other, 1, false); public charProperties(codepoint: number, preceding: UnicodeCharProperties): UnicodeCharProperties { // Optimize the simple ASCII case, under the condition that // UnicodeService.extractCharKind(preceding) === GRAPHEME_BREAK_Other // (which also covers the case that preceding === 0). if ((codepoint >= 32 && codepoint < 127) && (preceding >> 3) === 0) { return UnicodeGraphemeProvider._plainNarrowProperties; } let charInfo = UC.getInfo(codepoint); let w = UC.infoToWidthInfo(charInfo); let shouldJoin = false; if (w >= 2) { // Treat emoji_presentation_selector as WIDE. w = w === 3 || this.ambiguousCharsAreWide || codepoint === 0xfe0f ? 2 : 1; } else { w = 1; } if (preceding !== 0) { const oldWidth = UnicodeService.extractWidth(preceding); if (this.handleGraphemes) { charInfo = UC.shouldJoin(UnicodeService.extractCharKind(preceding), charInfo); } else { charInfo = w === 0 ? 1 : 0; } shouldJoin = charInfo > 0; if (shouldJoin) { if (oldWidth > w) { w = oldWidth; } else if (charInfo === 32) { // UC.GRAPHEME_BREAK_SAW_Regional_Pair) w = 2; } } } return UnicodeService.createPropertyValue(charInfo, w, shouldJoin); } public wcwidth(codepoint: number): UnicodeCharWidth { const charInfo = UC.getInfo(codepoint); const w = UC.infoToWidthInfo(charInfo); const kind = (charInfo & UC.GRAPHEME_BREAK_MASK) >> UC.GRAPHEME_BREAK_SHIFT; if (kind === UC.GRAPHEME_BREAK_Extend || kind === UC.GRAPHEME_BREAK_Prepend) { return 0; } if (w >= 2 && (w === 3 || this.ambiguousCharsAreWide)) { return 2; } return 1; } } ================================================ FILE: addons/addon-unicode-graphemes/src/UnicodeGraphemesAddon.ts ================================================ /** * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT * * UnicodeVersionProvider for V15 with grapeme cluster handleing. */ import type { Terminal, ITerminalAddon, IUnicodeHandling } from '@xterm/xterm'; import type { UnicodeGraphemesAddon as IUnicodeGraphemesApi } from '@xterm/addon-unicode-graphemes'; import { UnicodeGraphemeProvider } from './UnicodeGraphemeProvider'; export class UnicodeGraphemesAddon implements ITerminalAddon, IUnicodeGraphemesApi { private _provider15Graphemes?: UnicodeGraphemeProvider; private _provider15?: UnicodeGraphemeProvider; private _unicode?: IUnicodeHandling; private _oldVersion: string = ''; public activate(terminal: Terminal): void { this._provider15 ??= new UnicodeGraphemeProvider(false); this._provider15Graphemes ??= new UnicodeGraphemeProvider(true); const unicode = terminal.unicode; this._unicode = unicode; unicode.register(this._provider15); unicode.register(this._provider15Graphemes); this._oldVersion = unicode.activeVersion; unicode.activeVersion = '15-graphemes'; } public dispose(): void { if (this._unicode) { this._unicode.activeVersion = this._oldVersion; } } } ================================================ FILE: addons/addon-unicode-graphemes/src/third-party/UnicodeProperties.ts ================================================ import UnicodeTrie from './unicode-trie'; const trieRaw = "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"; declare const Buffer: { from(s: string, encoding: string): Uint8Array } | undefined; function _dec(s: string): Uint8Array { if (typeof Buffer !== 'undefined') return Buffer.from(s, 'base64'); const bs = atob(s); const r = new Uint8Array(bs.length); for (let i = 0; i < r.length; ++i) r[i] = bs.charCodeAt(i); return r; } const trieData = new UnicodeTrie(_dec(trieRaw)); export const GRAPHEME_BREAK_MASK = 0xF; export const GRAPHEME_BREAK_SHIFT = 0; export const CHARWIDTH_MASK = 0x30; export const CHARWIDTH_SHIFT = 4; // Values for the GRAPHEME_BREAK property export const GRAPHEME_BREAK_Other = 0; // includes CR, LF, Control export const GRAPHEME_BREAK_Prepend = 1; export const GRAPHEME_BREAK_Extend = 2; export const GRAPHEME_BREAK_Regional_Indicator = 3; export const GRAPHEME_BREAK_SpacingMark = 4; export const GRAPHEME_BREAK_Hangul_L = 5; export const GRAPHEME_BREAK_Hangul_V = 6; export const GRAPHEME_BREAK_Hangul_T = 7; export const GRAPHEME_BREAK_Hangul_LV = 8; export const GRAPHEME_BREAK_Hangul_LVT = 9; export const GRAPHEME_BREAK_ZWJ = 10; export const GRAPHEME_BREAK_ExtPic = 11; // Only used as return value from shouldJoin/shouldJoinBackwards. // (Must be positive; distinct from other values; // and become GRAPHEME_BREAK_Other when masked with GRAPHEME_BREAK_MASK.) const GRAPHEME_BREAK_SAW_Regional_Pair = 32; export const CHARWIDTH_NORMAL = 0; export const CHARWIDTH_FORCE_1COLUMN = 1; export const CHARWIDTH_EA_AMBIGUOUS = 2; export const CHARWIDTH_WIDE = 3; // In the following 'info' is an encoded value from trie.get(codePoint) // In the following 'info' is an encoded value from trie.get(codePoint) export function infoToWidthInfo(info: number): number { return (info & CHARWIDTH_MASK) >> CHARWIDTH_SHIFT; } export function infoToWidth(info: number, ambiguousIsWide = false): 0 | 1 |2 { const v = infoToWidthInfo(info); return v < CHARWIDTH_EA_AMBIGUOUS ? 1 : v >= CHARWIDTH_WIDE || ambiguousIsWide ? 2 : 1; } export function strWidth(str: string, preferWide: boolean): number { let width = 0; for (let i = 0; i < str.length;) { const codePoint = str.codePointAt(i) as number; width += infoToWidth(getInfo(codePoint), preferWide); i += (codePoint <= 0xffff) ? 1 : 2; } return width; } export function columnToIndexInContext(str: string, startIndex: number, column: number, preferWide: boolean): number { let rv = 0; for (let i = startIndex; ;) { if (i >= str.length) return i; const codePoint = str.codePointAt(i) as number; const w = infoToWidth(getInfo(codePoint), preferWide); rv += w; if (rv > column) return i; i += (codePoint <= 0xffff) ? 1 : 2; } } // Test if should break between beforeState and afterCode. // Return <= 0 if should break; > 0 if should join. // 'beforeState' is the return value from the previous possible break; // the value 0 is start of string. // 'afterCode' is the GRAPHEME_BREAK_Xxx value for the following codepoint. export function shouldJoin(beforeState: number, afterInfo: number): number { let beforeCode = (beforeState & GRAPHEME_BREAK_MASK) >> GRAPHEME_BREAK_SHIFT; let afterCode = (afterInfo & GRAPHEME_BREAK_MASK) >> GRAPHEME_BREAK_SHIFT; if (_shouldJoin(beforeCode, afterCode)) { if (afterCode === GRAPHEME_BREAK_Regional_Indicator) return GRAPHEME_BREAK_SAW_Regional_Pair; else return afterCode + 16; } else return afterCode - 16; } export function shouldJoinBackwards(beforeInfo: number, afterState: number): number { let afterCode = (afterState & GRAPHEME_BREAK_MASK) >> GRAPHEME_BREAK_SHIFT; let beforeCode = (beforeInfo & GRAPHEME_BREAK_MASK) >> GRAPHEME_BREAK_SHIFT; if (_shouldJoin(beforeCode, afterCode)) { if (beforeCode === GRAPHEME_BREAK_Regional_Indicator) return GRAPHEME_BREAK_SAW_Regional_Pair; else return beforeCode + 16; } else return beforeCode - 16; } /** Doesn't handle an odd number of RI characters. */ function _shouldJoin(beforeCode: number, afterCode: number): boolean { if (beforeCode >= GRAPHEME_BREAK_Hangul_L && beforeCode <= GRAPHEME_BREAK_Hangul_LVT) { if (beforeCode == GRAPHEME_BREAK_Hangul_L // GB6 && (afterCode == GRAPHEME_BREAK_Hangul_L || afterCode == GRAPHEME_BREAK_Hangul_V || afterCode == GRAPHEME_BREAK_Hangul_LV || afterCode == GRAPHEME_BREAK_Hangul_LVT)) return true; if ((beforeCode == GRAPHEME_BREAK_Hangul_LV // GB7 || beforeCode == GRAPHEME_BREAK_Hangul_V) && (afterCode == GRAPHEME_BREAK_Hangul_V || afterCode == GRAPHEME_BREAK_Hangul_T)) return true; if ((beforeCode == GRAPHEME_BREAK_Hangul_LVT // GB8 || beforeCode == GRAPHEME_BREAK_Hangul_T) && afterCode == GRAPHEME_BREAK_Hangul_T) return true; } if (afterCode == GRAPHEME_BREAK_Extend // GB9 || afterCode == GRAPHEME_BREAK_ZWJ || beforeCode == GRAPHEME_BREAK_Prepend // GB9a || afterCode == GRAPHEME_BREAK_SpacingMark) // GB9b return true; if (beforeCode == GRAPHEME_BREAK_ZWJ // GB11 && afterCode == GRAPHEME_BREAK_ExtPic) return true; if (afterCode == GRAPHEME_BREAK_Regional_Indicator // GB12, GB13 && beforeCode == GRAPHEME_BREAK_Regional_Indicator) return true; return false; } export function getInfo(codePoint: number): number { return trieData.get(codePoint); } ================================================ FILE: addons/addon-unicode-graphemes/src/third-party/tiny-inflate.ts ================================================ var TINF_OK = 0; var TINF_DATA_ERROR = -3; class Tree { table = new Uint16Array(16); /* table of code length counts */ trans = new Uint16Array(288); /* code -> symbol translation table */ }; class Data { tag: number = 0; bitcount: number = 0; destLen: number = 0; ltree: Tree; dtree: Tree; source: Uint8Array; dest: Uint8Array; sourceIndex: number = 0; constructor(source: Uint8Array, dest: Uint8Array) { this.source = source; this.dest = dest; this.ltree = new Tree(); /* dynamic length/symbol tree */ this.dtree = new Tree(); /* dynamic distance tree */ } } /* --------------------------------------------------- * * -- uninitialized global data (static structures) -- * * --------------------------------------------------- */ var sltree = new Tree(); var sdtree = new Tree(); /* extra bits and base tables for length codes */ var length_bits = new Uint8Array(30); var length_base = new Uint16Array(30); /* extra bits and base tables for distance codes */ var dist_bits = new Uint8Array(30); var dist_base = new Uint16Array(30); /* special ordering of code length codes */ var clcidx = new Uint8Array([ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]); /* used by tinf_decode_trees, avoids allocations every call */ const code_tree = new Tree(); const lengths = new Uint8Array(288 + 32); /* ----------------------- * * -- utility functions -- * * ----------------------- */ /* build extra bits and base tables */ function tinf_build_bits_base(bits: Uint8Array, base: Uint16Array, delta: number, first: number): void { var i, sum; /* build bits table */ for (i = 0; i < delta; ++i) bits[i] = 0; for (i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta | 0; /* build base table */ for (sum = first, i = 0; i < 30; ++i) { base[i] = sum; sum += 1 << bits[i]; } } /* build the fixed huffman trees */ function tinf_build_fixed_trees(lt: Tree, dt: Tree): void { var i; /* build fixed length tree */ for (i = 0; i < 7; ++i) lt.table[i] = 0; lt.table[7] = 24; lt.table[8] = 152; lt.table[9] = 112; for (i = 0; i < 24; ++i) lt.trans[i] = 256 + i; for (i = 0; i < 144; ++i) lt.trans[24 + i] = i; for (i = 0; i < 8; ++i) lt.trans[24 + 144 + i] = 280 + i; for (i = 0; i < 112; ++i) lt.trans[24 + 144 + 8 + i] = 144 + i; /* build fixed distance tree */ for (i = 0; i < 5; ++i) dt.table[i] = 0; dt.table[5] = 32; for (i = 0; i < 32; ++i) dt.trans[i] = i; } /* given an array of code lengths, build a tree */ var offs = new Uint16Array(16); function tinf_build_tree(t: Tree, lengths: Uint8Array, off: number, num: number): void { var i, sum; /* clear code length count table */ for (i = 0; i < 16; ++i) t.table[i] = 0; /* scan symbol lengths, and sum code length counts */ for (i = 0; i < num; ++i) t.table[lengths[off + i]]++; t.table[0] = 0; /* compute offset table for distribution sort */ for (sum = 0, i = 0; i < 16; ++i) { offs[i] = sum; sum += t.table[i]; } /* create code->symbol translation table (symbols sorted by code) */ for (i = 0; i < num; ++i) { if (lengths[off + i]) t.trans[offs[lengths[off + i]]++] = i; } } /* ---------------------- * * -- decode functions -- * * ---------------------- */ /* get one bit from source stream */ function tinf_getbit(d: Data): number { /* check if tag is empty */ if (!d.bitcount--) { /* load next tag */ d.tag = d.source[d.sourceIndex++]; d.bitcount = 7; } /* shift bit out of tag */ var bit = d.tag & 1; d.tag >>>= 1; return bit; } /* read a num bit value from a stream and add base */ function tinf_read_bits(d: Data, num: number, base: number): number { if (!num) return base; while (d.bitcount < 24) { d.tag |= d.source[d.sourceIndex++] << d.bitcount; d.bitcount += 8; } var val = d.tag & (0xffff >>> (16 - num)); d.tag >>>= num; d.bitcount -= num; return val + base; } /* given a data stream and a tree, decode a symbol */ function tinf_decode_symbol(d: Data, t: Tree): number { while (d.bitcount < 24) { d.tag |= d.source[d.sourceIndex++] << d.bitcount; d.bitcount += 8; } var sum = 0, cur = 0, len = 0; var tag = d.tag; /* get more bits while code value is above sum */ do { cur = 2 * cur + (tag & 1); tag >>>= 1; ++len; sum += t.table[len]; cur -= t.table[len]; } while (cur >= 0); d.tag = tag; d.bitcount -= len; return t.trans[sum + cur]; } /* given a data stream, decode dynamic trees from it */ function tinf_decode_trees(d: Data, lt: Tree, dt: Tree): void { var hlit, hdist, hclen; var i, num, length; /* get 5 bits HLIT (257-286) */ hlit = tinf_read_bits(d, 5, 257); /* get 5 bits HDIST (1-32) */ hdist = tinf_read_bits(d, 5, 1); /* get 4 bits HCLEN (4-19) */ hclen = tinf_read_bits(d, 4, 4); for (i = 0; i < 19; ++i) lengths[i] = 0; /* read code lengths for code length alphabet */ for (i = 0; i < hclen; ++i) { /* get 3 bits code length (0-7) */ var clen = tinf_read_bits(d, 3, 0); lengths[clcidx[i]] = clen; } /* build code length tree */ tinf_build_tree(code_tree, lengths, 0, 19); /* decode code lengths for the dynamic trees */ for (num = 0; num < hlit + hdist;) { var sym = tinf_decode_symbol(d, code_tree); switch (sym) { case 16: /* copy previous code length 3-6 times (read 2 bits) */ var prev = lengths[num - 1]; for (length = tinf_read_bits(d, 2, 3); length; --length) { lengths[num++] = prev; } break; case 17: /* repeat code length 0 for 3-10 times (read 3 bits) */ for (length = tinf_read_bits(d, 3, 3); length; --length) { lengths[num++] = 0; } break; case 18: /* repeat code length 0 for 11-138 times (read 7 bits) */ for (length = tinf_read_bits(d, 7, 11); length; --length) { lengths[num++] = 0; } break; default: /* values 0-15 represent the actual code lengths */ lengths[num++] = sym; break; } } /* build dynamic trees */ tinf_build_tree(lt, lengths, 0, hlit); tinf_build_tree(dt, lengths, hlit, hdist); } /* ----------------------------- * * -- block inflate functions -- * * ----------------------------- */ /* given a stream and two trees, inflate a block of data */ function tinf_inflate_block_data(d: Data, lt: Tree, dt: Tree): number { for (;;) { var sym = tinf_decode_symbol(d, lt); /* check for end of block */ if (sym === 256) { return TINF_OK; } if (sym < 256) { d.dest[d.destLen++] = sym; } else { var length, dist, offs; var i; sym -= 257; /* possibly get more bits from length code */ length = tinf_read_bits(d, length_bits[sym], length_base[sym]); dist = tinf_decode_symbol(d, dt); /* possibly get more bits from distance code */ offs = d.destLen - tinf_read_bits(d, dist_bits[dist], dist_base[dist]); /* copy match */ for (i = offs; i < offs + length; ++i) { d.dest[d.destLen++] = d.dest[i]; } } } } /* inflate an uncompressed block of data */ function tinf_inflate_uncompressed_block(d: Data) { var length, invlength; var i; /* unread from bitbuffer */ while (d.bitcount > 8) { d.sourceIndex--; d.bitcount -= 8; } /* get length */ length = d.source[d.sourceIndex + 1]; length = 256 * length + d.source[d.sourceIndex]; /* get one's complement of length */ invlength = d.source[d.sourceIndex + 3]; invlength = 256 * invlength + d.source[d.sourceIndex + 2]; /* check length */ if (length !== (~invlength & 0x0000ffff)) return TINF_DATA_ERROR; d.sourceIndex += 4; /* copy block */ for (i = length; i; --i) d.dest[d.destLen++] = d.source[d.sourceIndex++]; /* make sure we start next block on a byte boundary */ d.bitcount = 0; return TINF_OK; } /* inflate stream from source to dest */ function tinf_uncompress(source: Uint8Array, dest: Uint8Array) { var d = new Data(source, dest); var bfinal, btype, res; do { /* read final block flag */ bfinal = tinf_getbit(d); /* read block type (2 bits) */ btype = tinf_read_bits(d, 2, 0); /* decompress block */ switch (btype) { case 0: /* decompress uncompressed block */ res = tinf_inflate_uncompressed_block(d); break; case 1: /* decompress block with fixed huffman trees */ res = tinf_inflate_block_data(d, sltree, sdtree); break; case 2: /* decompress block with dynamic huffman trees */ tinf_decode_trees(d, d.ltree, d.dtree); res = tinf_inflate_block_data(d, d.ltree, d.dtree); break; default: res = TINF_DATA_ERROR; } if (res !== TINF_OK) throw new Error('Data error'); } while (!bfinal); if (d.destLen < d.dest.length) { if (typeof d.dest.slice === 'function') return d.dest.slice(0, d.destLen); else return d.dest.subarray(0, d.destLen); } return d.dest; } /* -------------------- * * -- initialization -- * * -------------------- */ /* build fixed huffman trees */ tinf_build_fixed_trees(sltree, sdtree); /* build extra bits and base tables */ tinf_build_bits_base(length_bits, length_base, 4, 3); tinf_build_bits_base(dist_bits, dist_base, 2, 1); /* fix a special case */ length_bits[28] = 0; length_base[28] = 258; export default tinf_uncompress ================================================ FILE: addons/addon-unicode-graphemes/src/third-party/unicode-trie.ts ================================================ import inflate from './tiny-inflate' // Shift size for getting the index-1 table offset. const SHIFT_1 = 6 + 5; // Shift size for getting the index-2 table offset. const SHIFT_2 = 5; // Difference between the two shift sizes, // for getting an index-1 offset from an index-2 offset. 6=11-5 const SHIFT_1_2 = SHIFT_1 - SHIFT_2; // Number of index-1 entries for the BMP. 32=0x20 // This part of the index-1 table is omitted from the serialized form. const OMITTED_BMP_INDEX_1_LENGTH = 0x10000 >> SHIFT_1; // Number of entries in an index-2 block. 64=0x40 const INDEX_2_BLOCK_LENGTH = 1 << SHIFT_1_2; // Mask for getting the lower bits for the in-index-2-block offset. */ const INDEX_2_MASK = INDEX_2_BLOCK_LENGTH - 1; // Shift size for shifting left the index array values. // Increases possible data size with 16-bit index values at the cost // of compactability. // This requires data blocks to be aligned by DATA_GRANULARITY. const INDEX_SHIFT = 2; // Number of entries in a data block. 32=0x20 const DATA_BLOCK_LENGTH = 1 << SHIFT_2; // Mask for getting the lower bits for the in-data-block offset. const DATA_MASK = DATA_BLOCK_LENGTH - 1; // The part of the index-2 table for U+D800..U+DBFF stores values for // lead surrogate code _units_ not code _points_. // Values for lead surrogate code _points_ are indexed with this portion of the table. // Length=32=0x20=0x400>>SHIFT_2. (There are 1024=0x400 lead surrogates.) const LSCP_INDEX_2_OFFSET = 0x10000 >> SHIFT_2; const LSCP_INDEX_2_LENGTH = 0x400 >> SHIFT_2; // Count the lengths of both BMP pieces. 2080=0x820 const INDEX_2_BMP_LENGTH = LSCP_INDEX_2_OFFSET + LSCP_INDEX_2_LENGTH; // The 2-byte UTF-8 version of the index-2 table follows at offset 2080=0x820. // Length 32=0x20 for lead bytes C0..DF, regardless of SHIFT_2. const UTF8_2B_INDEX_2_OFFSET = INDEX_2_BMP_LENGTH; const UTF8_2B_INDEX_2_LENGTH = 0x800 >> 6; // U+0800 is the first code point after 2-byte UTF-8 // The index-1 table, only used for supplementary code points, at offset 2112=0x840. // Variable length, for code points up to highStart, where the last single-value range starts. // Maximum length 512=0x200=0x100000>>SHIFT_1. // (For 0x100000 supplementary code points U+10000..U+10ffff.) // // The part of the index-2 table for supplementary code points starts // after this index-1 table. // // Both the index-1 table and the following part of the index-2 table // are omitted completely if there is only BMP data. const INDEX_1_OFFSET = UTF8_2B_INDEX_2_OFFSET + UTF8_2B_INDEX_2_LENGTH; // The alignment size of a data block. Also the granularity for compaction. const DATA_GRANULARITY = 1 << INDEX_SHIFT; const isBigEndian = (new Uint8Array(new Uint32Array([0x12345678]).buffer)[0] === 0x12); class UnicodeTrie { private data: Uint32Array; private highStart: number; private errorValue: number; constructor(data: Uint8Array) { // read binary format const view = new DataView(data.buffer); this.highStart = view.getUint32(0, true); this.errorValue = view.getUint32(4, true); let uncompressedLength = view.getUint32(8, true); data = data.subarray(12); // double inflate the actual trie data data = inflate(data, new Uint8Array(uncompressedLength)); data = inflate(data, new Uint8Array(uncompressedLength)); if (isBigEndian) { // swap bytes from little-endian const len = data.length; for (let i = 0; i < len; i += 4) { // Exchange data[i] and data[i + 3]: let x = data[i]; data[i] = data[i+3]; data[i+3] = x; // Exchange data[i + 1] and data[i + 2]: let y = data[i+1]; data[i+1] = data[i+2]; data[i+2] = y; } } this.data = new Uint32Array(data.buffer); } get(codePoint: number): number { let index; if ((codePoint < 0) || (codePoint > 0x10ffff)) { return this.errorValue; } if ((codePoint < 0xd800) || ((codePoint > 0xdbff) && (codePoint <= 0xffff))) { // Ordinary BMP code point, excluding leading surrogates. // BMP uses a single level lookup. BMP index starts at offset 0 in the index. // data is stored in the index array itself. index = (this.data[codePoint >> SHIFT_2] << INDEX_SHIFT) + (codePoint & DATA_MASK); return this.data[index]; } if (codePoint <= 0xffff) { // Lead Surrogate Code Point. A Separate index section is stored for // lead surrogate code units and code points. // The main index has the code unit data. // For this function, we need the code point data. index = (this.data[LSCP_INDEX_2_OFFSET + ((codePoint - 0xd800) >> SHIFT_2)] << INDEX_SHIFT) + (codePoint & DATA_MASK); return this.data[index]; } if (codePoint < this.highStart) { // Supplemental code point, use two-level lookup. index = this.data[(INDEX_1_OFFSET - OMITTED_BMP_INDEX_1_LENGTH) + (codePoint >> SHIFT_1)]; index = this.data[index + ((codePoint >> SHIFT_2) & INDEX_2_MASK)]; index = (index << INDEX_SHIFT) + (codePoint & DATA_MASK); return this.data[index]; } return this.data[this.data.length - DATA_GRANULARITY]; } } export default UnicodeTrie ================================================ FILE: addons/addon-unicode-graphemes/src/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es2021", "lib": [ "dom", "es2021" ], "rootDir": ".", "outDir": "../out", "sourceMap": true, "removeComments": true, "strict": true, "paths": { "common/*": [ "../../../src/common/*" ], "@xterm/addon-unicode-graphemes": [ "../typings/addon-unicode-graphemes.d.ts" ] }, "types": [ "../../../node_modules/@types/mocha" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" } ] } ================================================ FILE: addons/addon-unicode-graphemes/test/UnicodeGraphemesAddon.test.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import test from '@playwright/test'; import { deepStrictEqual, strictEqual } from 'assert'; import { ITestContext, createTestContext, openTerminal } from '../../../test/playwright/TestUtils'; let ctx: ITestContext; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); await openTerminal(ctx); }); test.afterAll(async () => await ctx.page.close()); test.describe('UnicodeGraphemesAddon', () => { test.beforeEach(async () => { await ctx.page.evaluate(` window.term.reset() window.unicode?.dispose(); window.unicode = new UnicodeGraphemesAddon(); window.term.loadAddon(window.unicode); `); }); async function evalWidth(str: string): Promise { return ctx.page.evaluate(`window.term._core.unicodeService.getStringCellWidth('${str}')`); } const ourVersion = '15-graphemes'; test('wcwidth V15 emoji test', async () => { // should have loaded '15-graphemes' deepStrictEqual(await ctx.page.evaluate(`window.term.unicode.versions`), ['6', '15', '15-graphemes']); // switch should not throw await ctx.page.evaluate(`window.term.unicode.activeVersion = '${ourVersion}';`); strictEqual(await ctx.page.evaluate(`window.term.unicode.activeVersion`), ourVersion); strictEqual(await evalWidth('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣'), 20, '10 emoji - width 10 in V6; 20 in V11 or later'); strictEqual(await evalWidth('\u{1F476}\u{1F3FF}\u{1F476}'), 4, 'baby with emoji modifier fitzpatrick type-6; baby'); strictEqual(await evalWidth('\u{1F469}\u200d\u{1f469}\u200d\u{1f466}'), 2, 'woman+zwj+woman+zwj+boy'); strictEqual(await evalWidth('=\u{1F3CB}\u{FE0F}=\u{F3CB}\u{1F3FE}\u200D\u2640='), 7, 'person lifting weights (plain, emoji); woman lighting weights, medium dark'); strictEqual(await evalWidth('\u{1F469}\u{1F469}\u{200D}\u{1F393}\u{1F468}\u{1F3FF}\u{200D}\u{1F393}'), 6, 'woman; woman student; man student dark'); strictEqual(await evalWidth('\u{1f1f3}\u{1f1f4}/'), 3, 'regional indicator symbol letters N and O, cluster'); strictEqual(await evalWidth('\u{1f1f3}/\u{1f1f4}'), 3, 'regional indicator symbol letters N and O, separated'); strictEqual(await evalWidth('\u0061\u0301'), 1, 'letter a with acute accent'); strictEqual(await evalWidth('{\u1100\u1161\u11a8\u1100\u1161}'), 6, 'Korean Jamo'); strictEqual(await evalWidth('\uAC00=\uD685='), 6, 'Hangul syllables (pre-composed)'); strictEqual(await evalWidth('(\u26b0\ufe0e)'), 3, 'coffin with text presentation'); strictEqual(await evalWidth('(\u26b0\ufe0f)'), 4, 'coffin with emoji presentation'); strictEqual(await evalWidth(''), 16, 'Égalité (using separate acute) emoij_presentation'); }); }); ================================================ FILE: addons/addon-unicode-graphemes/test/playwright.config.ts ================================================ import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { testDir: '.', timeout: 10000, projects: [ { name: 'Chromium', use: { browserName: 'chromium' } }, { name: 'FirefoxStable', use: { browserName: 'firefox' } }, { name: 'WebKit', use: { browserName: 'webkit' } } ], reporter: 'list', webServer: { command: 'npm run start', port: 3000, timeout: 120000, reuseExistingServer: !process.env.CI } }; export default config; ================================================ FILE: addons/addon-unicode-graphemes/test/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "ESNext", "lib": [ "es2021", ], "rootDir": ".", "outDir": "../out-test", "sourceMap": true, "removeComments": true, "paths": { "common/*": [ "../../../src/common/*" ], "browser/*": [ "../../../src/browser/*" ] }, "strict": true, "types": [ "../../../node_modules/@types/node" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" }, { "path": "../../../test/playwright" } ] } ================================================ FILE: addons/addon-unicode-graphemes/tsconfig.json ================================================ { "files": [], "include": [], "references": [ { "path": "./src" }, { "path": "./test" }, { "path": "./benchmark" } ] } ================================================ FILE: addons/addon-unicode-graphemes/typings/addon-unicode-graphemes.d.ts ================================================ /** * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal, ITerminalAddon } from '@xterm/xterm'; declare module '@xterm/addon-unicode-graphemes' { export class UnicodeGraphemesAddon implements ITerminalAddon { constructor(); public activate(terminal: Terminal): void; public dispose(): void; } } ================================================ FILE: addons/addon-unicode-graphemes/webpack.config.js ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ const path = require('path'); const addonName = 'UnicodeGraphemesAddon'; const mainFile = 'addon-unicode-graphemes.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'), vs: path.resolve('../../out/vs') } }, output: { filename: mainFile, path: path.resolve('./lib'), library: addonName, libraryTarget: 'umd', // Force usage of globalThis instead of global / self. (This is cross-env compatible) globalObject: 'globalThis', }, mode: 'production' }; ================================================ FILE: addons/addon-unicode11/.gitignore ================================================ lib node_modules ================================================ FILE: addons/addon-unicode11/.npmignore ================================================ # Blacklist - exclude everything except npm defaults such as LICENSE, etc * !*/ # Whitelist - lib/ !lib/**/*.d.ts !lib/**/*.js !lib/**/*.js.map !lib/**/*.mjs !lib/**/*.mjs.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 ================================================ FILE: addons/addon-unicode11/LICENSE ================================================ Copyright (c) 2019, 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. ================================================ FILE: addons/addon-unicode11/README.md ================================================ ## @xterm/addon-unicode11 An addon providing Unicode version 11 rules for xterm.js. ### Install ```bash npm install --save @xterm/addon-unicode11 ``` ### Usage ```ts import { Terminal } from '@xterm/xterm'; import { Unicode11Addon } from '@xterm/addon-unicode11'; const terminal = new Terminal(); const unicode11Addon = new Unicode11Addon(); terminal.loadAddon(unicode11Addon); // activate the new version terminal.unicode.activeVersion = '11'; ``` ================================================ FILE: addons/addon-unicode11/package.json ================================================ { "name": "@xterm/addon-unicode11", "version": "0.9.0", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, "main": "lib/addon-unicode11.js", "module": "lib/addon-unicode11.mjs", "types": "typings/addon-unicode11.d.ts", "repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-unicode11", "license": "MIT", "keywords": [ "terminal", "xterm", "xterm.js" ], "scripts": { "build": "../../node_modules/.bin/tsgo -p .", "prepackage": "npm run build", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", "start": "node ../../demo/start" } } ================================================ FILE: addons/addon-unicode11/src/Unicode11Addon.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT * * UnicodeVersionProvider for V11. */ import type { Terminal, ITerminalAddon } from '@xterm/xterm'; import type { Unicode11Addon as IUnicode11Api } from '@xterm/addon-unicode11'; import { UnicodeV11 } from './UnicodeV11'; export class Unicode11Addon implements ITerminalAddon, IUnicode11Api { public activate(terminal: Terminal): void { terminal.unicode.register(new UnicodeV11()); } public dispose(): void { } } ================================================ FILE: addons/addon-unicode11/src/UnicodeV11.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import { IUnicodeVersionProvider } from '@xterm/xterm'; import { UnicodeCharProperties, UnicodeCharWidth } from 'common/services/Services'; import { UnicodeService } from 'common/services/UnicodeService'; const BMP_COMBINING = [ [0x0300, 0x036F], [0x0483, 0x0489], [0x0591, 0x05BD], [0x05BF, 0x05BF], [0x05C1, 0x05C2], [0x05C4, 0x05C5], [0x05C7, 0x05C7], [0x0600, 0x0605], [0x0610, 0x061A], [0x061C, 0x061C], [0x064B, 0x065F], [0x0670, 0x0670], [0x06D6, 0x06DD], [0x06DF, 0x06E4], [0x06E7, 0x06E8], [0x06EA, 0x06ED], [0x070F, 0x070F], [0x0711, 0x0711], [0x0730, 0x074A], [0x07A6, 0x07B0], [0x07EB, 0x07F3], [0x07FD, 0x07FD], [0x0816, 0x0819], [0x081B, 0x0823], [0x0825, 0x0827], [0x0829, 0x082D], [0x0859, 0x085B], [0x08D3, 0x0902], [0x093A, 0x093A], [0x093C, 0x093C], [0x0941, 0x0948], [0x094D, 0x094D], [0x0951, 0x0957], [0x0962, 0x0963], [0x0981, 0x0981], [0x09BC, 0x09BC], [0x09C1, 0x09C4], [0x09CD, 0x09CD], [0x09E2, 0x09E3], [0x09FE, 0x09FE], [0x0A01, 0x0A02], [0x0A3C, 0x0A3C], [0x0A41, 0x0A42], [0x0A47, 0x0A48], [0x0A4B, 0x0A4D], [0x0A51, 0x0A51], [0x0A70, 0x0A71], [0x0A75, 0x0A75], [0x0A81, 0x0A82], [0x0ABC, 0x0ABC], [0x0AC1, 0x0AC5], [0x0AC7, 0x0AC8], [0x0ACD, 0x0ACD], [0x0AE2, 0x0AE3], [0x0AFA, 0x0AFF], [0x0B01, 0x0B01], [0x0B3C, 0x0B3C], [0x0B3F, 0x0B3F], [0x0B41, 0x0B44], [0x0B4D, 0x0B4D], [0x0B56, 0x0B56], [0x0B62, 0x0B63], [0x0B82, 0x0B82], [0x0BC0, 0x0BC0], [0x0BCD, 0x0BCD], [0x0C00, 0x0C00], [0x0C04, 0x0C04], [0x0C3E, 0x0C40], [0x0C46, 0x0C48], [0x0C4A, 0x0C4D], [0x0C55, 0x0C56], [0x0C62, 0x0C63], [0x0C81, 0x0C81], [0x0CBC, 0x0CBC], [0x0CBF, 0x0CBF], [0x0CC6, 0x0CC6], [0x0CCC, 0x0CCD], [0x0CE2, 0x0CE3], [0x0D00, 0x0D01], [0x0D3B, 0x0D3C], [0x0D41, 0x0D44], [0x0D4D, 0x0D4D], [0x0D62, 0x0D63], [0x0DCA, 0x0DCA], [0x0DD2, 0x0DD4], [0x0DD6, 0x0DD6], [0x0E31, 0x0E31], [0x0E34, 0x0E3A], [0x0E47, 0x0E4E], [0x0EB1, 0x0EB1], [0x0EB4, 0x0EBC], [0x0EC8, 0x0ECD], [0x0F18, 0x0F19], [0x0F35, 0x0F35], [0x0F37, 0x0F37], [0x0F39, 0x0F39], [0x0F71, 0x0F7E], [0x0F80, 0x0F84], [0x0F86, 0x0F87], [0x0F8D, 0x0F97], [0x0F99, 0x0FBC], [0x0FC6, 0x0FC6], [0x102D, 0x1030], [0x1032, 0x1037], [0x1039, 0x103A], [0x103D, 0x103E], [0x1058, 0x1059], [0x105E, 0x1060], [0x1071, 0x1074], [0x1082, 0x1082], [0x1085, 0x1086], [0x108D, 0x108D], [0x109D, 0x109D], [0x1160, 0x11FF], [0x135D, 0x135F], [0x1712, 0x1714], [0x1732, 0x1734], [0x1752, 0x1753], [0x1772, 0x1773], [0x17B4, 0x17B5], [0x17B7, 0x17BD], [0x17C6, 0x17C6], [0x17C9, 0x17D3], [0x17DD, 0x17DD], [0x180B, 0x180E], [0x1885, 0x1886], [0x18A9, 0x18A9], [0x1920, 0x1922], [0x1927, 0x1928], [0x1932, 0x1932], [0x1939, 0x193B], [0x1A17, 0x1A18], [0x1A1B, 0x1A1B], [0x1A56, 0x1A56], [0x1A58, 0x1A5E], [0x1A60, 0x1A60], [0x1A62, 0x1A62], [0x1A65, 0x1A6C], [0x1A73, 0x1A7C], [0x1A7F, 0x1A7F], [0x1AB0, 0x1ABE], [0x1B00, 0x1B03], [0x1B34, 0x1B34], [0x1B36, 0x1B3A], [0x1B3C, 0x1B3C], [0x1B42, 0x1B42], [0x1B6B, 0x1B73], [0x1B80, 0x1B81], [0x1BA2, 0x1BA5], [0x1BA8, 0x1BA9], [0x1BAB, 0x1BAD], [0x1BE6, 0x1BE6], [0x1BE8, 0x1BE9], [0x1BED, 0x1BED], [0x1BEF, 0x1BF1], [0x1C2C, 0x1C33], [0x1C36, 0x1C37], [0x1CD0, 0x1CD2], [0x1CD4, 0x1CE0], [0x1CE2, 0x1CE8], [0x1CED, 0x1CED], [0x1CF4, 0x1CF4], [0x1CF8, 0x1CF9], [0x1DC0, 0x1DF9], [0x1DFB, 0x1DFF], [0x200B, 0x200F], [0x202A, 0x202E], [0x2060, 0x2064], [0x2066, 0x206F], [0x20D0, 0x20F0], [0x2CEF, 0x2CF1], [0x2D7F, 0x2D7F], [0x2DE0, 0x2DFF], [0x302A, 0x302D], [0x3099, 0x309A], [0xA66F, 0xA672], [0xA674, 0xA67D], [0xA69E, 0xA69F], [0xA6F0, 0xA6F1], [0xA802, 0xA802], [0xA806, 0xA806], [0xA80B, 0xA80B], [0xA825, 0xA826], [0xA8C4, 0xA8C5], [0xA8E0, 0xA8F1], [0xA8FF, 0xA8FF], [0xA926, 0xA92D], [0xA947, 0xA951], [0xA980, 0xA982], [0xA9B3, 0xA9B3], [0xA9B6, 0xA9B9], [0xA9BC, 0xA9BD], [0xA9E5, 0xA9E5], [0xAA29, 0xAA2E], [0xAA31, 0xAA32], [0xAA35, 0xAA36], [0xAA43, 0xAA43], [0xAA4C, 0xAA4C], [0xAA7C, 0xAA7C], [0xAAB0, 0xAAB0], [0xAAB2, 0xAAB4], [0xAAB7, 0xAAB8], [0xAABE, 0xAABF], [0xAAC1, 0xAAC1], [0xAAEC, 0xAAED], [0xAAF6, 0xAAF6], [0xABE5, 0xABE5], [0xABE8, 0xABE8], [0xABED, 0xABED], [0xFB1E, 0xFB1E], [0xFE00, 0xFE0F], [0xFE20, 0xFE2F], [0xFEFF, 0xFEFF], [0xFFF9, 0xFFFB] ]; const HIGH_COMBINING = [ [0x101FD, 0x101FD], [0x102E0, 0x102E0], [0x10376, 0x1037A], [0x10A01, 0x10A03], [0x10A05, 0x10A06], [0x10A0C, 0x10A0F], [0x10A38, 0x10A3A], [0x10A3F, 0x10A3F], [0x10AE5, 0x10AE6], [0x10D24, 0x10D27], [0x10F46, 0x10F50], [0x11001, 0x11001], [0x11038, 0x11046], [0x1107F, 0x11081], [0x110B3, 0x110B6], [0x110B9, 0x110BA], [0x110BD, 0x110BD], [0x110CD, 0x110CD], [0x11100, 0x11102], [0x11127, 0x1112B], [0x1112D, 0x11134], [0x11173, 0x11173], [0x11180, 0x11181], [0x111B6, 0x111BE], [0x111C9, 0x111CC], [0x1122F, 0x11231], [0x11234, 0x11234], [0x11236, 0x11237], [0x1123E, 0x1123E], [0x112DF, 0x112DF], [0x112E3, 0x112EA], [0x11300, 0x11301], [0x1133B, 0x1133C], [0x11340, 0x11340], [0x11366, 0x1136C], [0x11370, 0x11374], [0x11438, 0x1143F], [0x11442, 0x11444], [0x11446, 0x11446], [0x1145E, 0x1145E], [0x114B3, 0x114B8], [0x114BA, 0x114BA], [0x114BF, 0x114C0], [0x114C2, 0x114C3], [0x115B2, 0x115B5], [0x115BC, 0x115BD], [0x115BF, 0x115C0], [0x115DC, 0x115DD], [0x11633, 0x1163A], [0x1163D, 0x1163D], [0x1163F, 0x11640], [0x116AB, 0x116AB], [0x116AD, 0x116AD], [0x116B0, 0x116B5], [0x116B7, 0x116B7], [0x1171D, 0x1171F], [0x11722, 0x11725], [0x11727, 0x1172B], [0x1182F, 0x11837], [0x11839, 0x1183A], [0x119D4, 0x119D7], [0x119DA, 0x119DB], [0x119E0, 0x119E0], [0x11A01, 0x11A0A], [0x11A33, 0x11A38], [0x11A3B, 0x11A3E], [0x11A47, 0x11A47], [0x11A51, 0x11A56], [0x11A59, 0x11A5B], [0x11A8A, 0x11A96], [0x11A98, 0x11A99], [0x11C30, 0x11C36], [0x11C38, 0x11C3D], [0x11C3F, 0x11C3F], [0x11C92, 0x11CA7], [0x11CAA, 0x11CB0], [0x11CB2, 0x11CB3], [0x11CB5, 0x11CB6], [0x11D31, 0x11D36], [0x11D3A, 0x11D3A], [0x11D3C, 0x11D3D], [0x11D3F, 0x11D45], [0x11D47, 0x11D47], [0x11D90, 0x11D91], [0x11D95, 0x11D95], [0x11D97, 0x11D97], [0x11EF3, 0x11EF4], [0x13430, 0x13438], [0x16AF0, 0x16AF4], [0x16B30, 0x16B36], [0x16F4F, 0x16F4F], [0x16F8F, 0x16F92], [0x1BC9D, 0x1BC9E], [0x1BCA0, 0x1BCA3], [0x1D167, 0x1D169], [0x1D173, 0x1D182], [0x1D185, 0x1D18B], [0x1D1AA, 0x1D1AD], [0x1D242, 0x1D244], [0x1DA00, 0x1DA36], [0x1DA3B, 0x1DA6C], [0x1DA75, 0x1DA75], [0x1DA84, 0x1DA84], [0x1DA9B, 0x1DA9F], [0x1DAA1, 0x1DAAF], [0x1E000, 0x1E006], [0x1E008, 0x1E018], [0x1E01B, 0x1E021], [0x1E023, 0x1E024], [0x1E026, 0x1E02A], [0x1E130, 0x1E136], [0x1E2EC, 0x1E2EF], [0x1E8D0, 0x1E8D6], [0x1E944, 0x1E94A], [0xE0001, 0xE0001], [0xE0020, 0xE007F], [0xE0100, 0xE01EF] ]; const BMP_WIDE = [ [0x1100, 0x115F], [0x231A, 0x231B], [0x2329, 0x232A], [0x23E9, 0x23EC], [0x23F0, 0x23F0], [0x23F3, 0x23F3], [0x25FD, 0x25FE], [0x2614, 0x2615], [0x2648, 0x2653], [0x267F, 0x267F], [0x2693, 0x2693], [0x26A1, 0x26A1], [0x26AA, 0x26AB], [0x26BD, 0x26BE], [0x26C4, 0x26C5], [0x26CE, 0x26CE], [0x26D4, 0x26D4], [0x26EA, 0x26EA], [0x26F2, 0x26F3], [0x26F5, 0x26F5], [0x26FA, 0x26FA], [0x26FD, 0x26FD], [0x2705, 0x2705], [0x270A, 0x270B], [0x2728, 0x2728], [0x274C, 0x274C], [0x274E, 0x274E], [0x2753, 0x2755], [0x2757, 0x2757], [0x2795, 0x2797], [0x27B0, 0x27B0], [0x27BF, 0x27BF], [0x2B1B, 0x2B1C], [0x2B50, 0x2B50], [0x2B55, 0x2B55], [0x2E80, 0x2E99], [0x2E9B, 0x2EF3], [0x2F00, 0x2FD5], [0x2FF0, 0x2FFB], [0x3000, 0x3029], [0x302E, 0x303E], [0x3041, 0x3096], [0x309B, 0x30FF], [0x3105, 0x312F], [0x3131, 0x318E], [0x3190, 0x31BA], [0x31C0, 0x31E3], [0x31F0, 0x321E], [0x3220, 0x3247], [0x3250, 0x4DBF], [0x4E00, 0xA48C], [0xA490, 0xA4C6], [0xA960, 0xA97C], [0xAC00, 0xD7A3], [0xF900, 0xFAFF], [0xFE10, 0xFE19], [0xFE30, 0xFE52], [0xFE54, 0xFE66], [0xFE68, 0xFE6B], [0xFF01, 0xFF60], [0xFFE0, 0xFFE6] ]; const HIGH_WIDE = [ [0x16FE0, 0x16FE3], [0x17000, 0x187F7], [0x18800, 0x18AF2], [0x1B000, 0x1B11E], [0x1B150, 0x1B152], [0x1B164, 0x1B167], [0x1B170, 0x1B2FB], [0x1F004, 0x1F004], [0x1F0CF, 0x1F0CF], [0x1F18E, 0x1F18E], [0x1F191, 0x1F19A], [0x1F200, 0x1F202], [0x1F210, 0x1F23B], [0x1F240, 0x1F248], [0x1F250, 0x1F251], [0x1F260, 0x1F265], [0x1F300, 0x1F320], [0x1F32D, 0x1F335], [0x1F337, 0x1F37C], [0x1F37E, 0x1F393], [0x1F3A0, 0x1F3CA], [0x1F3CF, 0x1F3D3], [0x1F3E0, 0x1F3F0], [0x1F3F4, 0x1F3F4], [0x1F3F8, 0x1F43E], [0x1F440, 0x1F440], [0x1F442, 0x1F4FC], [0x1F4FF, 0x1F53D], [0x1F54B, 0x1F54E], [0x1F550, 0x1F567], [0x1F57A, 0x1F57A], [0x1F595, 0x1F596], [0x1F5A4, 0x1F5A4], [0x1F5FB, 0x1F64F], [0x1F680, 0x1F6C5], [0x1F6CC, 0x1F6CC], [0x1F6D0, 0x1F6D2], [0x1F6D5, 0x1F6D5], [0x1F6EB, 0x1F6EC], [0x1F6F4, 0x1F6FA], [0x1F7E0, 0x1F7EB], [0x1F90D, 0x1F971], [0x1F973, 0x1F976], [0x1F97A, 0x1F9A2], [0x1F9A5, 0x1F9AA], [0x1F9AE, 0x1F9CA], [0x1F9CD, 0x1F9FF], [0x1FA70, 0x1FA73], [0x1FA78, 0x1FA7A], [0x1FA80, 0x1FA82], [0x1FA90, 0x1FA95], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD] ]; // BMP lookup table, lazy initialized during first addon loading let table: Uint8Array; function bisearch(ucs: number, data: number[][]): boolean { let min = 0; let max = data.length - 1; let mid; if (ucs < data[0][0] || ucs > data[max][1]) { return false; } while (max >= min) { mid = (min + max) >> 1; if (ucs > data[mid][1]) { min = mid + 1; } else if (ucs < data[mid][0]) { max = mid - 1; } else { return true; } } return false; } export class UnicodeV11 implements IUnicodeVersionProvider { public readonly version = '11'; constructor() { if (!table) { table = new Uint8Array(65536); table.fill(1); table[0] = 0; table.fill(0, 1, 32); table.fill(0, 0x7f, 0xa0); for (let r = 0; r < BMP_COMBINING.length; ++r) { table.fill(0, BMP_COMBINING[r][0], BMP_COMBINING[r][1] + 1); } for (let r = 0; r < BMP_WIDE.length; ++r) { table.fill(2, BMP_WIDE[r][0], BMP_WIDE[r][1] + 1); } } } public wcwidth(num: number): UnicodeCharWidth { if (num < 32) return 0; if (num < 127) return 1; if (num < 65536) return table[num] as UnicodeCharWidth; if (bisearch(num, HIGH_COMBINING)) return 0; if (bisearch(num, HIGH_WIDE)) return 2; return 1; } public charProperties(codepoint: number, preceding: UnicodeCharProperties): UnicodeCharProperties { let width = this.wcwidth(codepoint); let shouldJoin = width === 0 && preceding !== 0; if (shouldJoin) { const oldWidth = UnicodeService.extractWidth(preceding); if (oldWidth === 0) { shouldJoin = false; } else if (oldWidth > width) { width = oldWidth; } } return UnicodeService.createPropertyValue(0, width, shouldJoin); } } ================================================ FILE: addons/addon-unicode11/src/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es2021", "lib": [ "dom", "es2015" ], "rootDir": ".", "outDir": "../out", "sourceMap": true, "removeComments": true, "strict": true, "paths": { "common/*": [ "../../../src/common/*" ], "@xterm/addon-unicode11": [ "../typings/addon-unicode11.d.ts" ], "*": [ "./*" ] }, "types": [ "../../../node_modules/@types/mocha" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" } ] } ================================================ FILE: addons/addon-unicode11/test/Unicode11Addon.test.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import test from '@playwright/test'; import { deepStrictEqual } from 'assert'; import { ITestContext, createTestContext, openTerminal } from '../../../test/playwright/TestUtils'; let ctx: ITestContext; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); await openTerminal(ctx); }); test.afterAll(async () => await ctx.page.close()); test.describe('Unicode11Addon', () => { test.beforeEach(async () => { await ctx.page.evaluate(` window.term.reset() window.unicode11?.dispose(); window.unicode11 = new Unicode11Addon(); window.term.loadAddon(window.unicode11); `); }); test('wcwidth V11 emoji test', async () => { // should have loaded '11' deepStrictEqual((await ctx.page.evaluate(`window.term.unicode.versions`) as string[]).includes('11'), true); // switch should not throw await ctx.page.evaluate(`window.term.unicode.activeVersion = '11';`); deepStrictEqual(await ctx.page.evaluate(`window.term.unicode.activeVersion`), '11'); // v6: 10, V11: 20 deepStrictEqual(await ctx.page.evaluate(`window.term._core.unicodeService.getStringCellWidth('🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣')`), 20); }); }); ================================================ FILE: addons/addon-unicode11/test/playwright.config.ts ================================================ import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { testDir: '.', timeout: 10000, projects: [ { name: 'Chromium', use: { browserName: 'chromium' } }, { name: 'FirefoxStable', use: { browserName: 'firefox' } }, { name: 'WebKit', use: { browserName: 'webkit' } } ], reporter: 'list', webServer: { command: 'npm run start', port: 3000, timeout: 120000, reuseExistingServer: !process.env.CI } }; export default config; ================================================ FILE: addons/addon-unicode11/test/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "ESNext", "lib": [ "es2021", ], "rootDir": ".", "outDir": "../out-test", "sourceMap": true, "removeComments": true, "paths": { "common/*": [ "../../../src/common/*" ], "browser/*": [ "../../../src/browser/*" ], "*": [ "./*" ] }, "strict": true, "types": [ "../../../node_modules/@types/node" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" }, { "path": "../../../test/playwright" } ] } ================================================ FILE: addons/addon-unicode11/tsconfig.json ================================================ { "files": [], "include": [], "references": [ { "path": "./src" }, { "path": "./test" } ] } ================================================ FILE: addons/addon-unicode11/typings/addon-unicode11.d.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal, ITerminalAddon } from '@xterm/xterm'; declare module '@xterm/addon-unicode11' { export class Unicode11Addon implements ITerminalAddon { constructor(); public activate(terminal: Terminal): void; public dispose(): void; } } ================================================ FILE: addons/addon-unicode11/webpack.config.js ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ const path = require('path'); const addonName = 'Unicode11Addon'; const mainFile = 'addon-unicode11.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'), vs: path.resolve('../../out/vs') } }, output: { filename: mainFile, path: path.resolve('./lib'), library: addonName, libraryTarget: 'umd', // Force usage of globalThis instead of global / self. (This is cross-env compatible) globalObject: 'globalThis', }, mode: 'production' }; ================================================ FILE: addons/addon-web-fonts/LICENSE ================================================ Copyright (c) 2024, 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. ================================================ FILE: addons/addon-web-fonts/README.md ================================================ ## @xterm/addon-web-fonts Addon to use webfonts with [xterm.js](https://github.com/xtermjs/xterm.js). This addon requires xterm.js v5+. ### Install ```bash npm install --save @xterm/addon-web-fonts ``` ### Issue with Webfonts Webfonts are announced by CSS `font-face` rules (or its Javascript `FontFace` counterparts). Since font files tend to be quite big assets, browser engines often postpone their loading to an actual styling request of a codepoint matching a font file's `unicode-range`. In short - font files will not be loaded until really needed. xterm.js on the other hand heavily relies on exact measurement of character glyphs to layout its output. This is done by determining the glyph width (DOM renderer) or by creating a glyph texture (WebGl renderer) for every output character. For performance reasons both is done in synchronous code and cached. This logic only works properly, if a font glyph is available on its first usage, otherwise the browser will pick a glyph from a fallback font messing up the metrics. For webfonts and xterm.js this means that we cannot rely on the default loading strategy of the browser, but have to preload the font files before using that font in xterm.js. ### Static Preloading for the Rescue? If you dont mind higher initial loading times with a white page shown, you can tell the browser to preload the needed font files by placing the following link elements in the document's head above any other CSS/Javascript: ```html ... ``` Browsers also will resort to system fonts, if the preloading takes too long. So this solution has only a very limited scope. ### Loading with WebFontsAddon The webfonts addon offers several ways to deal with font assets loading without leaving the terminal in an unusable state. Recap - normally boostrapping of a new terminal involves these basic steps (Typescript): ```typescript import { Terminal } from '@xterm/xterm'; import { XYAddon } from '@xterm/addon-xy'; // create a `Terminal` instance with some options, e.g. a custom font family const terminal = new Terminal({fontFamily: 'monospace'}); // create and load all addons you want to use, e.g. fit addon const xyInstance = new XYAddon(); terminal.loadAddon(xyInstance); // finally: call `open` of the terminal instance terminal.open(your_terminal_div_element); // <-- critical path for webfonts // more boostrapping goes here ... ``` This code is guaranteed to work in all browsers synchronously, as the identifier `monospace` will always be available. It will also work synchronously with any installed system font, but breaks horribly with webfonts. The actual culprit here is the call to `terminal.open`, which attaches the terminal to the DOM and starts the renderer with all the glyph caching mentioned above, while the webfont is not yet fully available. To fix that, the webfonts addon provides a waiting condition (Typescript): ```typescript import { Terminal } from '@xterm/xterm'; import { XYAddon } from '@xterm/addon-xy'; import { WebFontsAddon } from '@xterm/addon-web-fonts'; // create a `Terminal` instance, now with webfonts const terminal = new Terminal({fontFamily: '"Web Mono 1", "Super Powerline", monospace'}); const xyInstance = new XYAddon(); terminal.loadAddon(xyInstance); const webFontsInstance = new WebFontsAddon(); terminal.loadAddon(webFontsInstance); // wait for webfonts to be fully loaded webFontsInstance.loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { terminal.open(your_terminal_div_element); // more boostrapping goes here ... }); ``` Here `loadFonts` will look up the font face objects in `document.fonts` and load them before continuing. For this to work, you have to make sure, that the CSS `font-face` rules for these webfonts are loaded beforehand, otherwise `loadFonts` will not find the font family names (promise will be rejected for missing font family names). Please note, that this cannot run synchronous anymore, so you will have to split your bootstrapping code into several stages. If that is too much of a hassle, you can also move the whole bootstrapping under the waiting condition by using the static loader instead (Typescript): ```typescript import { Terminal } from '@xterm/xterm'; import { XYAddon } from '@xterm/addon-xy'; // import static loader import { loadFonts } from '@xterm/addon-web-fonts'; loadFonts(['Web Mono 1', 'Super Powerline']).then(() => { // create a `Terminal` instance, now with webfonts const terminal = new Terminal({fontFamily: '"Web Mono 1", "Super Powerline", monospace'}); const xyInstance = new XYAddon(); terminal.loadAddon(xyInstance); // optional when using static loader const webfontsInstance = new WebFontsAddon(); terminal.loadAddon(webfontsInstance); terminal.open(your_terminal_div_element); // more boostrapping goes here ... }); ``` With the static loader creating and loading of the actual addon can be omitted, as fonts are already loaded before any terminal setup happens. ### Webfont Loading at Runtime Given you have a terminal already running and want to change the font family to a different not yet loaded webfont: ```typescript // either create font face objects in javascript const ff1 = new FontFace('New Web Mono', url1, ...); const ff2 = new FontFace('New Web Mono', url2, ...); // and await their loading loadFonts([ff1, ff2]).then(() => { // apply new webfont to terminal terminal.options.fontFamily = 'New Web Mono'; // since the new font might have slighly different metrics, // also run the fit addon here (or any other custom resize logic) fitAddon.fit(); }); // or alternatively use CSS to add new font-face rules, e.g. document.styleSheets[0].insertRule( "@font-face { font-family: 'New Web Mono'; src: url(newfont.woff); }", 0); // and await the new font family name loadFonts(['New Web Mono']).then(() => { // apply new webfont to terminal terminal.options.fontFamily = 'New Web Mono'; // since the new font might have slighly different metrics, // also run the fit addon here (or any other custom resize logic) fitAddon.fit(); }); ``` ### Forced Layout Update If you have the addon loaded into your terminal, you can force the terminal to update the layout with the method `WebFontsAddon.relayout`. This might come handy, if the terminal shows webfont related output issue for unknown reasons: ```typescript ... // given - terminal shows weird font issues, run: webFontsInstance.relayout().then(() => { // also run resize logic here, e.g. fit addon fitAddon.fit(); }); ``` Note that this method is only meant as a quickfix on a running terminal to keep it in a working condition. A production-ready integration should never rely on it, better fix the real root cause (most likely not properly awaiting the font loader higher up in the code). ### Webfonts from Fontsource The addon has been tested to work with webfonts from fontsource. Javascript example for `vite` with ESM import: ```javascript import { Terminal } from '@xterm/xterm'; import { FitAddon } from '@xterm/addon-fit'; import { loadFonts } from '@xterm/addon-web-fonts'; import '@xterm/xterm/css/xterm.css'; import '@fontsource/roboto-mono'; import '@fontsource/roboto-mono/400.css'; import '@fontsource/roboto-mono/400-italic.css'; import '@fontsource/roboto-mono/700.css'; import '@fontsource/roboto-mono/700-italic.css'; async function main() { let fontFamily = '"Roboto Mono", monospace'; try { await loadFonts(['Roboto Mono']); } catch (e) { fontFamily = 'monospace'; } const terminal = new Terminal({ fontFamily }); const fitAddon = new FitAddon(); terminal.loadAddon(fitAddon); terminal.open(document.getElementById('your-xterm-container-div')); fitAddon.fit(); // sync writing shows up in Roboto Mono w'o FOUT // and a fallback to monospace terminal.write('put any unicode char here'); } main(); ``` The fontsource packages download the font files to your project folder to be delivered from there later on. For security sensitive projects this should be the preferred way, as it brings the font files under your control. The example furthermore contains proper exception handling with a fallback (skipped in all other examples for better readability). --- Also see the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-web-fonts/typings/addon-web-fonts.d.ts). ================================================ FILE: addons/addon-web-fonts/package.json ================================================ { "name": "@xterm/addon-web-fonts", "version": "0.1.0", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, "main": "lib/addon-web-fonts.js", "module": "lib/addon-web-fonts.mjs", "types": "typings/addon-web-fonts.d.ts", "repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-web-fonts", "license": "MIT", "keywords": [ "terminal", "xterm", "xterm.js" ], "scripts": { "build": "../../node_modules/.bin/tsgo -p .", "prepackage": "npm run build", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", "start": "node ../../demo/start" }, "dependencies": {} } ================================================ FILE: addons/addon-web-fonts/src/WebFontsAddon.ts ================================================ /** * Copyright (c) 2024 The xterm.js authors. All rights reserved. * @license MIT */ import type { Terminal, ITerminalAddon } from '@xterm/xterm'; import type { WebFontsAddon as IWebFontsApi } from '@xterm/addon-web-fonts'; /** * Unquote a font family name. */ function unquote(s: string): string { if (s[0] === '"' && s[s.length - 1] === '"') return s.slice(1, -1); if (s[0] === '\'' && s[s.length - 1] === '\'') return s.slice(1, -1); return s; } /** * Quote a font family name conditionally. * @see https://mathiasbynens.be/notes/unquoted-font-family */ function quote(s: string): string { const pos = s.match(/([-_a-zA-Z0-9\xA0-\u{10FFFF}]+)/u); const neg = s.match(/^(-?\d|--)/m); if (!neg && pos && pos[1] === s) return s; return `"${s.replace(/"/g, '\\"')}"`; } function splitFamily(family: string | undefined): string[] { if (!family) return []; return family.split(',').map(e => unquote(e.trim())); } function createFamily(families: string[]): string { return families.map(quote).join(', '); } /** * Hash a font face from it properties. * Used in `loadFonts` to avoid bloating * `document.fonts` from multiple calls. */ function hashFontFace(ff: FontFace): string { return JSON.stringify([ unquote(ff.family), ff.stretch, ff.style, ff.unicodeRange, ff.weight ]); } /** * Wait for webfont resources to be loaded. * * Without any argument, all fonts currently listed in * `document.fonts` will be loaded. * For a more fine-grained loading strategy you can populate * the `fonts` argument with: * - font families : loads all fontfaces in `document.fonts` * matching the family names * - fontface objects : loads given fontfaces and adds them to * `document.fonts` * * The returned promise will resolve, when all loading is done. */ function _loadFonts(fonts?: (string | FontFace)[]): Promise { const ffs = Array.from(document.fonts); if (!fonts || !fonts.length) { return Promise.all(ffs.map(ff => ff.load())); } let toLoad: FontFace[] = []; const ffsHashed = ffs.map(ff => hashFontFace(ff)); for (const font of fonts) { if (font instanceof FontFace) { const fontHashed = hashFontFace(font); const idx = ffsHashed.indexOf(fontHashed); if (idx === -1) { document.fonts.add(font); ffs.push(font); ffsHashed.push(fontHashed); toLoad.push(font); } else { toLoad.push(ffs[idx]); } } else { // string as font const familyFiltered = ffs.filter(ff => font === unquote(ff.family)); toLoad = toLoad.concat(familyFiltered); if (!familyFiltered.length) { return Promise.reject(`font family "${font}" not registered in document.fonts`); } } } return Promise.all(toLoad.map(ff => ff.load())); } export function loadFonts(fonts?: (string | FontFace)[]): Promise { return document.fonts.ready.then(() => _loadFonts(fonts)); } export class WebFontsAddon implements ITerminalAddon, IWebFontsApi { private _term: Terminal | undefined; constructor(public initialRelayout: boolean = true) { } public dispose(): void { this._term = undefined; } public activate(term: Terminal): void { this._term = term; if (this.initialRelayout) { document.fonts.ready.then(() => this.relayout()); } } public loadFonts(fonts?: (string | FontFace)[]): Promise { return loadFonts(fonts); } public async relayout(): Promise { if (!this._term) { return; } await document.fonts.ready; const family = this._term.options.fontFamily; const families = splitFamily(family); const webFamilies = Array.from(new Set(Array.from(document.fonts).map(e => unquote(e.family)))); const dirty: string[] = []; const clean: string[] = []; for (const fam of families) { (webFamilies.indexOf(fam) !== -1 ? dirty : clean).push(fam); } if (!dirty.length) { return; } await _loadFonts(dirty); if (this._term) { this._term.options.fontFamily = clean.length ? createFamily(clean) : 'monospace'; this._term.options.fontFamily = family; } } } ================================================ FILE: addons/addon-web-fonts/src/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es2021", "lib": [ "dom", "es2015", "dom.iterable" ], "rootDir": ".", "outDir": "../out", "sourceMap": true, "removeComments": true, "strict": true, "types": [ "../../../node_modules/@types/mocha" ], "paths": { "@xterm/addon-web-fonts": [ "../typings/addon-web-fonts.d.ts" ] } }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ] } ================================================ FILE: addons/addon-web-fonts/test/WebFontsAddon.test.ts ================================================ /** * Copyright (c) 2024 The xterm.js authors. All rights reserved. * @license MIT */ import test from '@playwright/test'; import { deepStrictEqual, strictEqual } from 'assert'; import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from '../../../test/playwright/TestUtils'; let ctx: ITestContext; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); await openTerminal(ctx, { cols: 40 }); }); test.afterAll(async () => await ctx.page.close()); test.describe('WebFontsAddon', () => { test.beforeEach(async () => { // make sure that we start with no webfonts in the document const empty = await await getDocumentFonts(); deepStrictEqual(empty, []); }); test.afterEach(async () => { // for font loading tests to work, we have to remove added rules and fonts // to work around the quite aggressive font caching done by the browsers await ctx.page.evaluate(` document.styleSheets[0].deleteRule(1); document.styleSheets[0].deleteRule(0); document.fonts.clear(); `); }); test.describe('font loading at runtime', () => { test('loadFonts (JS)', async () => { await ctx.page.evaluate(` const ff1 = new FontFace('Kongtext', "url(/kongtext.regular.ttf) format('truetype')"); const ff2 = new FontFace('BPdots', "url(/bpdots.regular.otf) format('opentype')"); loadFonts([ff1, ff2]); `); deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }, { family: 'BPdots', status: 'loaded' }]); }); test('loadFonts (CSS, unquoted)', async () => { await ctx.page.evaluate(` document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/kongtext.regular.ttf) format('truetype')}", 0); document.styleSheets[0].insertRule("@font-face {font-family: BPdots; src: url(/bpdots.regular.otf) format('opentype')}", 1); loadFonts(['Kongtext', 'BPdots']); `); deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }, { family: 'BPdots', status: 'loaded' }]); }); test('loadFonts (CSS, quoted)', async ({ browser }) => { // NOTE: firefox preserves family quotes from CSS rules in fontface, all other browsers unquote them await ctx.page.evaluate(` document.styleSheets[0].insertRule("@font-face {font-family: 'Kongtext'; src: url(/kongtext.regular.ttf) format('truetype')}", 0); document.styleSheets[0].insertRule("@font-face {font-family: 'BPdots'; src: url(/bpdots.regular.otf) format('opentype')}", 1); loadFonts(['Kongtext', 'BPdots']); `); if (browser.browserType().name() === 'firefox') { deepStrictEqual(await getDocumentFonts(), [{ family: '"Kongtext"', status: 'loaded' }, { family: '"BPdots"', status: 'loaded' }]); } else { deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }, { family: 'BPdots', status: 'loaded' }]); } }); test('FontFace hashing', async () => { // multiple calls of `loadFonts` with the same objects shall not bloat document.fonts await ctx.page.evaluate(` const ff1 = new FontFace('Kongtext', "url(/kongtext.regular.ttf) format('truetype')"); const ff2 = new FontFace('BPdots', "url(/bpdots.regular.otf) format('opentype')"); loadFonts([ff1, ff2]); loadFonts([ff1, ff2]); loadFonts([ff1, ff2]).then(() => loadFonts([ff1, ff2])); `); deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }, { family: 'BPdots', status: 'loaded' }]); }); test('autoload & relayout from ctor', async ({ browser }) => { // to make this test work, we exclude the default measurement char W (x57) by restricting unicode-range // now the browser will postpone font loading until codepoint is hit --> wrong glyph metrics on first usage const data = await ctx.page.evaluate(` document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/kongtext.regular.ttf) format('truetype'); unicode-range: U+00A0-00FF}", 0); `); deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); // broken case: webfont in ctor without addon usage await ctx.page.evaluate(` window.helperTerm = new Terminal({fontFamily: '"Kongtext", ' + term.options.fontFamily}); window.helperTerm.open(term.element); `); // safari loads the font, firefox & chrome dont if (browser.browserType().name() === 'webkit') { deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }]); } else { deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); } // good case: addon fixes layout for webfont in ctor // the relayout happens async, so wait a bit with a promise await ctx.page.evaluate(` window.helperTerm.dispose(); window.helperTerm = new Terminal({fontFamily: '"Kongtext", ' + term.options.fontFamily}); window._webfontsAddon = new WebFontsAddon(); window.helperTerm.loadAddon(window._webfontsAddon); window.helperTerm.open(term.element); `); await timeout(100); deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }]); // cleanup this messy test case await ctx.page.evaluate(` window.helperTerm.dispose(); window._webfontsAddon.dispose(); `); }); }); }); async function getDocumentFonts(): Promise { return ctx.page.evaluate(`Array.from(document.fonts).map(ff => ({family: ff.family, status: ff.status}))`); } ================================================ FILE: addons/addon-web-fonts/test/playwright.config.ts ================================================ import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { testDir: '.', timeout: 10000, projects: [ { name: 'Chromium', use: { browserName: 'chromium' } }, { name: 'FirefoxStable', use: { browserName: 'firefox' } }, { name: 'WebKit', use: { browserName: 'webkit' } } ], reporter: 'list', webServer: { command: 'npm run start', port: 3000, timeout: 120000, reuseExistingServer: !process.env.CI } }; export default config; ================================================ FILE: addons/addon-web-fonts/test/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es2021", "lib": [ "es2021", ], // "downlevelIteration": true, "rootDir": ".", "outDir": "../out-test", "sourceMap": true, "removeComments": true, "paths": { "common/*": [ "../../../src/common/*" ], "browser/*": [ "../../../src/browser/*" ], "*": [ "./*" ] }, "strict": true, "types": [ "../../../node_modules/@types/node" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" }, { "path": "../../../test/playwright" } ] } ================================================ FILE: addons/addon-web-fonts/tsconfig.json ================================================ { "files": [], "include": [], "references": [ { "path": "./src" }, { "path": "./test" } ] } ================================================ FILE: addons/addon-web-fonts/typings/addon-web-fonts.d.ts ================================================ /** * Copyright (c) 2024 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal, ITerminalAddon } from '@xterm/xterm'; declare module '@xterm/addon-web-fonts' { /** * Addon to use webfonts in xterm.js */ export class WebFontsAddon implements ITerminalAddon { /** * @param initialRelayout Force initial relayout, if a webfont was found (default true). */ constructor(initialRelayout?: boolean); public activate(terminal: Terminal): void; public dispose(): void; /** * Wait for webfont resources to be loaded. * * Without any argument, all fonts currently listed in * `document.fonts` will be loaded. * For a more fine-grained loading strategy you can populate * the `fonts` argument with: * - font families : loads all fontfaces in `document.fonts` * matching the family names * - fontface objects : loads given fontfaces and adds them to * `document.fonts` * * The returned promise will resolve, when all loading is done. */ public loadFonts(fonts?: (string | FontFace)[]): Promise; /** * Force a terminal relayout by altering `options.FontFamily`. * * Found webfonts in `fontFamily` are temporarily removed until the webfont * resources are fully loaded. * * Call this method, if a terminal with webfonts is stuck with broken * glyph metrics. * * The returned promise will resolve, when font loading and layouting are done. */ public relayout(): Promise; } /** * Wait for webfont resources to be loaded. * * Without any argument, all fonts currently listed in * `document.fonts` will be loaded. * For a more fine-grained loading strategy you can populate * the `fonts` argument with: * - font families : loads all fontfaces in `document.fonts` * matching the family names * - fontface objects : loads given fontfaces and adds them to * `document.fonts` * * The returned promise will resolve, when all loading is done. */ function loadFonts(fonts?: (string | FontFace)[]): Promise; } ================================================ FILE: addons/addon-web-fonts/webpack.config.js ================================================ /** * Copyright (c) 2024 The xterm.js authors. All rights reserved. * @license MIT */ const path = require('path'); const addonName = 'WebFontsAddon'; const mainFile = 'addon-web-fonts.js'; module.exports = { 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', // Force usage of globalThis instead of global / self. (This is cross-env compatible) globalObject: 'globalThis', }, mode: 'production' }; ================================================ FILE: addons/addon-web-links/.gitignore ================================================ lib node_modules ================================================ FILE: addons/addon-web-links/.npmignore ================================================ # Blacklist - exclude everything except npm defaults such as LICENSE, etc * !*/ # Whitelist - lib/ !lib/**/*.d.ts !lib/**/*.js !lib/**/*.js.map !lib/**/*.mjs !lib/**/*.mjs.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 ================================================ FILE: addons/addon-web-links/LICENSE ================================================ 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. ================================================ FILE: addons/addon-web-links/README.md ================================================ ## @xterm/addon-web-links An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables web links. This addon requires xterm.js v4+. ### Install ```bash npm install --save @xterm/addon-web-links ``` ### Usage ```ts import { Terminal } from '@xterm/xterm'; import { WebLinksAddon } from '@xterm/addon-web-links'; const terminal = new Terminal(); terminal.loadAddon(new WebLinksAddon()); ``` See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-web-links/typings/addon-web-links.d.ts) for more advanced usage. ================================================ FILE: addons/addon-web-links/package.json ================================================ { "name": "@xterm/addon-web-links", "version": "0.12.0", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, "main": "lib/addon-web-links.js", "module": "lib/addon-web-links.mjs", "types": "typings/addon-web-links.d.ts", "repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-web-links", "license": "MIT", "keywords": [ "terminal", "xterm", "xterm.js" ], "scripts": { "build": "../../node_modules/.bin/tsgo -p .", "prepackage": "npm run build", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", "start": "node ../../demo/start" } } ================================================ FILE: addons/addon-web-links/src/WebLinkProvider.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import { ILinkProvider, ILink, Terminal, IViewportRange, IBufferLine } from '@xterm/xterm'; export interface ILinkProviderOptions { hover?(event: MouseEvent, text: string, location: IViewportRange): void; leave?(event: MouseEvent, text: string): void; urlRegex?: RegExp; } export class WebLinkProvider implements ILinkProvider { constructor( private readonly _terminal: Terminal, private readonly _regex: RegExp, private readonly _handler: (event: MouseEvent, uri: string) => void, private readonly _options: ILinkProviderOptions = {} ) { } public provideLinks(y: number, callback: (links: ILink[] | undefined) => void): void { const links = LinkComputer.computeLink(y, this._regex, this._terminal, this._handler); callback(this._addCallbacks(links)); } private _addCallbacks(links: ILink[]): ILink[] { return links.map(link => { link.leave = this._options.leave; link.hover = (event: MouseEvent, uri: string): void => { if (this._options.hover) { const { range } = link; this._options.hover(event, uri, range); } }; return link; }); } } function isUrl(urlString: string): boolean { try { const url = new URL(urlString); const parsedBase = url.password && url.username ? `${url.protocol}//${url.username}:${url.password}@${url.host}` : url.username ? `${url.protocol}//${url.username}@${url.host}` : `${url.protocol}//${url.host}`; return urlString.toLocaleLowerCase().startsWith(parsedBase.toLocaleLowerCase()); } catch { return false; } } export class LinkComputer { public static computeLink(y: number, regex: RegExp, terminal: Terminal, activate: (event: MouseEvent, uri: string) => void): ILink[] { const rex = new RegExp(regex.source, (regex.flags || '') + 'g'); const [lines, startLineIndex] = LinkComputer._getWindowedLineStrings(y - 1, terminal); const line = lines.join(''); let match; const result: ILink[] = []; while (match = rex.exec(line)) { const text = match[0]; // check via URL if the matched text would form a proper url if (!isUrl(text)) { continue; } // map string positions back to buffer positions // values are 0-based right side excluding const [startY, startX] = LinkComputer._mapStrIdx(terminal, startLineIndex, 0, match.index); const [endY, endX] = LinkComputer._mapStrIdx(terminal, startY, startX, text.length); if (startY === -1 || startX === -1 || endY === -1 || endX === -1) { continue; } // range expects values 1-based right side including, thus +1 except for endX const range = { start: { x: startX + 1, y: startY + 1 }, end: { x: endX, y: endY + 1 } }; result.push({ range, text, activate }); } return result; } /** * Get wrapped content lines for the current line index. * The top/bottom line expansion stops at whitespaces or length > 2048. * Returns an array with line strings and the top line index. * * NOTE: We pull line strings with trimRight=true on purpose to make sure * to correctly match urls with early wrapped wide chars. This corrupts the string index * for 1:1 backmapping to buffer positions, thus needs an additional correction in _mapStrIdx. */ private static _getWindowedLineStrings(lineIndex: number, terminal: Terminal): [string[], number] { let line: IBufferLine | undefined; let topIdx = lineIndex; let bottomIdx = lineIndex; let length = 0; let content = ''; const lines: string[] = []; if ((line = terminal.buffer.active.getLine(lineIndex))) { const currentContent = line.translateToString(true); // expand top, stop on whitespaces or length > 2048 if (line.isWrapped && currentContent[0] !== ' ') { length = 0; while ((line = terminal.buffer.active.getLine(--topIdx)) && length < 2048) { content = line.translateToString(true); length += content.length; lines.push(content); if (!line.isWrapped || content.indexOf(' ') !== -1) { break; } } lines.reverse(); } // append current line lines.push(currentContent); // expand bottom, stop on whitespaces or length > 2048 length = 0; while ((line = terminal.buffer.active.getLine(++bottomIdx)) && line.isWrapped && length < 2048) { content = line.translateToString(true); length += content.length; lines.push(content); if (content.indexOf(' ') !== -1) { break; } } } return [lines, topIdx]; } /** * Map a string index back to buffer positions. * Returns buffer position as [lineIndex, columnIndex] 0-based, * or [-1, -1] in case the lookup ran into a non-existing line. */ private static _mapStrIdx(terminal: Terminal, lineIndex: number, rowIndex: number, stringIndex: number): [number, number] { const buf = terminal.buffer.active; const cell = buf.getNullCell(); let start = rowIndex; while (stringIndex) { const line = buf.getLine(lineIndex); if (!line) { return [-1, -1]; } for (let i = start; i < line.length; ++i) { line.getCell(i, cell); const chars = cell.getChars(); const width = cell.getWidth(); if (width) { stringIndex -= chars.length || 1; // correct stringIndex for early wrapped wide chars: // - currently only happens at last cell // - cells to the right are reset with chars='' and width=1 in InputHandler.print // - follow-up line must be wrapped and contain wide char at first cell // --> if all these conditions are met, correct stringIndex by +1 if (i === line.length - 1 && chars === '') { const line = buf.getLine(lineIndex + 1); if (line && line.isWrapped) { line.getCell(0, cell); if (cell.getWidth() === 2) { stringIndex += 1; } } } } if (stringIndex < 0) { return [lineIndex, i]; } } lineIndex++; start = 0; } return [lineIndex, start]; } } ================================================ FILE: addons/addon-web-links/src/WebLinksAddon.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import type { Terminal, ITerminalAddon, IDisposable } from '@xterm/xterm'; import type { WebLinksAddon as IWebLinksApi } from '@xterm/addon-web-links'; import { ILinkProviderOptions, WebLinkProvider } from './WebLinkProvider'; // consider everthing starting with http:// or https:// // up to first whitespace, `"` or `'` as url // NOTE: The repeated end clause is needed to not match a dangling `:` // resembling the old (...)*([^:"\'\\s]) final path clause // additionally exclude early + final: // - unsafe from rfc3986: !*'() // - unsafe chars from rfc1738: {}|\^~[]` (minus [] as we need them for ipv6 adresses, also allow ~) // also exclude as finals: // - final interpunction like ,.!? // - any sort of brackets <>()[]{} (not spec conform, but often used to enclose urls) // - unsafe chars from rfc1738: {}|\^~[]` const strictUrlRegex = /(https?|HTTPS?):[/]{2}[^\s"'!*(){}|\\\^<>`]*[^\s"':,.!?{}|\\\^~\[\]`()<>]/; function handleLink(event: MouseEvent, uri: string): void { const newWindow = window.open(); if (newWindow) { try { newWindow.opener = null; } catch { // no-op, Electron can throw } newWindow.location.href = uri; } else { console.warn('Opening link blocked as opener could not be cleared'); } } export class WebLinksAddon implements ITerminalAddon, IWebLinksApi { private _terminal: Terminal | undefined; private _linkProvider: IDisposable | undefined; constructor( private _handler: (event: MouseEvent, uri: string) => void = handleLink, private _options: ILinkProviderOptions = {} ) { } public activate(terminal: Terminal): void { this._terminal = terminal; const options = this._options as ILinkProviderOptions; const regex = options.urlRegex ?? strictUrlRegex; this._linkProvider = this._terminal.registerLinkProvider(new WebLinkProvider(this._terminal, regex, this._handler, options)); } public dispose(): void { this._linkProvider?.dispose(); } } ================================================ FILE: addons/addon-web-links/src/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es2021", "lib": [ "dom", "es2015" ], "rootDir": ".", "outDir": "../out", "sourceMap": true, "removeComments": true, "strict": true, "types": [ "../../../node_modules/@types/mocha" ], "paths": { "@xterm/addon-web-links": [ "../typings/addon-web-links.d.ts" ] } }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ] } ================================================ FILE: addons/addon-web-links/test/WebLinksAddon.test.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import test from '@playwright/test'; import { deepStrictEqual, strictEqual } from 'assert'; import { readFile } from 'fs'; import { resolve } from 'path'; import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from '../../../test/playwright/TestUtils'; interface ILinkStateData { uri?: string; range?: { start: { x: number; y: number; }; end: { x: number; y: number; }; }; } let ctx: ITestContext; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); await openTerminal(ctx, { cols: 40 }); }); test.afterAll(async () => await ctx.page.close()); test.describe('WebLinksAddon', () => { test.beforeEach(async () => { await ctx.page.evaluate(` window.term.reset(); window._linkaddon?.dispose(); `); await timeout(10); await ctx.page.evaluate(` window._linkaddon = new WebLinksAddon(); window.term.loadAddon(window._linkaddon); `); }); const countryTlds = [ '.ac', '.ad', '.ae', '.af', '.ag', '.ai', '.al', '.am', '.ao', '.aq', '.ar', '.as', '.at', '.au', '.aw', '.ax', '.az', '.ba', '.bb', '.bd', '.be', '.bf', '.bg', '.bh', '.bi', '.bj', '.bm', '.bn', '.bo', '.bq', '.br', '.bs', '.bt', '.bw', '.by', '.bz', '.ca', '.cc', '.cd', '.cf', '.cg', '.ch', '.ci', '.ck', '.cl', '.cm', '.cn', '.co', '.cr', '.cu', '.cv', '.cw', '.cx', '.cy', '.cz', '.de', '.dj', '.dk', '.dm', '.do', '.dz', '.ec', '.ee', '.eg', '.eh', '.er', '.es', '.et', '.eu', '.fi', '.fj', '.fk', '.fm', '.fo', '.fr', '.ga', '.gd', '.ge', '.gf', '.gg', '.gh', '.gi', '.gl', '.gm', '.gn', '.gp', '.gq', '.gr', '.gs', '.gt', '.gu', '.gw', '.gy', '.hk', '.hm', '.hn', '.hr', '.ht', '.hu', '.id', '.ie', '.il', '.im', '.in', '.io', '.iq', '.ir', '.is', '.it', '.je', '.jm', '.jo', '.jp', '.ke', '.kg', '.kh', '.ki', '.km', '.kn', '.kp', '.kr', '.kw', '.ky', '.kz', '.la', '.lb', '.lc', '.li', '.lk', '.lr', '.ls', '.lt', '.lu', '.lv', '.ly', '.ma', '.mc', '.md', '.me', '.mg', '.mh', '.mk', '.ml', '.mm', '.mn', '.mo', '.mp', '.mq', '.mr', '.ms', '.mt', '.mu', '.mv', '.mw', '.mx', '.my', '.mz', '.na', '.nc', '.ne', '.nf', '.ng', '.ni', '.nl', '.no', '.np', '.nr', '.nu', '.nz', '.om', '.pa', '.pe', '.pf', '.pg', '.ph', '.pk', '.pl', '.pm', '.pn', '.pr', '.ps', '.pt', '.pw', '.py', '.qa', '.re', '.ro', '.rs', '.ru', '.rw', '.sa', '.sb', '.sc', '.sd', '.se', '.sg', '.sh', '.si', '.sk', '.sl', '.sm', '.sn', '.so', '.sr', '.ss', '.st', '.su', '.sv', '.sx', '.sy', '.sz', '.tc', '.td', '.tf', '.tg', '.th', '.tj', '.tk', '.tl', '.tm', '.tn', '.to', '.tr', '.tt', '.tv', '.tw', '.tz', '.ua', '.ug', '.uk', '.us', '.uy', '.uz', '.va', '.vc', '.ve', '.vg', '.vi', '.vn', '.vu', '.wf', '.ws', '.ye', '.yt', '.za', '.zm', '.zw' ]; for (const tld of countryTlds) { test(tld, async () => await testHostName(`foo${tld}`)); } test(`.com`, async () => await testHostName(`foo.com`)); for (const tld of countryTlds) { test(`.com${tld}`, async () => await testHostName(`foo.com${tld}`)); } test.describe('correct buffer offsets & uri', () => { test.beforeEach(async () => { await ctx.page.evaluate(` window._linkStateData = {uri:''}; window._linkaddon._options.hover = (event, uri, range) => { window._linkStateData = { uri, range }; }; `); }); test('all half width', async () => { await ctx.proxy.write('aaa http://example.com aaa http://example.com aaa'); await resetAndHover(5, 0); await evalLinkStateData('http://example.com', { start: { x: 5, y: 1 }, end: { x: 22, y: 1 } }); await resetAndHover(1, 1); await evalLinkStateData('http://example.com', { start: { x: 28, y: 1 }, end: { x: 5, y: 2 } }); }); test('url after full width', async () => { await ctx.proxy.write('¥¥¥ http://example.com ¥¥¥ http://example.com aaa'); await resetAndHover(8, 0); await evalLinkStateData('http://example.com', { start: { x: 8, y: 1 }, end: { x: 25, y: 1 } }); await resetAndHover(1, 1); await evalLinkStateData('http://example.com', { start: { x: 34, y: 1 }, end: { x: 11, y: 2 } }); }); test('full width within url and before', async () => { await ctx.proxy.write('¥¥¥ https://ko.wikipedia.org/wiki/위키백과:대문 aaa https://ko.wikipedia.org/wiki/위키백과:대문 ¥¥¥'); await resetAndHover(8, 0); await evalLinkStateData('https://ko.wikipedia.org/wiki/위키백과:대문', { start: { x: 8, y: 1 }, end: { x: 11, y: 2 } }); await resetAndHover(1, 1); await evalLinkStateData('https://ko.wikipedia.org/wiki/위키백과:대문', { start: { x: 8, y: 1 }, end: { x: 11, y: 2 } }); await resetAndHover(17, 1); await evalLinkStateData('https://ko.wikipedia.org/wiki/위키백과:대문', { start: { x: 17, y: 2 }, end: { x: 19, y: 3 } }); }); test('name + password url after full width and combining', async () => { await ctx.proxy.write('¥¥¥cafe\u0301 http://test:password@example.com/some_path'); await resetAndHover(12, 0); await evalLinkStateData('http://test:password@example.com/some_path', { start: { x: 12, y: 1 }, end: { x: 13, y: 2 } }); await resetAndHover(5, 1); await evalLinkStateData('http://test:password@example.com/some_path', { start: { x: 12, y: 1 }, end: { x: 13, y: 2 } }); }); test('url encoded params work properly', async () => { await ctx.proxy.write('¥¥¥cafe\u0301 http://test:password@example.com/some_path?param=1%202%3'); await resetAndHover(12, 0); await evalLinkStateData('http://test:password@example.com/some_path?param=1%202%3', { start: { x: 12, y: 1 }, end: { x: 27, y: 2 } }); await resetAndHover(5, 1); await evalLinkStateData('http://test:password@example.com/some_path?param=1%202%3', { start: { x: 12, y: 1 }, end: { x: 27, y: 2 } }); }); }); // issue #4964 test('uppercase in protocol and host, default ports', async () => { await ctx.proxy.write( ` HTTP://EXAMPLE.COM \r\n` + ` HTTPS://Example.com \r\n` + ` HTTP://Example.com:80 \r\n` + ` HTTP://Example.com:80/staysUpper \r\n` + ` HTTP://Ab:xY@abc.com:80/staysUpper \r\n` ); await pollForLinkAtCell(3, 0, `HTTP://EXAMPLE.COM`); await pollForLinkAtCell(3, 1, `HTTPS://Example.com`); await pollForLinkAtCell(3, 2, `HTTP://Example.com:80`); await pollForLinkAtCell(3, 3, `HTTP://Example.com:80/staysUpper`); await pollForLinkAtCell(3, 4, `HTTP://Ab:xY@abc.com:80/staysUpper`); }); }); async function testHostName(hostname: string): Promise { await ctx.proxy.write( ` http://${hostname} \r\n` + ` http://${hostname}/a~b#c~d?e~f \r\n` + ` http://${hostname}/colon:test \r\n` + ` http://${hostname}/colon:test: \r\n` + `"http://${hostname}/"\r\n` + `\'http://${hostname}/\'\r\n` + `http://${hostname}/subpath/+/id` ); await pollForLinkAtCell(3, 0, `http://${hostname}`); await pollForLinkAtCell(3, 1, `http://${hostname}/a~b#c~d?e~f`); await pollForLinkAtCell(3, 2, `http://${hostname}/colon:test`); await pollForLinkAtCell(3, 3, `http://${hostname}/colon:test`); await pollForLinkAtCell(2, 4, `http://${hostname}/`); await pollForLinkAtCell(2, 5, `http://${hostname}/`); await pollForLinkAtCell(1, 6, `http://${hostname}/subpath/+/id`); } async function pollForLinkAtCell(col: number, row: number, value: string): Promise { await ctx.page.mouse.move(...(await cellPos(col, row))); await pollFor(ctx.page, `!!Array.from(document.querySelectorAll('.xterm-rows > :nth-child(${row+1}) > span[style]')).filter(el => el.style.textDecoration == 'underline').length`, true); const text = await ctx.page.evaluate(`Array.from(document.querySelectorAll('.xterm-rows > :nth-child(${row+1}) > span[style]')).filter(el => el.style.textDecoration == 'underline').map(el => el.textContent).join('');`); deepStrictEqual(text, value); } async function resetAndHover(col: number, row: number): Promise { await ctx.page.mouse.move(0, 0); await ctx.page.evaluate(`window._linkStateData = {uri:''};`); await new Promise(r => setTimeout(r, 200)); await ctx.page.mouse.move(...(await cellPos(col, row))); await pollFor(ctx.page, `!!window._linkStateData.uri.length`, true); } async function evalLinkStateData(uri: string, range: any): Promise { const data: ILinkStateData = await ctx.page.evaluate(`window._linkStateData`); strictEqual(data.uri, uri); deepStrictEqual(data.range, range); } async function cellPos(col: number, row: number): Promise<[number, number]> { const coords: any = await ctx.page.evaluate(` (function() { const rect = window.term.element.getBoundingClientRect(); const dim = window.term.dimensions; return {left: rect.left, top: rect.top, bottom: rect.bottom, right: rect.right, width: dim.css.cell.width, height: dim.css.cell.height}; })(); `); return [col * coords.width + coords.left + 2, row * coords.height + coords.top + 2]; } ================================================ FILE: addons/addon-web-links/test/playwright.config.ts ================================================ import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { testDir: '.', timeout: 10000, projects: [ { name: 'Chromium', use: { browserName: 'chromium' } }, { name: 'FirefoxStable', use: { browserName: 'firefox' } }, { name: 'WebKit', use: { browserName: 'webkit' } } ], reporter: 'list', webServer: { command: 'npm run start', port: 3000, timeout: 120000, reuseExistingServer: !process.env.CI } }; export default config; ================================================ FILE: addons/addon-web-links/test/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es2021", "lib": [ "es2021", ], // "downlevelIteration": true, "rootDir": ".", "outDir": "../out-test", "sourceMap": true, "removeComments": true, "paths": { "common/*": [ "../../../src/common/*" ], "browser/*": [ "../../../src/browser/*" ], "*": [ "./*" ] }, "strict": true, "types": [ "../../../node_modules/@types/node" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" }, { "path": "../../../test/playwright" } ] } ================================================ FILE: addons/addon-web-links/tsconfig.json ================================================ { "files": [], "include": [], "references": [ { "path": "./src" }, { "path": "./test" } ] } ================================================ FILE: addons/addon-web-links/typings/addon-web-links.d.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal, ITerminalAddon, IViewportRange } from '@xterm/xterm'; declare module '@xterm/addon-web-links' { /** * An xterm.js addon that enables web links. */ export class WebLinksAddon implements ITerminalAddon { /** * Creates a new web links addon. * @param handler The callback when the link is called. * * Note that this may not work when the terminal is hosted inside an iframe, * in that case provide a custom handler in that case being mindful of * possible security issues like reverse tabnapping. * @param options Options for the link provider. */ constructor(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkProviderOptions); /** * Activates the addon * @param terminal The terminal the addon is being loaded in. */ public activate(terminal: Terminal): void; /** * Disposes the addon. */ public dispose(): void; } /** * An object containing options for a link provider. */ export interface ILinkProviderOptions { /** * A callback that fires when the mouse hovers over a link. */ hover?(event: MouseEvent, text: string, location: IViewportRange): void; /** * A callback that fires when the mouse leaves a link. Note that this can * happen even when tooltipCallback hasn't fired for the link yet. */ leave?(event: MouseEvent, text: string): void; /** * A callback to use instead of the default one. */ urlRegex?: RegExp; } } ================================================ FILE: addons/addon-web-links/webpack.config.js ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ const path = require('path'); const addonName = 'WebLinksAddon'; const mainFile = 'addon-web-links.js'; module.exports = { 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', // Force usage of globalThis instead of global / self. (This is cross-env compatible) globalObject: 'globalThis', }, mode: 'production' }; ================================================ FILE: addons/addon-webgl/.gitignore ================================================ lib node_modules ================================================ FILE: addons/addon-webgl/.npmignore ================================================ # Blacklist - exclude everything except npm defaults such as LICENSE, etc * !*/ # Whitelist - lib/ !lib/**/*.d.ts !lib/**/*.js !lib/**/*.js.map !lib/**/*.mjs !lib/**/*.mjs.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 ================================================ FILE: addons/addon-webgl/LICENSE ================================================ Copyright (c) 2018, 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. ================================================ FILE: addons/addon-webgl/README.md ================================================ ## @xterm/addon-webgl An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables a WebGL2-based renderer. This addon requires xterm.js v4+. ### Install ```bash npm install --save @xterm/addon-webgl ``` ### Usage ```ts import { Terminal } from '@xterm/xterm'; import { WebglAddon } from '@xterm/addon-webgl'; const terminal = new Terminal(); terminal.open(element); terminal.loadAddon(new WebglAddon()); ``` See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/addon-webgl/typings/addon-webgl.d.ts) for more advanced usage. ### Handling Context Loss The browser may drop WebGL contexts for various reasons like OOM or after the system has been suspended. There is an API exposed that fires the `webglcontextlost` event fired on the canvas so embedders can handle it however they wish. An easy, but suboptimal way, to handle this is by disposing of WebglAddon when the event fires: ```ts const terminal = new Terminal(); const addon = new WebglAddon(); addon.onContextLoss(e => { addon.dispose(); }); terminal.loadAddon(addon); ``` Read more about handling WebGL context losses on the [Khronos wiki](https://www.khronos.org/webgl/wiki/HandlingContextLost). ================================================ FILE: addons/addon-webgl/package.json ================================================ { "name": "@xterm/addon-webgl", "version": "0.19.0", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" }, "main": "lib/addon-webgl.js", "module": "lib/addon-webgl.mjs", "types": "typings/addon-webgl.d.ts", "repository": "https://github.com/xtermjs/xterm.js/tree/master/addons/addon-webgl", "license": "MIT", "keywords": [ "terminal", "webgl", "xterm", "xterm.js" ], "scripts": { "build": "../../node_modules/.bin/tsgo -p .", "prepackage": "npm run build", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", "start": "node ../../demo/start" } } ================================================ FILE: addons/addon-webgl/src/CellColorResolver.ts ================================================ import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { ReadonlyColorSet } from 'browser/Types'; import { Attributes, BgFlags, ExtFlags, FgFlags, NULL_CELL_CODE, UnderlineStyle } from 'common/buffer/Constants'; import { IDecorationService, IOptionsService } from 'common/services/Services'; import { ICellData } from 'common/Types'; import { Terminal } from '@xterm/xterm'; import { rgba } from 'common/Color'; import { treatGlyphAsBackgroundColor } from 'browser/renderer/shared/RendererUtils'; import { blockPatternCodepoints } from './customGlyphs/CustomGlyphDefinitions'; // Work variables to avoid garbage collection let $fg = 0; let $bg = 0; let $hasFg = false; let $hasBg = false; let $isSelected = false; let $colors: ReadonlyColorSet | undefined; let $variantOffset = 0; export class CellColorResolver { /** * The shared result of the {@link resolve} call. This is only safe to use immediately after as * any other calls will share object. */ public readonly result: { fg: number, bg: number, ext: number } = { fg: 0, bg: 0, ext: 0 }; constructor( private readonly _terminal: Terminal, private readonly _optionService: IOptionsService, private readonly _selectionRenderModel: ISelectionRenderModel, private readonly _decorationService: IDecorationService, private readonly _coreBrowserService: ICoreBrowserService, private readonly _themeService: IThemeService ) { } /** * Resolves colors for the cell, putting the result into the shared {@link result}. This resolves * overrides, inverse and selection for the cell which can then be used to feed into the renderer. */ public resolve(cell: ICellData, x: number, y: number, deviceCellWidth: number, deviceCellHeight: number): void { this.result.bg = cell.bg; this.result.fg = cell.fg; this.result.ext = cell.bg & BgFlags.HAS_EXTENDED ? cell.extended.ext : 0; // Get any foreground/background overrides, this happens on the model to avoid spreading // override logic throughout the different sub-renderers // Reset overrides work variables $bg = 0; $fg = 0; $hasBg = false; $hasFg = false; $isSelected = false; $colors = this._themeService.colors; $variantOffset = 0; const code = cell.getCode(); if (code !== NULL_CELL_CODE && cell.extended.underlineStyle === UnderlineStyle.DOTTED) { const lineWidth = Math.max(1, Math.floor(this._optionService.rawOptions.fontSize * this._coreBrowserService.dpr / 15)); $variantOffset = x * deviceCellWidth % (Math.round(lineWidth) * 2); } if ($variantOffset === 0 && blockPatternCodepoints.has(code)) { $variantOffset = ((x * deviceCellWidth) % 2) * 2 + ((y * deviceCellHeight) % 2); } // Apply decorations on the bottom layer this._decorationService.forEachDecorationAtCell(x, y, 'bottom', d => { if (d.backgroundColorRGB) { $bg = d.backgroundColorRGB.rgba >> 8 & Attributes.RGB_MASK; $hasBg = true; } if (d.foregroundColorRGB) { $fg = d.foregroundColorRGB.rgba >> 8 & Attributes.RGB_MASK; $hasFg = true; } }); // Apply the selection color if needed $isSelected = this._selectionRenderModel.isCellSelected(this._terminal, x, y); if ($isSelected) { // If the cell has a bg color, retain the color by blending it with the selection color if ( (this.result.fg & FgFlags.INVERSE) || (this.result.bg & Attributes.CM_MASK) !== Attributes.CM_DEFAULT ) { // Resolve the standard bg color if (this.result.fg & FgFlags.INVERSE) { switch (this.result.fg & Attributes.CM_MASK) { case Attributes.CM_P16: case Attributes.CM_P256: $bg = this._themeService.colors.ansi[this.result.fg & Attributes.PCOLOR_MASK].rgba; break; case Attributes.CM_RGB: $bg = ((this.result.fg & Attributes.RGB_MASK) << 8) | 0xFF; break; case Attributes.CM_DEFAULT: default: $bg = this._themeService.colors.foreground.rgba; } } else { switch (this.result.bg & Attributes.CM_MASK) { case Attributes.CM_P16: case Attributes.CM_P256: $bg = this._themeService.colors.ansi[this.result.bg & Attributes.PCOLOR_MASK].rgba; break; case Attributes.CM_RGB: $bg = ((this.result.bg & Attributes.RGB_MASK) << 8) | 0xFF; break; // No need to consider default bg color here as it's not possible } } // Blend with selection bg color $bg = rgba.blend( $bg, ((this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba & 0xFFFFFF00) | 0x80 ) >> 8 & Attributes.RGB_MASK; } else { $bg = (this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba >> 8 & Attributes.RGB_MASK; } $hasBg = true; // Apply explicit selection foreground if present if ($colors.selectionForeground) { $fg = $colors.selectionForeground.rgba >> 8 & Attributes.RGB_MASK; $hasFg = true; } // Overwrite fg as bg if it's a special decorative glyph (eg. powerline) if (treatGlyphAsBackgroundColor(cell.getCode())) { // Inverse default background should be treated as transparent if ( (this.result.fg & FgFlags.INVERSE) && (this.result.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT ) { $fg = (this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba >> 8 & Attributes.RGB_MASK; } else { if (this.result.fg & FgFlags.INVERSE) { switch (this.result.bg & Attributes.CM_MASK) { case Attributes.CM_P16: case Attributes.CM_P256: $fg = this._themeService.colors.ansi[this.result.bg & Attributes.PCOLOR_MASK].rgba; break; case Attributes.CM_RGB: $fg = ((this.result.bg & Attributes.RGB_MASK) << 8) | 0xFF; break; // No need to consider default bg color here as it's not possible } } else { switch (this.result.fg & Attributes.CM_MASK) { case Attributes.CM_P16: case Attributes.CM_P256: $fg = this._themeService.colors.ansi[this.result.fg & Attributes.PCOLOR_MASK].rgba; break; case Attributes.CM_RGB: $fg = ((this.result.fg & Attributes.RGB_MASK) << 8) | 0xFF; break; case Attributes.CM_DEFAULT: default: $fg = this._themeService.colors.foreground.rgba; } } $fg = rgba.blend( $fg, ((this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba & 0xFFFFFF00) | 0x80 ) >> 8 & Attributes.RGB_MASK; } $hasFg = true; } } // Apply decorations on the top layer this._decorationService.forEachDecorationAtCell(x, y, 'top', d => { if (d.backgroundColorRGB) { $bg = d.backgroundColorRGB.rgba >> 8 & Attributes.RGB_MASK; $hasBg = true; } if (d.foregroundColorRGB) { $fg = d.foregroundColorRGB.rgba >> 8 & Attributes.RGB_MASK; $hasFg = true; } }); // Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag // ahead of time in order to use the correct cache key if ($hasBg) { if ($isSelected) { // Non-RGB attributes from model + force non-dim + override + force RGB color mode $bg = (cell.bg & ~Attributes.RGB_MASK & ~BgFlags.DIM) | $bg | Attributes.CM_RGB; } else { // Non-RGB attributes from model + override + force RGB color mode $bg = (cell.bg & ~Attributes.RGB_MASK) | $bg | Attributes.CM_RGB; } } if ($hasFg) { // Non-RGB attributes from model + force disable inverse + override + force RGB color mode $fg = (cell.fg & ~Attributes.RGB_MASK & ~FgFlags.INVERSE) | $fg | Attributes.CM_RGB; } // Handle case where inverse was specified by only one of bg override or fg override was set, // resolving the other inverse color and setting the inverse flag if needed. if (this.result.fg & FgFlags.INVERSE) { if ($hasBg && !$hasFg) { // Resolve bg color type (default color has a different meaning in fg vs bg) if ((this.result.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) { $fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | (($colors.background.rgba >> 8 & Attributes.RGB_MASK) & Attributes.RGB_MASK) | Attributes.CM_RGB; } else { $fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | this.result.bg & (Attributes.RGB_MASK | Attributes.CM_MASK); } $hasFg = true; } if (!$hasBg && $hasFg) { // Resolve bg color type (default color has a different meaning in fg vs bg) if ((this.result.fg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) { $bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | (($colors.foreground.rgba >> 8 & Attributes.RGB_MASK) & Attributes.RGB_MASK) | Attributes.CM_RGB; } else { $bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | this.result.fg & (Attributes.RGB_MASK | Attributes.CM_MASK); } $hasBg = true; } } // Release object $colors = undefined; // Use the override if it exists this.result.bg = $hasBg ? $bg : this.result.bg; this.result.fg = $hasFg ? $fg : this.result.fg; // Reset overrides variantOffset this.result.ext &= ~ExtFlags.VARIANT_OFFSET; this.result.ext |= ($variantOffset << 29) & ExtFlags.VARIANT_OFFSET; } } ================================================ FILE: addons/addon-webgl/src/CharAtlasCache.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { TextureAtlas } from './TextureAtlas'; import { ITerminalOptions, Terminal } from '@xterm/xterm'; import { ITerminal, ReadonlyColorSet } from 'browser/Types'; import { ICharAtlasConfig, ITextureAtlas } from './Types'; import { generateConfig, configEquals } from './CharAtlasUtils'; import type { ILogService } from 'common/services/Services'; interface ITextureAtlasCacheEntry { atlas: ITextureAtlas; config: ICharAtlasConfig; // N.B. This implementation potentially holds onto copies of the terminal forever, so // this may cause memory leaks. ownedBy: Terminal[]; } const charAtlasCache: ITextureAtlasCacheEntry[] = []; /** * Acquires a char atlas, either generating a new one or returning an existing * one that is in use by another terminal. */ export function acquireTextureAtlas( terminal: Terminal, options: Required, colors: ReadonlyColorSet, deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, devicePixelRatio: number, deviceMaxTextureSize: number, customGlyphs: boolean = true ): ITextureAtlas { const newConfig = generateConfig(deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, options, colors, devicePixelRatio, deviceMaxTextureSize, customGlyphs); // Check to see if the terminal already owns this config for (let i = 0; i < charAtlasCache.length; i++) { const entry = charAtlasCache[i]; const ownedByIndex = entry.ownedBy.indexOf(terminal); if (ownedByIndex >= 0) { if (configEquals(entry.config, newConfig)) { return entry.atlas; } // The configs differ, release the terminal from the entry if (entry.ownedBy.length === 1) { entry.atlas.dispose(); charAtlasCache.splice(i, 1); } else { entry.ownedBy.splice(ownedByIndex, 1); } break; } } // Try match a char atlas from the cache for (let i = 0; i < charAtlasCache.length; i++) { const entry = charAtlasCache[i]; if (configEquals(entry.config, newConfig)) { // Add the terminal to the cache entry and return entry.ownedBy.push(terminal); return entry.atlas; } } const core: ITerminal = (terminal as any)._core; const logService = (core as any)._logService as ILogService; const newEntry: ITextureAtlasCacheEntry = { atlas: new TextureAtlas(document, newConfig, core.unicodeService, logService), config: newConfig, ownedBy: [terminal] }; charAtlasCache.push(newEntry); return newEntry.atlas; } /** * Removes a terminal reference from the cache, allowing its memory to be freed. * @param terminal The terminal to remove. */ export function removeTerminalFromCache(terminal: Terminal): void { for (let i = 0; i < charAtlasCache.length; i++) { const index = charAtlasCache[i].ownedBy.indexOf(terminal); if (index !== -1) { if (charAtlasCache[i].ownedBy.length === 1) { // Remove the cache entry if it's the only terminal charAtlasCache[i].atlas.dispose(); charAtlasCache.splice(i, 1); } else { // Remove the reference from the cache entry charAtlasCache[i].ownedBy.splice(index, 1); } break; } } } ================================================ FILE: addons/addon-webgl/src/CharAtlasUtils.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ICharAtlasConfig } from './Types'; import { Attributes } from 'common/buffer/Constants'; import { ITerminalOptions } from '@xterm/xterm'; import { IColorSet, ReadonlyColorSet } from 'browser/Types'; import { NULL_COLOR } from 'common/Color'; export function generateConfig(deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, options: Required, colors: ReadonlyColorSet, devicePixelRatio: number, deviceMaxTextureSize: number, customGlyphs: boolean = true): ICharAtlasConfig { // null out some fields that don't matter const clonedColors: IColorSet = { foreground: colors.foreground, background: colors.background, cursor: NULL_COLOR, cursorAccent: NULL_COLOR, selectionForeground: NULL_COLOR, selectionBackgroundTransparent: NULL_COLOR, selectionBackgroundOpaque: NULL_COLOR, selectionInactiveBackgroundTransparent: NULL_COLOR, selectionInactiveBackgroundOpaque: NULL_COLOR, overviewRulerBorder: NULL_COLOR, scrollbarSliderBackground: NULL_COLOR, scrollbarSliderHoverBackground: NULL_COLOR, scrollbarSliderActiveBackground: NULL_COLOR, // For the static char atlas, we only use the first 16 colors, but we need all 256 for the // dynamic character atlas. ansi: colors.ansi.slice(), contrastCache: colors.contrastCache, halfContrastCache: colors.halfContrastCache }; return { customGlyphs, devicePixelRatio, deviceMaxTextureSize, letterSpacing: options.letterSpacing, lineHeight: options.lineHeight, deviceCellWidth: deviceCellWidth, deviceCellHeight: deviceCellHeight, deviceCharWidth: deviceCharWidth, deviceCharHeight: deviceCharHeight, fontFamily: options.fontFamily, fontSize: options.fontSize, fontWeight: options.fontWeight, fontWeightBold: options.fontWeightBold, allowTransparency: options.allowTransparency, drawBoldTextInBrightColors: options.drawBoldTextInBrightColors, minimumContrastRatio: options.minimumContrastRatio, colors: clonedColors }; } export function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean { for (let i = 0; i < a.colors.ansi.length; i++) { if (a.colors.ansi[i].rgba !== b.colors.ansi[i].rgba) { return false; } } return a.devicePixelRatio === b.devicePixelRatio && a.customGlyphs === b.customGlyphs && a.lineHeight === b.lineHeight && a.letterSpacing === b.letterSpacing && a.fontFamily === b.fontFamily && a.fontSize === b.fontSize && a.fontWeight === b.fontWeight && a.fontWeightBold === b.fontWeightBold && a.allowTransparency === b.allowTransparency && a.deviceCharWidth === b.deviceCharWidth && a.deviceCharHeight === b.deviceCharHeight && a.drawBoldTextInBrightColors === b.drawBoldTextInBrightColors && a.minimumContrastRatio === b.minimumContrastRatio && a.colors.foreground.rgba === b.colors.foreground.rgba && a.colors.background.rgba === b.colors.background.rgba; } export function is256Color(colorCode: number): boolean { return (colorCode & Attributes.CM_MASK) === Attributes.CM_P16 || (colorCode & Attributes.CM_MASK) === Attributes.CM_P256; } ================================================ FILE: addons/addon-webgl/src/Constants.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { isFirefox, isLegacyEdge } from 'common/Platform'; export const DIM_OPACITY = 0.5; // The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge // would result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly // unaligned Powerline fonts (PR 3356#issuecomment-850928179). export const TEXT_BASELINE: CanvasTextBaseline = isFirefox || isLegacyEdge ? 'bottom' : 'ideographic'; ================================================ FILE: addons/addon-webgl/src/CursorBlinkStateManager.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { RendererConstants } from 'browser/renderer/shared/Constants'; import { ICoreBrowserService } from 'browser/services/Services'; const enum Constants { /** * The time between cursor blinks. */ BLINK_INTERVAL = 600, } export class CursorBlinkStateManager { public isCursorVisible: boolean; private _animationFrame: number | undefined; private _blinkStartTimeout: number | undefined; private _blinkInterval: number | undefined; private _idleTimeout: number | undefined; private _isIdlePaused: boolean = false; /** * The time at which the animation frame was restarted, this is used on the * next render to restart the timers so they don't need to restart the timers * multiple times over a short period. */ private _animationTimeRestarted: number | undefined; constructor( private _renderCallback: () => void, private _coreBrowserService: ICoreBrowserService ) { this.isCursorVisible = true; if (this._coreBrowserService.isFocused) { this._restartInterval(); this._resetIdleTimer(); } } public get isPaused(): boolean { return !(this._blinkStartTimeout || this._blinkInterval); } public dispose(): void { if (this._blinkInterval) { this._coreBrowserService.window.clearInterval(this._blinkInterval); this._blinkInterval = undefined; } if (this._blinkStartTimeout) { this._coreBrowserService.window.clearTimeout(this._blinkStartTimeout); this._blinkStartTimeout = undefined; } if (this._animationFrame) { this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame); this._animationFrame = undefined; } if (this._idleTimeout) { this._coreBrowserService.window.clearTimeout(this._idleTimeout); this._idleTimeout = undefined; } } public restartBlinkAnimation(): void { if (this._isIdlePaused) { this._resetIdleTimer(); } if (this.isPaused) { return; } // Save a timestamp so that the restart can be done on the next interval this._animationTimeRestarted = Date.now(); // Force a cursor render to ensure it's visible and in the correct position this.isCursorVisible = true; if (!this._animationFrame) { this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => { this._renderCallback(); this._animationFrame = undefined; }); } } private _restartInterval(timeToStart: number = Constants.BLINK_INTERVAL): void { // Clear any existing interval if (this._blinkInterval) { this._coreBrowserService.window.clearInterval(this._blinkInterval); this._blinkInterval = undefined; } // Setup the initial timeout which will hide the cursor, this is done before // the regular interval is setup in order to support restarting the blink // animation in a lightweight way (without thrashing clearInterval and // setInterval). this._blinkStartTimeout = this._coreBrowserService.window.setTimeout(() => { // Check if another animation restart was requested while this was being // started if (this._animationTimeRestarted) { const time = Constants.BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); this._animationTimeRestarted = undefined; if (time > 0) { this._restartInterval(time); return; } } // Hide the cursor this.isCursorVisible = false; this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => { this._renderCallback(); this._animationFrame = undefined; }); // Setup the blink interval this._blinkInterval = this._coreBrowserService.window.setInterval(() => { // Adjust the animation time if it was restarted if (this._animationTimeRestarted) { // calc time diff // Make restart interval do a setTimeout initially? const time = Constants.BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted); this._animationTimeRestarted = undefined; this._restartInterval(time); return; } // Invert visibility and render this.isCursorVisible = !this.isCursorVisible; this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => { this._renderCallback(); this._animationFrame = undefined; }); }, Constants.BLINK_INTERVAL); }, timeToStart); } public pause(): void { this.isCursorVisible = true; this._isIdlePaused = false; if (this._blinkInterval) { this._coreBrowserService.window.clearInterval(this._blinkInterval); this._blinkInterval = undefined; } if (this._blinkStartTimeout) { this._coreBrowserService.window.clearTimeout(this._blinkStartTimeout); this._blinkStartTimeout = undefined; } if (this._animationFrame) { this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame); this._animationFrame = undefined; } if (this._idleTimeout) { this._coreBrowserService.window.clearTimeout(this._idleTimeout); this._idleTimeout = undefined; } } public resume(): void { // Clear out any existing timers just in case this.pause(); this._animationTimeRestarted = undefined; this._restartInterval(); this._resetIdleTimer(); this.restartBlinkAnimation(); } /** * Resets the idle timer. If the terminal is idle for the idle timeout period, * the cursor blinking will stop. */ private _resetIdleTimer(): void { this._isIdlePaused = false; if (this._idleTimeout) { this._coreBrowserService.window.clearTimeout(this._idleTimeout); } this._idleTimeout = this._coreBrowserService.window.setTimeout(() => { this._stopBlinkingDueToIdle(); }, RendererConstants.CURSOR_BLINK_IDLE_TIMEOUT); } /** * Stops cursor blinking due to idle timeout. */ private _stopBlinkingDueToIdle(): void { // Make cursor visible and stop blinking this.isCursorVisible = true; this._isIdlePaused = true; if (this._blinkInterval) { this._coreBrowserService.window.clearInterval(this._blinkInterval); this._blinkInterval = undefined; } if (this._blinkStartTimeout) { this._coreBrowserService.window.clearTimeout(this._blinkStartTimeout); this._blinkStartTimeout = undefined; } if (this._animationFrame) { this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame); this._animationFrame = undefined; } // Clear the idle timeout as we've already acted on it this._coreBrowserService.window.clearTimeout(this._idleTimeout); this._idleTimeout = undefined; // Trigger a render to show the cursor in its final visible state this._renderCallback(); } } ================================================ FILE: addons/addon-webgl/src/DevicePixelObserver.ts ================================================ /** * Copyright (c) 2022 The xterm.js authors. All rights reserved. * @license MIT */ import { toDisposable, IDisposable } from 'common/Lifecycle'; export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable { // Observe any resizes to the element and extract the actual pixel size of the element if the // devicePixelContentBoxSize API is supported. This allows correcting rounding errors when // converting between CSS pixels and device pixels which causes blurry rendering when device // pixel ratio is not a round number. let observer: ResizeObserver | undefined = new parentWindow.ResizeObserver((entries) => { const entry = entries.find((entry) => entry.target === element); if (!entry) { return; } // Disconnect if devicePixelContentBoxSize isn't supported by the browser if (!('devicePixelContentBoxSize' in entry)) { observer?.disconnect(); observer = undefined; return; } // Fire the callback, ignore events where the dimensions are 0x0 as the canvas is likely hidden const width = entry.devicePixelContentBoxSize[0].inlineSize; const height = entry.devicePixelContentBoxSize[0].blockSize; if (width > 0 && height > 0) { callback(width, height); } }); try { observer.observe(element, { box: ['device-pixel-content-box'] } as any); } catch { observer.disconnect(); observer = undefined; } return toDisposable(() => observer?.disconnect()); } ================================================ FILE: addons/addon-webgl/src/GlyphRenderer.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { TextureAtlas } from './TextureAtlas'; import { IRenderDimensions } from 'browser/renderer/shared/Types'; import { NULL_CELL_CODE } from 'common/buffer/Constants'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { Terminal } from '@xterm/xterm'; import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject, type IRasterizedGlyph, type ITextureAtlas } from './Types'; import { createProgram, GLTexture, PROJECTION_MATRIX } from './WebglUtils'; import type { IOptionsService } from 'common/services/Services'; import { allowRescaling, throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; interface IVertices { attributes: Float32Array; /** * These buffers are the ones used to bind to WebGL, the reason there are * multiple is to allow double buffering to work as you cannot modify the * buffer while it's being used by the GPU. Having multiple lets us start * working on the next frame. */ attributesBuffers: Float32Array[]; count: number; } const enum VertexAttribLocations { UNIT_QUAD = 0, CELL_POSITION = 1, OFFSET = 2, SIZE = 3, TEXPAGE = 4, TEXCOORD = 5, TEXSIZE = 6 } const vertexShaderSource = `#version 300 es layout (location = ${VertexAttribLocations.UNIT_QUAD}) in vec2 a_unitquad; layout (location = ${VertexAttribLocations.CELL_POSITION}) in vec2 a_cellpos; layout (location = ${VertexAttribLocations.OFFSET}) in vec2 a_offset; layout (location = ${VertexAttribLocations.SIZE}) in vec2 a_size; layout (location = ${VertexAttribLocations.TEXPAGE}) in float a_texpage; layout (location = ${VertexAttribLocations.TEXCOORD}) in vec2 a_texcoord; layout (location = ${VertexAttribLocations.TEXSIZE}) in vec2 a_texsize; uniform mat4 u_projection; uniform vec2 u_resolution; out vec2 v_texcoord; flat out int v_texpage; void main() { vec2 zeroToOne = (a_offset / u_resolution) + a_cellpos + (a_unitquad * a_size); gl_Position = u_projection * vec4(zeroToOne, 0.0, 1.0); v_texpage = int(a_texpage); v_texcoord = a_texcoord + a_unitquad * a_texsize; }`; function createFragmentShaderSource(maxFragmentShaderTextureUnits: number): string { let textureConditionals = ''; for (let i = 1; i < maxFragmentShaderTextureUnits; i++) { textureConditionals += ` else if (v_texpage == ${i}) { outColor = texture(u_texture[${i}], v_texcoord); }`; } return (`#version 300 es precision lowp float; in vec2 v_texcoord; flat in int v_texpage; uniform sampler2D u_texture[${maxFragmentShaderTextureUnits}]; out vec4 outColor; void main() { if (v_texpage == 0) { outColor = texture(u_texture[0], v_texcoord); } ${textureConditionals} }`); } const INDICES_PER_CELL = 11; const BYTES_PER_CELL = INDICES_PER_CELL * Float32Array.BYTES_PER_ELEMENT; const CELL_POSITION_INDICES = 2; // Work variables to avoid garbage collection let $i = 0; let $glyph: IRasterizedGlyph | undefined = undefined; let $leftCellPadding = 0; let $clippedPixels = 0; export class GlyphRenderer extends Disposable { private readonly _program: WebGLProgram; private readonly _vertexArrayObject: IWebGLVertexArrayObject; private readonly _projectionLocation: WebGLUniformLocation; private readonly _resolutionLocation: WebGLUniformLocation; private readonly _textureLocation: WebGLUniformLocation; private readonly _atlasTextures: GLTexture[]; private readonly _attributesBuffer: WebGLBuffer; private _atlas: ITextureAtlas | undefined; private _activeBuffer: number = 0; private readonly _vertices: IVertices = { count: 0, attributes: new Float32Array(0), attributesBuffers: [ new Float32Array(0), new Float32Array(0) ] }; constructor( private readonly _terminal: Terminal, private readonly _gl: IWebGL2RenderingContext, private _dimensions: IRenderDimensions, private readonly _optionsService: IOptionsService ) { super(); const gl = this._gl; if (TextureAtlas.maxAtlasPages === undefined) { // Typically 8 or 16 TextureAtlas.maxAtlasPages = Math.min(32, throwIfFalsy(gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) as number | null)); // Almost all clients will support >= 4096 TextureAtlas.maxTextureSize = throwIfFalsy(gl.getParameter(gl.MAX_TEXTURE_SIZE) as number | null); } this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, createFragmentShaderSource(TextureAtlas.maxAtlasPages))); this._register(toDisposable(() => gl.deleteProgram(this._program))); // Uniform locations this._projectionLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_projection')); this._resolutionLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_resolution')); this._textureLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_texture')); // Create and set the vertex array object this._vertexArrayObject = gl.createVertexArray(); gl.bindVertexArray(this._vertexArrayObject); // Setup a_unitquad, this defines the 4 vertices of a rectangle const unitQuadVertices = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]); const unitQuadVerticesBuffer = gl.createBuffer(); this._register(toDisposable(() => gl.deleteBuffer(unitQuadVerticesBuffer))); gl.bindBuffer(gl.ARRAY_BUFFER, unitQuadVerticesBuffer); gl.bufferData(gl.ARRAY_BUFFER, unitQuadVertices, gl.STATIC_DRAW); gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD); gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0); // Setup the unit quad element array buffer, this points to indices in // unitQuadVertices to allow is to draw 2 triangles from the vertices via a // triangle strip const unitQuadElementIndices = new Uint8Array([0, 1, 2, 3]); const elementIndicesBuffer = gl.createBuffer(); this._register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer))); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementIndicesBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, unitQuadElementIndices, gl.STATIC_DRAW); // Setup attributes this._attributesBuffer = throwIfFalsy(gl.createBuffer()); this._register(toDisposable(() => gl.deleteBuffer(this._attributesBuffer))); gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.enableVertexAttribArray(VertexAttribLocations.OFFSET); gl.vertexAttribPointer(VertexAttribLocations.OFFSET, 2, gl.FLOAT, false, BYTES_PER_CELL, 0); gl.vertexAttribDivisor(VertexAttribLocations.OFFSET, 1); gl.enableVertexAttribArray(VertexAttribLocations.SIZE); gl.vertexAttribPointer(VertexAttribLocations.SIZE, 2, gl.FLOAT, false, BYTES_PER_CELL, 2 * Float32Array.BYTES_PER_ELEMENT); gl.vertexAttribDivisor(VertexAttribLocations.SIZE, 1); gl.enableVertexAttribArray(VertexAttribLocations.TEXPAGE); gl.vertexAttribPointer(VertexAttribLocations.TEXPAGE, 1, gl.FLOAT, false, BYTES_PER_CELL, 4 * Float32Array.BYTES_PER_ELEMENT); gl.vertexAttribDivisor(VertexAttribLocations.TEXPAGE, 1); gl.enableVertexAttribArray(VertexAttribLocations.TEXCOORD); gl.vertexAttribPointer(VertexAttribLocations.TEXCOORD, 2, gl.FLOAT, false, BYTES_PER_CELL, 5 * Float32Array.BYTES_PER_ELEMENT); gl.vertexAttribDivisor(VertexAttribLocations.TEXCOORD, 1); gl.enableVertexAttribArray(VertexAttribLocations.TEXSIZE); gl.vertexAttribPointer(VertexAttribLocations.TEXSIZE, 2, gl.FLOAT, false, BYTES_PER_CELL, 7 * Float32Array.BYTES_PER_ELEMENT); gl.vertexAttribDivisor(VertexAttribLocations.TEXSIZE, 1); gl.enableVertexAttribArray(VertexAttribLocations.CELL_POSITION); gl.vertexAttribPointer(VertexAttribLocations.CELL_POSITION, 2, gl.FLOAT, false, BYTES_PER_CELL, 9 * Float32Array.BYTES_PER_ELEMENT); gl.vertexAttribDivisor(VertexAttribLocations.CELL_POSITION, 1); // Setup static uniforms gl.useProgram(this._program); const textureUnits = new Int32Array(TextureAtlas.maxAtlasPages); for (let i = 0; i < TextureAtlas.maxAtlasPages; i++) { textureUnits[i] = i; } gl.uniform1iv(this._textureLocation, textureUnits); gl.uniformMatrix4fv(this._projectionLocation, false, PROJECTION_MATRIX); // Setup 1x1 red pixel textures for all potential atlas pages, if one of these invalid textures // is ever drawn it will show characters as red rectangles. this._atlasTextures = []; for (let i = 0; i < TextureAtlas.maxAtlasPages; i++) { const glTexture = new GLTexture(throwIfFalsy(gl.createTexture())); this._register(toDisposable(() => gl.deleteTexture(glTexture.texture))); gl.activeTexture(gl.TEXTURE0 + i); gl.bindTexture(gl.TEXTURE_2D, glTexture.texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255])); this._atlasTextures[i] = glTexture; } // Allow drawing of transparent texture gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); // Set viewport this.handleResize(); } public beginFrame(): boolean { return this._atlas ? this._atlas.beginFrame() : true; } public updateCell(x: number, y: number, code: number, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { // Since this function is called for every cell (`rows*cols`), it must be very optimized. It // should not instantiate any variables unless a new glyph is drawn to the cache where the // slight slowdown is acceptable for the developer ergonomics provided as it's a once of for // each glyph. this._updateCell(this._vertices.attributes, x, y, code, bg, fg, ext, chars, width, lastBg); } private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, ext: number, chars: string, width: number, lastBg: number): void { $i = (y * this._terminal.cols + x) * INDICES_PER_CELL; // Exit early if this is a null character, allow space character to continue as it may have // underline/strikethrough styles if (code === NULL_CELL_CODE || code === undefined/* This is used for the right side of wide chars */) { array.fill(0, $i, $i + INDICES_PER_CELL - 1 - CELL_POSITION_INDICES); return; } if (!this._atlas) { return; } // Get the glyph if (chars && chars.length > 1) { $glyph = this._atlas.getRasterizedGlyphCombinedChar(chars, bg, fg, ext, false, this._terminal.element); } else { $glyph = this._atlas.getRasterizedGlyph(code, bg, fg, ext, false, this._terminal.element); } $leftCellPadding = Math.floor((this._dimensions.device.cell.width - this._dimensions.device.char.width) / 2); if (bg !== lastBg && $glyph.offset.x > $leftCellPadding) { $clippedPixels = $glyph.offset.x - $leftCellPadding; // a_origin array[$i ] = -($glyph.offset.x - $clippedPixels) + this._dimensions.device.char.left; array[$i + 1] = -$glyph.offset.y + this._dimensions.device.char.top; // a_size array[$i + 2] = ($glyph.size.x - $clippedPixels) / this._dimensions.device.canvas.width; array[$i + 3] = $glyph.size.y / this._dimensions.device.canvas.height; // a_texpage array[$i + 4] = $glyph.texturePage; // a_texcoord array[$i + 5] = $glyph.texturePositionClipSpace.x + $clippedPixels / this._atlas.pages[$glyph.texturePage].canvas.width; array[$i + 6] = $glyph.texturePositionClipSpace.y; // a_texsize array[$i + 7] = $glyph.sizeClipSpace.x - $clippedPixels / this._atlas.pages[$glyph.texturePage].canvas.width; array[$i + 8] = $glyph.sizeClipSpace.y; } else { // a_origin array[$i ] = -$glyph.offset.x + this._dimensions.device.char.left; array[$i + 1] = -$glyph.offset.y + this._dimensions.device.char.top; // a_size array[$i + 2] = $glyph.size.x / this._dimensions.device.canvas.width; array[$i + 3] = $glyph.size.y / this._dimensions.device.canvas.height; // a_texpage array[$i + 4] = $glyph.texturePage; // a_texcoord array[$i + 5] = $glyph.texturePositionClipSpace.x; array[$i + 6] = $glyph.texturePositionClipSpace.y; // a_texsize array[$i + 7] = $glyph.sizeClipSpace.x; array[$i + 8] = $glyph.sizeClipSpace.y; } // a_cellpos only changes on resize // Reduce scale horizontally for wide glyphs printed in cells that would overlap with the // following cell (ie. the width is not 2). if (this._optionsService.rawOptions.rescaleOverlappingGlyphs) { if (allowRescaling(code, width, $glyph.size.x, this._dimensions.device.cell.width)) { array[$i + 2] = (this._dimensions.device.cell.width - 1) / this._dimensions.device.canvas.width; // - 1 to improve readability } } } public clear(): void { const terminal = this._terminal; const newCount = terminal.cols * terminal.rows * INDICES_PER_CELL; // Clear vertices if (this._vertices.count !== newCount) { this._vertices.attributes = new Float32Array(newCount); } else { this._vertices.attributes.fill(0); } let i = 0; for (; i < this._vertices.attributesBuffers.length; i++) { if (this._vertices.count !== newCount) { this._vertices.attributesBuffers[i] = new Float32Array(newCount); } else { this._vertices.attributesBuffers[i].fill(0); } } this._vertices.count = newCount; i = 0; for (let y = 0; y < terminal.rows; y++) { for (let x = 0; x < terminal.cols; x++) { this._vertices.attributes[i + 9] = x / terminal.cols; this._vertices.attributes[i + 10] = y / terminal.rows; i += INDICES_PER_CELL; } } } public handleResize(): void { const gl = this._gl; gl.useProgram(this._program); gl.viewport(0, 0, this._dimensions.device.canvas.width, this._dimensions.device.canvas.height); gl.uniform2f(this._resolutionLocation, this._dimensions.device.canvas.width, this._dimensions.device.canvas.height); this.clear(); } public render(renderModel: IRenderModel): void { if (!this._atlas) { return; } const gl = this._gl; gl.useProgram(this._program); gl.bindVertexArray(this._vertexArrayObject); // Alternate buffers each frame as the active buffer gets locked while it's in use by the GPU this._activeBuffer = (this._activeBuffer + 1) % 2; const activeBuffer = this._vertices.attributesBuffers[this._activeBuffer]; // Copy data for each cell of each line up to its line length (the last non-whitespace cell) // from the attributes buffer into activeBuffer, which is the one that gets bound to the GPU. // The reasons for this are as follows: // - So the active buffer can be alternated so we don't get blocked on rendering finishing // - To copy either the normal attributes buffer or the selection attributes buffer when there // is a selection // - So we don't send vertices for all the line-ending whitespace to the GPU let bufferLength = 0; for (let y = 0; y < renderModel.lineLengths.length; y++) { const si = y * this._terminal.cols * INDICES_PER_CELL; const sub = this._vertices.attributes.subarray(si, si + renderModel.lineLengths[y] * INDICES_PER_CELL); activeBuffer.set(sub, bufferLength); bufferLength += sub.length; } // Bind the attributes buffer gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.bufferData(gl.ARRAY_BUFFER, activeBuffer.subarray(0, bufferLength), gl.STREAM_DRAW); // Bind the atlas page texture if they have changed for (let i = 0; i < this._atlas.pages.length; i++) { if (this._atlas.pages[i].version !== this._atlasTextures[i].version) { this._bindAtlasPageTexture(gl, this._atlas, i); } } // Draw the viewport gl.drawElementsInstanced(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_BYTE, 0, bufferLength / INDICES_PER_CELL); } public setAtlas(atlas: ITextureAtlas): void { this._atlas = atlas; for (const glTexture of this._atlasTextures) { glTexture.version = -1; } } private _bindAtlasPageTexture(gl: IWebGL2RenderingContext, atlas: ITextureAtlas, i: number): void { gl.activeTexture(gl.TEXTURE0 + i); gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[i].texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, atlas.pages[i].canvas); gl.generateMipmap(gl.TEXTURE_2D); this._atlasTextures[i].version = atlas.pages[i].version; } public setDimensions(dimensions: IRenderDimensions): void { this._dimensions = dimensions; } } ================================================ FILE: addons/addon-webgl/src/RectangleRenderer.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { IRenderDimensions } from 'browser/renderer/shared/Types'; import { IThemeService } from 'browser/services/Services'; import { ReadonlyColorSet } from 'browser/Types'; import { Attributes, FgFlags } from 'common/buffer/Constants'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { IColor } from 'common/Types'; import { Terminal } from '@xterm/xterm'; import { RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel'; import { IRenderModel, IWebGL2RenderingContext, IWebGLVertexArrayObject } from './Types'; import { createProgram, expandFloat32Array, PROJECTION_MATRIX } from './WebglUtils'; import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; const enum VertexAttribLocations { POSITION = 0, SIZE = 1, COLOR = 2, UNIT_QUAD = 3 } const vertexShaderSource = `#version 300 es layout (location = ${VertexAttribLocations.POSITION}) in vec2 a_position; layout (location = ${VertexAttribLocations.SIZE}) in vec2 a_size; layout (location = ${VertexAttribLocations.COLOR}) in vec4 a_color; layout (location = ${VertexAttribLocations.UNIT_QUAD}) in vec2 a_unitquad; uniform mat4 u_projection; out vec4 v_color; void main() { vec2 zeroToOne = a_position + (a_unitquad * a_size); gl_Position = u_projection * vec4(zeroToOne, 0.0, 1.0); v_color = a_color; }`; const fragmentShaderSource = `#version 300 es precision lowp float; in vec4 v_color; out vec4 outColor; void main() { outColor = v_color; }`; const INDICES_PER_RECTANGLE = 8; const BYTES_PER_RECTANGLE = INDICES_PER_RECTANGLE * Float32Array.BYTES_PER_ELEMENT; const INITIAL_BUFFER_RECTANGLE_CAPACITY = 20 * INDICES_PER_RECTANGLE; class Vertices { public attributes: Float32Array; public count: number; constructor() { this.attributes = new Float32Array(INITIAL_BUFFER_RECTANGLE_CAPACITY); this.count = 0; } } // Work variables to avoid garbage collection let $rgba = 0; let $x1 = 0; let $y1 = 0; let $r = 0; let $g = 0; let $b = 0; let $a = 0; export class RectangleRenderer extends Disposable { private _program: WebGLProgram; private _vertexArrayObject: IWebGLVertexArrayObject; private _attributesBuffer: WebGLBuffer; private _projectionLocation: WebGLUniformLocation; private _bgFloat!: Float32Array; private _cursorFloat!: Float32Array; private _vertices: Vertices = new Vertices(); private _verticesCursor: Vertices = new Vertices(); constructor( private _terminal: Terminal, private _gl: IWebGL2RenderingContext, private _dimensions: IRenderDimensions, private readonly _themeService: IThemeService ) { super(); const gl = this._gl; this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource)); this._register(toDisposable(() => gl.deleteProgram(this._program))); // Uniform locations this._projectionLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_projection')); // Create and set the vertex array object this._vertexArrayObject = gl.createVertexArray(); gl.bindVertexArray(this._vertexArrayObject); // Setup a_unitquad, this defines the 4 vertices of a rectangle const unitQuadVertices = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]); const unitQuadVerticesBuffer = gl.createBuffer(); this._register(toDisposable(() => gl.deleteBuffer(unitQuadVerticesBuffer))); gl.bindBuffer(gl.ARRAY_BUFFER, unitQuadVerticesBuffer); gl.bufferData(gl.ARRAY_BUFFER, unitQuadVertices, gl.STATIC_DRAW); gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD); gl.vertexAttribPointer(VertexAttribLocations.UNIT_QUAD, 2, this._gl.FLOAT, false, 0, 0); // Setup the unit quad element array buffer, this points to indices in // unitQuadVertices to allow is to draw 2 triangles from the vertices via a // triangle strip const unitQuadElementIndices = new Uint8Array([0, 1, 2, 3]); const elementIndicesBuffer = gl.createBuffer(); this._register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer))); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementIndicesBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, unitQuadElementIndices, gl.STATIC_DRAW); // Setup attributes this._attributesBuffer = throwIfFalsy(gl.createBuffer()); this._register(toDisposable(() => gl.deleteBuffer(this._attributesBuffer))); gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.enableVertexAttribArray(VertexAttribLocations.POSITION); gl.vertexAttribPointer(VertexAttribLocations.POSITION, 2, gl.FLOAT, false, BYTES_PER_RECTANGLE, 0); gl.vertexAttribDivisor(VertexAttribLocations.POSITION, 1); gl.enableVertexAttribArray(VertexAttribLocations.SIZE); gl.vertexAttribPointer(VertexAttribLocations.SIZE, 2, gl.FLOAT, false, BYTES_PER_RECTANGLE, 2 * Float32Array.BYTES_PER_ELEMENT); gl.vertexAttribDivisor(VertexAttribLocations.SIZE, 1); gl.enableVertexAttribArray(VertexAttribLocations.COLOR); gl.vertexAttribPointer(VertexAttribLocations.COLOR, 4, gl.FLOAT, false, BYTES_PER_RECTANGLE, 4 * Float32Array.BYTES_PER_ELEMENT); gl.vertexAttribDivisor(VertexAttribLocations.COLOR, 1); this._updateCachedColors(_themeService.colors); this._register(this._themeService.onChangeColors(e => { this._updateCachedColors(e); this._updateViewportRectangle(); })); } public renderBackgrounds(): void { this._renderVertices(this._vertices); } public renderCursor(): void { this._renderVertices(this._verticesCursor); } private _renderVertices(vertices: Vertices): void { const gl = this._gl; gl.useProgram(this._program); gl.bindVertexArray(this._vertexArrayObject); gl.uniformMatrix4fv(this._projectionLocation, false, PROJECTION_MATRIX); // Bind attributes buffer and draw gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.bufferData(gl.ARRAY_BUFFER, vertices.attributes, gl.DYNAMIC_DRAW); gl.drawElementsInstanced(this._gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_BYTE, 0, vertices.count); } public handleResize(): void { this._updateViewportRectangle(); } public setDimensions(dimensions: IRenderDimensions): void { this._dimensions = dimensions; } private _updateCachedColors(colors: ReadonlyColorSet): void { this._bgFloat = this._colorToFloat32Array(colors.background); this._cursorFloat = this._colorToFloat32Array(colors.cursor); } private _updateViewportRectangle(): void { // Set first rectangle that clears the screen this._addRectangleFloat( this._vertices.attributes, 0, 0, 0, this._terminal.cols * this._dimensions.device.cell.width, this._terminal.rows * this._dimensions.device.cell.height, this._bgFloat ); } public updateBackgrounds(model: IRenderModel): void { const terminal = this._terminal; const vertices = this._vertices; // Declare variable ahead of time to avoid garbage collection let rectangleCount = 1; let y: number; let x: number; let currentStartX: number; let currentBg: number; let currentFg: number; let currentInverse: boolean; let modelIndex: number; let bg: number; let fg: number; let inverse: boolean; let offset: number; for (y = 0; y < terminal.rows; y++) { currentStartX = -1; currentBg = 0; currentFg = 0; currentInverse = false; for (x = 0; x < terminal.cols; x++) { modelIndex = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; bg = model.cells[modelIndex + RENDER_MODEL_BG_OFFSET]; fg = model.cells[modelIndex + RENDER_MODEL_FG_OFFSET]; inverse = !!(fg & FgFlags.INVERSE); if (bg !== currentBg || (fg !== currentFg && (currentInverse || inverse))) { // A rectangle needs to be drawn if going from non-default to another color if (currentBg !== 0 || (currentInverse && currentFg !== 0)) { offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, x, y); } currentStartX = x; currentBg = bg; currentFg = fg; currentInverse = inverse; } } // Finish rectangle if it's still going if (currentBg !== 0 || (currentInverse && currentFg !== 0)) { offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, terminal.cols, y); } } vertices.count = rectangleCount; } public updateCursor(model: IRenderModel): void { const vertices = this._verticesCursor; const cursor = model.cursor; if (!cursor || cursor.style === 'block') { vertices.count = 0; return; } let offset: number; let rectangleCount = 0; if (cursor.style === 'bar' || cursor.style === 'outline') { // Left edge offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._addRectangleFloat( vertices.attributes, offset, cursor.x * this._dimensions.device.cell.width, cursor.y * this._dimensions.device.cell.height, cursor.style === 'bar' ? cursor.dpr * cursor.cursorWidth : cursor.dpr, this._dimensions.device.cell.height, this._cursorFloat ); } if (cursor.style === 'underline' || cursor.style === 'outline') { // Bottom edge offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._addRectangleFloat( vertices.attributes, offset, cursor.x * this._dimensions.device.cell.width, (cursor.y + 1) * this._dimensions.device.cell.height - cursor.dpr, cursor.width * this._dimensions.device.cell.width, cursor.dpr, this._cursorFloat ); } if (cursor.style === 'outline') { // Top edge offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._addRectangleFloat( vertices.attributes, offset, cursor.x * this._dimensions.device.cell.width, cursor.y * this._dimensions.device.cell.height, cursor.width * this._dimensions.device.cell.width, cursor.dpr, this._cursorFloat ); // Right edge offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._addRectangleFloat( vertices.attributes, offset, (cursor.x + cursor.width) * this._dimensions.device.cell.width - cursor.dpr, cursor.y * this._dimensions.device.cell.height, cursor.dpr, this._dimensions.device.cell.height, this._cursorFloat ); } vertices.count = rectangleCount; } private _updateRectangle(vertices: Vertices, offset: number, fg: number, bg: number, startX: number, endX: number, y: number): void { if (fg & FgFlags.INVERSE) { switch (fg & Attributes.CM_MASK) { case Attributes.CM_P16: case Attributes.CM_P256: $rgba = this._themeService.colors.ansi[fg & Attributes.PCOLOR_MASK].rgba; break; case Attributes.CM_RGB: $rgba = (fg & Attributes.RGB_MASK) << 8; break; case Attributes.CM_DEFAULT: default: $rgba = this._themeService.colors.foreground.rgba; } } else { switch (bg & Attributes.CM_MASK) { case Attributes.CM_P16: case Attributes.CM_P256: $rgba = this._themeService.colors.ansi[bg & Attributes.PCOLOR_MASK].rgba; break; case Attributes.CM_RGB: $rgba = (bg & Attributes.RGB_MASK) << 8; break; case Attributes.CM_DEFAULT: default: $rgba = this._themeService.colors.background.rgba; } } if (vertices.attributes.length < offset + 4) { vertices.attributes = expandFloat32Array(vertices.attributes, this._terminal.rows * this._terminal.cols * INDICES_PER_RECTANGLE); } $x1 = startX * this._dimensions.device.cell.width; $y1 = y * this._dimensions.device.cell.height; $r = (($rgba >> 24) & 0xFF) / 255; $g = (($rgba >> 16) & 0xFF) / 255; $b = (($rgba >> 8 ) & 0xFF) / 255; $a = 1; this._addRectangle(vertices.attributes, offset, $x1, $y1, (endX - startX) * this._dimensions.device.cell.width, this._dimensions.device.cell.height, $r, $g, $b, $a); } private _addRectangle(array: Float32Array, offset: number, x1: number, y1: number, width: number, height: number, r: number, g: number, b: number, a: number): void { array[offset ] = x1 / this._dimensions.device.canvas.width; array[offset + 1] = y1 / this._dimensions.device.canvas.height; array[offset + 2] = width / this._dimensions.device.canvas.width; array[offset + 3] = height / this._dimensions.device.canvas.height; array[offset + 4] = r; array[offset + 5] = g; array[offset + 6] = b; array[offset + 7] = a; } private _addRectangleFloat(array: Float32Array, offset: number, x1: number, y1: number, width: number, height: number, color: Float32Array): void { array[offset ] = x1 / this._dimensions.device.canvas.width; array[offset + 1] = y1 / this._dimensions.device.canvas.height; array[offset + 2] = width / this._dimensions.device.canvas.width; array[offset + 3] = height / this._dimensions.device.canvas.height; array[offset + 4] = color[0]; array[offset + 5] = color[1]; array[offset + 6] = color[2]; array[offset + 7] = color[3]; } private _colorToFloat32Array(color: IColor): Float32Array { return new Float32Array([ ((color.rgba >> 24) & 0xFF) / 255, ((color.rgba >> 16) & 0xFF) / 255, ((color.rgba >> 8 ) & 0xFF) / 255, ((color.rgba ) & 0xFF) / 255 ]); } } ================================================ FILE: addons/addon-webgl/src/RenderModel.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { ICursorRenderModel, IRenderModel } from './Types'; import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; import { createSelectionRenderModel } from 'browser/renderer/shared/SelectionRenderModel'; export const RENDER_MODEL_INDICIES_PER_CELL = 4; export const RENDER_MODEL_BG_OFFSET = 1; export const RENDER_MODEL_FG_OFFSET = 2; export const RENDER_MODEL_EXT_OFFSET = 3; export const COMBINED_CHAR_BIT_MASK = 0x80000000; export class RenderModel implements IRenderModel { public cells: Uint32Array; public lineLengths: Uint32Array; public selection: ISelectionRenderModel; public cursor?: ICursorRenderModel; constructor() { this.cells = new Uint32Array(0); this.lineLengths = new Uint32Array(0); this.selection = createSelectionRenderModel(); } public resize(cols: number, rows: number): void { const indexCount = cols * rows * RENDER_MODEL_INDICIES_PER_CELL; if (indexCount !== this.cells.length) { this.cells = new Uint32Array(indexCount); this.lineLengths = new Uint32Array(rows); } } public clear(): void { this.cells.fill(0, 0); this.lineLengths.fill(0, 0); } } ================================================ FILE: addons/addon-webgl/src/TextureAtlas.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { IColorContrastCache } from 'browser/Types'; import { DIM_OPACITY, TEXT_BASELINE } from './Constants'; import { tryDrawCustomGlyph } from './customGlyphs/CustomGlyphRasterizer'; import { computeNextVariantOffset, treatGlyphAsBackgroundColor, isPowerlineGlyph, isRestrictedPowerlineGlyph, throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; import { IBoundingBox, ICharAtlasConfig, IRasterizedGlyph, ITextureAtlas } from './Types'; import { NULL_COLOR, channels, color, rgba } from 'common/Color'; import { FourKeyMap } from 'common/MultiKeyMap'; import { IdleTaskQueue } from 'common/TaskQueue'; import { IColor } from 'common/Types'; import { AttributeData } from 'common/buffer/AttributeData'; import { Attributes, DEFAULT_COLOR, DEFAULT_EXT, UnderlineStyle } from 'common/buffer/Constants'; import { ILogService, IUnicodeService } from 'common/services/Services'; import { Emitter } from 'common/Event'; /** * A shared object which is used to draw nothing for a particular cell. */ const NULL_RASTERIZED_GLYPH: IRasterizedGlyph = { texturePage: 0, texturePosition: { x: 0, y: 0 }, texturePositionClipSpace: { x: 0, y: 0 }, offset: { x: 0, y: 0 }, size: { x: 0, y: 0 }, sizeClipSpace: { x: 0, y: 0 } }; const TMP_CANVAS_GLYPH_PADDING = 2; const enum Constants { /** * The amount of pixel padding to allow in each row. Setting this to zero would make the atlas * page pack as tightly as possible, but more pages would end up being created as a result. */ ROW_PIXEL_THRESHOLD = 2, /** * The maximum texture size regardless of what the actual hardware maximum turns out to be. This * is enforced to ensure uploading the texture still finishes in a reasonable amount of time. A * 4096 squared image takes up 16MB of GPU memory. */ FORCED_MAX_TEXTURE_SIZE = 4096 } interface ICharAtlasActiveRow { x: number; y: number; height: number; } // Work variables to avoid garbage collection let $glyph = undefined; export class TextureAtlas implements ITextureAtlas { private _didWarmUp: boolean = false; private _cacheMap: FourKeyMap = new FourKeyMap(); private _cacheMapCombined: FourKeyMap = new FourKeyMap(); // The texture that the atlas is drawn to private _pages: AtlasPage[] = []; public get pages(): { canvas: HTMLCanvasElement, version: number }[] { return this._pages; } // The set of atlas pages that can be written to private _activePages: AtlasPage[] = []; private _overflowSizePage: AtlasPage | undefined; private _tmpCanvas: HTMLCanvasElement; // A temporary context that glyphs are drawn to before being transfered to the atlas. private _tmpCtx: CanvasRenderingContext2D; private _workBoundingBox: IBoundingBox = { top: 0, left: 0, bottom: 0, right: 0 }; private _workAttributeData: AttributeData = new AttributeData(); private _textureSize: number = 512; public static maxAtlasPages: number | undefined; public static maxTextureSize: number | undefined; private readonly _onAddTextureAtlasCanvas = new Emitter(); public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event; private readonly _onRemoveTextureAtlasCanvas = new Emitter(); public readonly onRemoveTextureAtlasCanvas = this._onRemoveTextureAtlasCanvas.event; constructor( private readonly _document: Document, private readonly _config: ICharAtlasConfig, private readonly _unicodeService: IUnicodeService, private readonly _logService: ILogService ) { this._createNewPage(); this._tmpCanvas = createCanvas( _document, this._config.deviceCellWidth * 4 + TMP_CANVAS_GLYPH_PADDING * 2, this._config.deviceCellHeight + TMP_CANVAS_GLYPH_PADDING * 2 ); this._tmpCtx = throwIfFalsy(this._tmpCanvas.getContext('2d', { alpha: this._config.allowTransparency, willReadFrequently: true })); } public dispose(): void { this._tmpCanvas.remove(); for (const page of this.pages) { page.canvas.remove(); } this._onAddTextureAtlasCanvas.dispose(); } public warmUp(): void { if (!this._didWarmUp) { this._doWarmUp(); this._didWarmUp = true; } } private _doWarmUp(): void { // Pre-fill with ASCII 33-126, this is not urgent and done in idle callbacks const queue = new IdleTaskQueue(this._logService); for (let i = 33; i < 126; i++) { queue.enqueue(() => { if (!this._cacheMap.get(i, DEFAULT_COLOR, DEFAULT_COLOR, DEFAULT_EXT)) { const rasterizedGlyph = this._drawToCache(i, DEFAULT_COLOR, DEFAULT_COLOR, DEFAULT_EXT, false, undefined); this._cacheMap.set(i, DEFAULT_COLOR, DEFAULT_COLOR, DEFAULT_EXT, rasterizedGlyph); } }); } } private _requestClearModel = false; public beginFrame(): boolean { return this._requestClearModel; } public clearTexture(): void { if (this._pages[0].currentRow.x === 0 && this._pages[0].currentRow.y === 0) { return; } for (const page of this._pages) { page.clear(); } this._cacheMap.clear(); this._cacheMapCombined.clear(); this._didWarmUp = false; } private _createNewPage(): AtlasPage { // Try merge the set of the 4 most used pages of the largest size. This is is deferred to a // microtask to ensure it does not interrupt textures that will be rendered in the current // animation frame which would result in blank rendered areas. This is actually not that // expensive relative to drawing the glyphs, so there is no need to wait for an idle callback. if (TextureAtlas.maxAtlasPages && this._pages.length >= Math.max(4, TextureAtlas.maxAtlasPages)) { // Find the set of the largest 4 images, below the maximum size, with the highest // percentages used const pagesBySize = this._pages.filter(e => { return e.canvas.width * 2 <= (TextureAtlas.maxTextureSize || Constants.FORCED_MAX_TEXTURE_SIZE); }).sort((a, b) => { if (b.canvas.width !== a.canvas.width) { return b.canvas.width - a.canvas.width; } return b.percentageUsed - a.percentageUsed; }); let sameSizeI = -1; let size = 0; for (let i = 0; i < pagesBySize.length; i++) { if (pagesBySize[i].canvas.width !== size) { sameSizeI = i; size = pagesBySize[i].canvas.width; } else if (i - sameSizeI === 3) { break; } } // Gather details of the merge const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4); // Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot // effectively reduce page count and merging would cause issues. if (mergingPages.length < 4 || mergingPages.some(p => p.canvas.width !== mergingPages[0].canvas.width)) { const newPage = new AtlasPage(this._document, this._textureSize); this._pages.push(newPage); this._activePages.push(newPage); this._onAddTextureAtlasCanvas.fire(newPage.canvas); return newPage; } const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a > b ? 1 : -1); const mergedPageIndex = this.pages.length - mergingPages.length; // Merge into the new page const mergedPage = this._mergePages(mergingPages, mergedPageIndex); mergedPage.version++; // Delete the pages, shifting glyph texture pages as needed for (let i = sortedMergingPagesIndexes.length - 1; i >= 0; i--) { this._deletePage(sortedMergingPagesIndexes[i]); } // Add the new merged page to the end this.pages.push(mergedPage); // Request the model to be cleared to refresh all texture pages. this._requestClearModel = true; this._onAddTextureAtlasCanvas.fire(mergedPage.canvas); } // All new atlas pages are created small as they are highly dynamic const newPage = new AtlasPage(this._document, this._textureSize); this._pages.push(newPage); this._activePages.push(newPage); this._onAddTextureAtlasCanvas.fire(newPage.canvas); return newPage; } private _mergePages(mergingPages: AtlasPage[], mergedPageIndex: number): AtlasPage { const mergedSize = mergingPages[0].canvas.width * 2; const mergedPage = new AtlasPage(this._document, mergedSize, mergingPages); for (const [i, p] of mergingPages.entries()) { const xOffset = i * p.canvas.width % mergedSize; const yOffset = Math.floor(i / 2) * p.canvas.height; mergedPage.ctx.drawImage(p.canvas, xOffset, yOffset); for (const g of p.glyphs) { g.texturePage = mergedPageIndex; g.sizeClipSpace.x = g.size.x / mergedSize; g.sizeClipSpace.y = g.size.y / mergedSize; g.texturePosition.x += xOffset; g.texturePosition.y += yOffset; g.texturePositionClipSpace.x = g.texturePosition.x / mergedSize; g.texturePositionClipSpace.y = g.texturePosition.y / mergedSize; } this._onRemoveTextureAtlasCanvas.fire(p.canvas); // Remove the merging page from active pages if it was there const index = this._activePages.indexOf(p); if (index !== -1) { this._activePages.splice(index, 1); } } return mergedPage; } private _deletePage(pageIndex: number): void { this._pages.splice(pageIndex, 1); for (let j = pageIndex; j < this._pages.length; j++) { const adjustingPage = this._pages[j]; for (const g of adjustingPage.glyphs) { g.texturePage--; } adjustingPage.version++; } } public getRasterizedGlyphCombinedChar(chars: string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph { return this._getFromCacheMap(this._cacheMapCombined, chars, bg, fg, ext, restrictToCellHeight, domContainer); } public getRasterizedGlyph(code: number, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph { return this._getFromCacheMap(this._cacheMap, code, bg, fg, ext, restrictToCellHeight, domContainer); } /** * Gets the glyphs texture coords, drawing the texture if it's not already */ private _getFromCacheMap( cacheMap: FourKeyMap, key: string | number, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined ): IRasterizedGlyph { $glyph = cacheMap.get(key, bg, fg, ext); if (!$glyph) { $glyph = this._drawToCache(key, bg, fg, ext, restrictToCellHeight, domContainer); cacheMap.set(key, bg, fg, ext, $glyph); } return $glyph; } private _getColorFromAnsiIndex(idx: number): IColor { if (idx >= this._config.colors.ansi.length) { throw new Error('No color found for idx ' + idx); } return this._config.colors.ansi[idx]; } private _getBackgroundColor(bgColorMode: number, bgColor: number, inverse: boolean, dim: boolean): IColor { if (this._config.allowTransparency) { // The background color might have some transparency, so we need to render it as fully // transparent in the atlas. Otherwise we'd end up drawing the transparent background twice // around the anti-aliased edges of the glyph, and it would look too dark. return NULL_COLOR; } let result: IColor; switch (bgColorMode) { case Attributes.CM_P16: case Attributes.CM_P256: result = this._getColorFromAnsiIndex(bgColor); break; case Attributes.CM_RGB: const arr = AttributeData.toColorRGB(bgColor); result = channels.toColor(arr[0], arr[1], arr[2]); break; case Attributes.CM_DEFAULT: default: if (inverse) { result = color.opaque(this._config.colors.foreground); } else { result = this._config.colors.background; } break; } // Ignore alpha channel when allowTransparency is false if (!this._config.allowTransparency) { result = color.opaque(result); } return result; } private _getForegroundColor(bg: number, bgColorMode: number, bgColor: number, fg: number, fgColorMode: number, fgColor: number, inverse: boolean, dim: boolean, bold: boolean, excludeFromContrastRatioDemands: boolean): IColor { const minimumContrastColor = this._getMinimumContrastColor(bg, bgColorMode, bgColor, fg, fgColorMode, fgColor, inverse, bold, dim, excludeFromContrastRatioDemands); if (minimumContrastColor) { return minimumContrastColor; } let result: IColor; switch (fgColorMode) { case Attributes.CM_P16: case Attributes.CM_P256: if (this._config.drawBoldTextInBrightColors && bold && fgColor < 8) { fgColor += 8; } result = this._getColorFromAnsiIndex(fgColor); break; case Attributes.CM_RGB: const arr = AttributeData.toColorRGB(fgColor); result = channels.toColor(arr[0], arr[1], arr[2]); break; case Attributes.CM_DEFAULT: default: if (inverse) { result = this._config.colors.background; } else { result = this._config.colors.foreground; } } // Always use an opaque color regardless of allowTransparency if (this._config.allowTransparency) { result = color.opaque(result); } // Apply dim to the color, opacity is fine to use for the foreground color if (dim) { result = color.multiplyOpacity(result, DIM_OPACITY); } return result; } private _resolveBackgroundRgba(bgColorMode: number, bgColor: number, inverse: boolean): number { switch (bgColorMode) { case Attributes.CM_P16: case Attributes.CM_P256: return this._getColorFromAnsiIndex(bgColor).rgba; case Attributes.CM_RGB: return bgColor << 8; case Attributes.CM_DEFAULT: default: if (inverse) { return this._config.colors.foreground.rgba; } return this._config.colors.background.rgba; } } private _resolveForegroundRgba(fgColorMode: number, fgColor: number, inverse: boolean, bold: boolean): number { switch (fgColorMode) { case Attributes.CM_P16: case Attributes.CM_P256: if (this._config.drawBoldTextInBrightColors && bold && fgColor < 8) { fgColor += 8; } return this._getColorFromAnsiIndex(fgColor).rgba; case Attributes.CM_RGB: return fgColor << 8; case Attributes.CM_DEFAULT: default: if (inverse) { return this._config.colors.background.rgba; } return this._config.colors.foreground.rgba; } } private _getMinimumContrastColor(bg: number, bgColorMode: number, bgColor: number, fg: number, fgColorMode: number, fgColor: number, inverse: boolean, bold: boolean, dim: boolean, excludeFromContrastRatioDemands: boolean): IColor | undefined { if (this._config.minimumContrastRatio === 1 || excludeFromContrastRatioDemands) { return undefined; } // Try get from cache first const cache = this._getContrastCache(dim); const adjustedColor = cache.getColor(bg, fg); if (adjustedColor !== undefined) { return adjustedColor ?? undefined; } const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, inverse); const fgRgba = this._resolveForegroundRgba(fgColorMode, fgColor, inverse, bold); // Dim cells only require half the contrast, otherwise they wouldn't be distinguishable from // non-dim cells const result = rgba.ensureContrastRatio(bgRgba, fgRgba, this._config.minimumContrastRatio / (dim ? 2 : 1)); if (!result) { cache.setColor(bg, fg, null); return undefined; } const color = channels.toColor( (result >> 24) & 0xFF, (result >> 16) & 0xFF, (result >> 8) & 0xFF ); cache.setColor(bg, fg, color); return color; } private _getContrastCache(dim: boolean): IColorContrastCache { if (dim) { return this._config.colors.halfContrastCache; } return this._config.colors.contrastCache; } private _drawToCache(codeOrChars: number | string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph { const chars = typeof codeOrChars === 'number' ? String.fromCharCode(codeOrChars) : codeOrChars; // Uncomment for debugging // console.log(`draw to cache "${chars}"`, bg, fg, ext); // Attach the canvas to the DOM in order to inherit font-feature-settings // from the parent elements. This is necessary for ligatures and variants to // work. if (domContainer && this._tmpCanvas.parentElement !== domContainer) { this._tmpCanvas.style.display = 'none'; domContainer.append(this._tmpCanvas); } // Allow 1 cell width per character, with a minimum of 2 (CJK), plus some padding. This is used // to draw the glyph to the canvas as well as to restrict the bounding box search to ensure // giant ligatures (eg. =====>) don't impact overall performance. const allowedWidth = Math.min(this._config.deviceCellWidth * Math.max(chars.length, 2) + TMP_CANVAS_GLYPH_PADDING * 2, this._config.deviceMaxTextureSize); if (this._tmpCanvas.width < allowedWidth) { this._tmpCanvas.width = allowedWidth; } // Include line height when drawing glyphs const allowedHeight = Math.min(this._config.deviceCellHeight + TMP_CANVAS_GLYPH_PADDING * 4, this._textureSize); if (this._tmpCanvas.height < allowedHeight) { this._tmpCanvas.height = allowedHeight; } this._tmpCtx.save(); this._workAttributeData.fg = fg; this._workAttributeData.bg = bg; this._workAttributeData.extended.ext = ext; const invisible = !!this._workAttributeData.isInvisible(); if (invisible) { return NULL_RASTERIZED_GLYPH; } const bold = !!this._workAttributeData.isBold(); const inverse = !!this._workAttributeData.isInverse(); const dim = !!this._workAttributeData.isDim(); const italic = !!this._workAttributeData.isItalic(); const underline = !!this._workAttributeData.isUnderline(); const strikethrough = !!this._workAttributeData.isStrikethrough(); const overline = !!this._workAttributeData.isOverline(); let fgColor = this._workAttributeData.getFgColor(); let fgColorMode = this._workAttributeData.getFgColorMode(); let bgColor = this._workAttributeData.getBgColor(); let bgColorMode = this._workAttributeData.getBgColorMode(); if (inverse) { const temp = fgColor; fgColor = bgColor; bgColor = temp; const temp2 = fgColorMode; fgColorMode = bgColorMode; bgColorMode = temp2; } // draw the background const backgroundColor = this._getBackgroundColor(bgColorMode, bgColor, inverse, dim); // Use a 'copy' composite operation to clear any existing glyph out of _tmpCtxWithAlpha, // regardless of transparency in backgroundColor this._tmpCtx.globalCompositeOperation = 'copy'; this._tmpCtx.fillStyle = backgroundColor.css; this._tmpCtx.fillRect(0, 0, this._tmpCanvas.width, this._tmpCanvas.height); this._tmpCtx.globalCompositeOperation = 'source-over'; // draw the foreground/glyph const fontWeight = bold ? this._config.fontWeightBold : this._config.fontWeight; const fontStyle = italic ? 'italic' : ''; this._tmpCtx.font = `${fontStyle} ${fontWeight} ${this._config.fontSize * this._config.devicePixelRatio}px ${this._config.fontFamily}`; this._tmpCtx.textBaseline = TEXT_BASELINE; const powerlineGlyph = chars.length === 1 && isPowerlineGlyph(chars.charCodeAt(0)); const restrictedPowerlineGlyph = chars.length === 1 && isRestrictedPowerlineGlyph(chars.charCodeAt(0)); const foregroundColor = this._getForegroundColor(bg, bgColorMode, bgColor, fg, fgColorMode, fgColor, inverse, dim, bold, treatGlyphAsBackgroundColor(chars.charCodeAt(0))); this._tmpCtx.fillStyle = foregroundColor.css; // For powerline glyphs left/top padding is excluded (https://github.com/microsoft/vscode/issues/120129) const padding = restrictedPowerlineGlyph ? 0 : TMP_CANVAS_GLYPH_PADDING * 2; // Draw custom characters if applicable let customGlyph = false; if (this._config.customGlyphs !== false) { const variantOffset = this._workAttributeData.getUnderlineVariantOffset(); customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.deviceCharWidth, this._config.deviceCharHeight, this._config.fontSize, this._config.devicePixelRatio, backgroundColor.css, variantOffset); } // Whether to clear pixels based on a threshold difference between the glyph color and the // background color. This should be disabled when the glyph contains multiple colors such as // underline colors to prevent important colors could get cleared. let enableClearThresholdCheck = !powerlineGlyph; let chWidth: number; if (typeof codeOrChars === 'number') { chWidth = this._unicodeService.wcwidth(codeOrChars); } else { chWidth = this._unicodeService.getStringCellWidth(codeOrChars); } // Draw underline if (underline) { this._tmpCtx.save(); const lineWidth = Math.max(1, Math.floor(this._config.fontSize * this._config.devicePixelRatio / 15)); // When the line width is odd, draw at a 0.5 position const yOffset = lineWidth % 2 === 1 ? 0.5 : 0; this._tmpCtx.lineWidth = lineWidth; // Underline color if (this._workAttributeData.isUnderlineColorDefault()) { this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle; } else if (this._workAttributeData.isUnderlineColorRGB()) { enableClearThresholdCheck = false; this._tmpCtx.strokeStyle = `rgb(${AttributeData.toColorRGB(this._workAttributeData.getUnderlineColor()).join(',')})`; } else { enableClearThresholdCheck = false; let fg = this._workAttributeData.getUnderlineColor(); if (this._config.drawBoldTextInBrightColors && this._workAttributeData.isBold() && fg < 8) { fg += 8; } this._tmpCtx.strokeStyle = this._getColorFromAnsiIndex(fg).css; } this._tmpCtx.fillStyle = this._tmpCtx.strokeStyle; // Underline style/stroke this._tmpCtx.beginPath(); const xLeft = padding; const yTopDefault = Math.ceil(padding + this._config.deviceCharHeight) - yOffset - (restrictToCellHeight ? lineWidth * 2 : 0); const yBotDefault = yTopDefault + lineWidth * 2; let nextOffset = this._workAttributeData.getUnderlineVariantOffset(); let yTop = 0; let yBot = 0; for (let i = 0; i < chWidth; i++) { let wasFilled = false; this._tmpCtx.save(); yTop = yTopDefault; yBot = yBotDefault; const xChLeft = xLeft + i * this._config.deviceCellWidth; const xChRight = xLeft + (i + 1) * this._config.deviceCellWidth; switch (this._workAttributeData.extended.underlineStyle) { case UnderlineStyle.DOUBLE: this._tmpCtx.moveTo(xChLeft, yTopDefault); this._tmpCtx.lineTo(xChRight, yTopDefault); this._tmpCtx.moveTo(xChLeft, yBotDefault); this._tmpCtx.lineTo(xChRight, yBotDefault); break; case UnderlineStyle.CURLY: yTop = this._config.deviceCharHeight + 1; yBot = yTop + 3 * this._config.devicePixelRatio; const clipRegion = new Path2D(); clipRegion.rect(xChLeft, yTop, this._config.deviceCellWidth, yBot - yTop); this._tmpCtx.clip(clipRegion); // Draw a zigzag pattern, this is derived from the SVG used in monaco for the same // style. The viewbox is 6x3 so scale it using that. const cellW = this._config.deviceCellWidth; const curlyH = (yBot - yTop); const scaleX = cellW / 6; const scaleY = curlyH / 3; const polygons: number[][] = [ [0, 2, 1, 3, 2.4, 3, 0, 0.6], [5.5, 0, 2.5, 3, 1.1, 3, 4.1, 0], [4, 0, 6, 2, 6, 0.6, 5.4, 0], ]; for (const polygon of polygons) { this._tmpCtx.beginPath(); for (let i = 0; i < polygon.length; i += 2) { const x = xChLeft + polygon[i] * scaleX; const y = yBot - polygon[i + 1] * scaleY; if (i === 0) { this._tmpCtx.moveTo(x, y); } else { this._tmpCtx.lineTo(x, y); } } this._tmpCtx.closePath(); this._tmpCtx.fill(); } wasFilled = true; break; case UnderlineStyle.DOTTED: const offsetWidth = nextOffset === 0 ? 0 : (nextOffset >= lineWidth ? lineWidth * 2 - nextOffset : lineWidth - nextOffset); // a line and a gap. const isLineStart = nextOffset >= lineWidth ? false : true; if (isLineStart === false || offsetWidth === 0) { this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]); this._tmpCtx.moveTo(xChLeft + offsetWidth, yTopDefault); this._tmpCtx.lineTo(xChRight, yTopDefault); } else { this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]); this._tmpCtx.moveTo(xChLeft, yTopDefault); this._tmpCtx.lineTo(xChLeft + offsetWidth, yTopDefault); this._tmpCtx.moveTo(xChLeft + offsetWidth + lineWidth, yTopDefault); this._tmpCtx.lineTo(xChRight, yTopDefault); } nextOffset = computeNextVariantOffset(xChRight - xChLeft, lineWidth, nextOffset); break; case UnderlineStyle.DASHED: const lineRatio = 0.6; const gapRatio = 0.3; // End line ratio is approximately equal to 0.1 const xChWidth = xChRight - xChLeft; const line = Math.floor(lineRatio * xChWidth); const gap = Math.floor(gapRatio * xChWidth); const end = xChWidth - line - gap; this._tmpCtx.setLineDash([line, gap, end]); this._tmpCtx.moveTo(xChLeft, yTopDefault); this._tmpCtx.lineTo(xChRight, yTopDefault); break; case UnderlineStyle.SINGLE: default: this._tmpCtx.moveTo(xChLeft, yTopDefault); this._tmpCtx.lineTo(xChRight, yTopDefault); break; } if (!wasFilled) { this._tmpCtx.stroke(); } this._tmpCtx.restore(); } this._tmpCtx.restore(); // Draw stroke in the background color for non custom characters in order to give an outline // between the text and the underline. Only do this when font size is >= 12 as the underline // looks odd when the font size is too small if (!customGlyph && this._config.fontSize >= 12) { // This only works when transparency is disabled because it's not clear how to clear stroked // text if (!this._config.allowTransparency && chars !== ' ') { // Measure the text, only draw the stroke if there is a descent beyond an alphabetic text // baseline this._tmpCtx.save(); this._tmpCtx.textBaseline = 'alphabetic'; const metrics = this._tmpCtx.measureText(chars); this._tmpCtx.restore(); if ('actualBoundingBoxDescent' in metrics && metrics.actualBoundingBoxDescent > 0) { // This translates to 1/2 the line width in either direction this._tmpCtx.save(); // Clip the region to only draw in valid pixels near the underline to avoid a slight // outline around the whole glyph, as well as additional pixels in the glyph at the top // which would increase GPU memory demands const clipRegion = new Path2D(); clipRegion.rect(xLeft, yTop - Math.ceil(lineWidth / 2), this._config.deviceCellWidth * chWidth, yBot - yTop + Math.ceil(lineWidth / 2)); this._tmpCtx.clip(clipRegion); this._tmpCtx.lineWidth = this._config.devicePixelRatio * 3; this._tmpCtx.strokeStyle = backgroundColor.css; this._tmpCtx.strokeText(chars, padding, padding + this._config.deviceCharHeight); this._tmpCtx.restore(); } } } } // Overline if (overline) { const lineWidth = Math.max(1, Math.floor(this._config.fontSize * this._config.devicePixelRatio / 15)); const yOffset = lineWidth % 2 === 1 ? 0.5 : 0; this._tmpCtx.lineWidth = lineWidth; this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle; this._tmpCtx.beginPath(); this._tmpCtx.moveTo(padding, padding + yOffset); this._tmpCtx.lineTo(padding + this._config.deviceCharWidth * chWidth, padding + yOffset); this._tmpCtx.stroke(); } // Draw the character if (!customGlyph) { this._tmpCtx.fillText(chars, padding, padding + this._config.deviceCharHeight); } // If this character is underscore and beyond the cell bounds, shift it up until it is visible // even on the bottom row, try for a maximum of 5 pixels. if (chars === '_' && !this._config.allowTransparency) { let isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight), backgroundColor, foregroundColor, enableClearThresholdCheck); if (isBeyondCellBounds) { for (let offset = 1; offset <= 5; offset++) { this._tmpCtx.save(); this._tmpCtx.fillStyle = backgroundColor.css; this._tmpCtx.fillRect(0, 0, this._tmpCanvas.width, this._tmpCanvas.height); this._tmpCtx.restore(); this._tmpCtx.fillText(chars, padding, padding + this._config.deviceCharHeight - offset); isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight), backgroundColor, foregroundColor, enableClearThresholdCheck); if (!isBeyondCellBounds) { break; } } } } // Draw strokethrough if (strikethrough) { const lineWidth = Math.max(1, Math.floor(this._config.fontSize * this._config.devicePixelRatio / 10)); const yOffset = this._tmpCtx.lineWidth % 2 === 1 ? 0.5 : 0; // When the width is odd, draw at 0.5 position this._tmpCtx.lineWidth = lineWidth; this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle; this._tmpCtx.beginPath(); this._tmpCtx.moveTo(padding, padding + Math.floor(this._config.deviceCharHeight / 2) - yOffset); this._tmpCtx.lineTo(padding + this._config.deviceCharWidth * chWidth, padding + Math.floor(this._config.deviceCharHeight / 2) - yOffset); this._tmpCtx.stroke(); } this._tmpCtx.restore(); // clear the background from the character to avoid issues with drawing over the previous // character if it extends past it's bounds const imageData = this._tmpCtx.getImageData( 0, 0, this._tmpCanvas.width, this._tmpCanvas.height ); // Clear out the background color and determine if the glyph is empty. let isEmpty: boolean; if (!this._config.allowTransparency) { isEmpty = clearColor(imageData, backgroundColor, foregroundColor, enableClearThresholdCheck); } else { isEmpty = checkCompletelyTransparent(imageData); } // Handle empty glyphs if (isEmpty) { return NULL_RASTERIZED_GLYPH; } const rasterizedGlyph = this._findGlyphBoundingBox(imageData, this._workBoundingBox, allowedWidth, restrictedPowerlineGlyph, customGlyph, padding); // Find the best atlas row to use let activePage: AtlasPage; let activeRow: ICharAtlasActiveRow; while (true) { // If there are no active pages (the last smallest 4 were merged), create a new one if (this._activePages.length === 0) { const newPage = this._createNewPage(); activePage = newPage; activeRow = newPage.currentRow; activeRow.height = rasterizedGlyph.size.y; break; } // Get the best current row from all active pages activePage = this._activePages[this._activePages.length - 1]; activeRow = activePage.currentRow; for (const p of this._activePages) { if (rasterizedGlyph.size.y <= p.currentRow.height) { activePage = p; activeRow = p.currentRow; } } // TODO: This algorithm could be simplified: // - Search for the page with ROW_PIXEL_THRESHOLD in mind // - Keep track of current/fixed rows in a Map // Replace the best current row with a fixed row if there is one at least as good as the // current row. Search in reverse to prioritize filling in older pages. for (let i = this._activePages.length - 1; i >= 0; i--) { for (const row of this._activePages[i].fixedRows) { if (row.height <= activeRow.height && rasterizedGlyph.size.y <= row.height) { activePage = this._activePages[i]; activeRow = row; } } } // Create a new page for oversized glyphs as they come up if (rasterizedGlyph.size.x > this._textureSize) { if (!this._overflowSizePage) { this._overflowSizePage = new AtlasPage(this._document, this._config.deviceMaxTextureSize); this.pages.push(this._overflowSizePage); // Request the model to be cleared to refresh all texture pages. this._requestClearModel = true; this._onAddTextureAtlasCanvas.fire(this._overflowSizePage.canvas); } activePage = this._overflowSizePage; activeRow = this._overflowSizePage.currentRow; // Move to next row if necessary if (activeRow.x + rasterizedGlyph.size.x >= activePage.canvas.width) { activeRow.x = 0; activeRow.y += activeRow.height; activeRow.height = 0; } break; } // Create a new page if too much vertical space would be wasted or there is not enough room // left in the page. The previous active row will become fixed in the process as it now has a // fixed height if (activeRow.y + rasterizedGlyph.size.y >= activePage.canvas.height || activeRow.height > rasterizedGlyph.size.y + Constants.ROW_PIXEL_THRESHOLD) { // Create the new fixed height row, creating a new page if there isn't enough room on the // current page let wasPageAndRowFound = false; if (activePage.currentRow.y + activePage.currentRow.height + rasterizedGlyph.size.y >= activePage.canvas.height) { // Find the first page with room to create the new row on let candidatePage: AtlasPage | undefined; for (const p of this._activePages) { if (p.currentRow.y + p.currentRow.height + rasterizedGlyph.size.y < p.canvas.height) { candidatePage = p; break; } } if (candidatePage) { activePage = candidatePage; } else { // Before creating a new atlas page that would trigger a page merge, check if the // current active row is sufficient when ignoring the ROW_PIXEL_THRESHOLD. This will // improve texture utilization by using the available space before the page is merged // and becomes static. if ( TextureAtlas.maxAtlasPages && this._pages.length >= TextureAtlas.maxAtlasPages && activeRow.y + rasterizedGlyph.size.y <= activePage.canvas.height && activeRow.height >= rasterizedGlyph.size.y && activeRow.x + rasterizedGlyph.size.x <= activePage.canvas.width ) { // activePage and activeRow is already valid wasPageAndRowFound = true; } else { // Create a new page if there is no room const newPage = this._createNewPage(); activePage = newPage; activeRow = newPage.currentRow; activeRow.height = rasterizedGlyph.size.y; wasPageAndRowFound = true; } } } if (!wasPageAndRowFound) { // Fix the current row as the new row is being added below if (activePage.currentRow.height > 0) { activePage.fixedRows.push(activePage.currentRow); } activeRow = { x: 0, y: activePage.currentRow.y + activePage.currentRow.height, height: rasterizedGlyph.size.y }; activePage.fixedRows.push(activeRow); // Create the new current row below the new fixed height row activePage.currentRow = { x: 0, y: activeRow.y + activeRow.height, height: 0 }; } // TODO: Remove pages from _activePages when all rows are filled } // Exit the loop if there is enough room in the row if (activeRow.x + rasterizedGlyph.size.x <= activePage.canvas.width) { break; } // If there is not enough room in the current row, finish it and try again if (activeRow === activePage.currentRow) { activeRow.x = 0; activeRow.y += activeRow.height; activeRow.height = 0; } else { activePage.fixedRows.splice(activePage.fixedRows.indexOf(activeRow), 1); } } // Record texture position rasterizedGlyph.texturePage = this._pages.indexOf(activePage); rasterizedGlyph.texturePosition.x = activeRow.x; rasterizedGlyph.texturePosition.y = activeRow.y; rasterizedGlyph.texturePositionClipSpace.x = activeRow.x / activePage.canvas.width; rasterizedGlyph.texturePositionClipSpace.y = activeRow.y / activePage.canvas.height; // Fix the clipspace position as pages may be of differing size rasterizedGlyph.sizeClipSpace.x /= activePage.canvas.width; rasterizedGlyph.sizeClipSpace.y /= activePage.canvas.height; // Update atlas current row, for fixed rows the glyph height will never be larger than the row // height activeRow.height = Math.max(activeRow.height, rasterizedGlyph.size.y); activeRow.x += rasterizedGlyph.size.x; // putImageData doesn't do any blending, so it will overwrite any existing cache entry for us activePage.ctx.putImageData( imageData, rasterizedGlyph.texturePosition.x - this._workBoundingBox.left, rasterizedGlyph.texturePosition.y - this._workBoundingBox.top, this._workBoundingBox.left, this._workBoundingBox.top, rasterizedGlyph.size.x, rasterizedGlyph.size.y ); activePage.addGlyph(rasterizedGlyph); activePage.version++; return rasterizedGlyph; } /** * Given an ImageData object, find the bounding box of the non-transparent * portion of the texture and return an IRasterizedGlyph with these * dimensions. * @param imageData The image data to read. * @param boundingBox An IBoundingBox to put the clipped bounding box values. */ private _findGlyphBoundingBox(imageData: ImageData, boundingBox: IBoundingBox, allowedWidth: number, restrictedGlyph: boolean, customGlyph: boolean, padding: number): IRasterizedGlyph { boundingBox.top = 0; const height = restrictedGlyph ? this._config.deviceCellHeight : this._tmpCanvas.height; const width = restrictedGlyph ? this._config.deviceCellWidth : allowedWidth; let found = false; for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const alphaOffset = y * this._tmpCanvas.width * 4 + x * 4 + 3; if (imageData.data[alphaOffset] !== 0) { boundingBox.top = y; found = true; break; } } if (found) { break; } } boundingBox.left = 0; found = false; for (let x = 0; x < padding + width; x++) { for (let y = 0; y < height; y++) { const alphaOffset = y * this._tmpCanvas.width * 4 + x * 4 + 3; if (imageData.data[alphaOffset] !== 0) { boundingBox.left = x; found = true; break; } } if (found) { break; } } boundingBox.right = width; found = false; for (let x = padding + width - 1; x >= padding; x--) { for (let y = 0; y < height; y++) { const alphaOffset = y * this._tmpCanvas.width * 4 + x * 4 + 3; if (imageData.data[alphaOffset] !== 0) { boundingBox.right = x; found = true; break; } } if (found) { break; } } boundingBox.bottom = height; found = false; for (let y = height - 1; y >= 0; y--) { for (let x = 0; x < width; x++) { const alphaOffset = y * this._tmpCanvas.width * 4 + x * 4 + 3; if (imageData.data[alphaOffset] !== 0) { boundingBox.bottom = y; found = true; break; } } if (found) { break; } } return { texturePage: 0, texturePosition: { x: 0, y: 0 }, texturePositionClipSpace: { x: 0, y: 0 }, size: { x: boundingBox.right - boundingBox.left + 1, y: boundingBox.bottom - boundingBox.top + 1 }, sizeClipSpace: { x: (boundingBox.right - boundingBox.left + 1), y: (boundingBox.bottom - boundingBox.top + 1) }, offset: { x: -boundingBox.left + padding + ((restrictedGlyph || customGlyph) ? Math.floor((this._config.deviceCellWidth - this._config.deviceCharWidth) / 2) : 0), y: -boundingBox.top + padding + ((restrictedGlyph || customGlyph) ? this._config.lineHeight === 1 ? 0 : Math.round((this._config.deviceCellHeight - this._config.deviceCharHeight) / 2) : 0) } }; } } class AtlasPage { public readonly canvas: HTMLCanvasElement; public readonly ctx: CanvasRenderingContext2D; private _usedPixels: number = 0; public get percentageUsed(): number { return this._usedPixels / (this.canvas.width * this.canvas.height); } private readonly _glyphs: IRasterizedGlyph[] = []; public get glyphs(): ReadonlyArray { return this._glyphs; } public addGlyph(glyph: IRasterizedGlyph): void { this._glyphs.push(glyph); this._usedPixels += glyph.size.x * glyph.size.y; } /** * Used to check whether the canvas of the atlas page has changed. */ public version = 0; // Texture atlas current positioning data. The texture packing strategy used is to fill from // left-to-right and top-to-bottom. When the glyph being written is less than half of the current // row's height, the following happens: // // - The current row becomes the fixed height row A // - A new fixed height row B the exact size of the glyph is created below the current row // - A new dynamic height current row is created below B // // This strategy does a good job preventing space being wasted for very short glyphs such as // underscores, hyphens etc. or those with underlines rendered. public currentRow: ICharAtlasActiveRow = { x: 0, y: 0, height: 0 }; public readonly fixedRows: ICharAtlasActiveRow[] = []; constructor( document: Document, size: number, sourcePages?: AtlasPage[] ) { if (sourcePages) { for (const p of sourcePages) { this._glyphs.push(...p.glyphs); this._usedPixels += p._usedPixels; } } this.canvas = createCanvas(document, size, size); // The canvas needs alpha because we use clearColor to convert the background color to alpha. // It might also contain some characters with transparent backgrounds if allowTransparency is // set. this.ctx = throwIfFalsy(this.canvas.getContext('2d', { alpha: true })); } public clear(): void { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.currentRow.x = 0; this.currentRow.y = 0; this.currentRow.height = 0; this.fixedRows.length = 0; this.version++; } } /** * Makes a particular rgb color and colors that are nearly the same in an ImageData completely * transparent. * @returns True if the result is "empty", meaning all pixels are fully transparent. */ function clearColor(imageData: ImageData, bg: IColor, fg: IColor, enableThresholdCheck: boolean): boolean { // Get color channels const r = bg.rgba >>> 24; const g = bg.rgba >>> 16 & 0xFF; const b = bg.rgba >>> 8 & 0xFF; const fgR = fg.rgba >>> 24; const fgG = fg.rgba >>> 16 & 0xFF; const fgB = fg.rgba >>> 8 & 0xFF; // Calculate a threshold that when below a color will be treated as transpart when the sum of // channel value differs. This helps improve rendering when glyphs overlap with others. This // threshold is calculated relative to the difference between the background and foreground to // ensure important details of the glyph are always shown, even when the contrast ratio is low. // The number 12 is largely arbitrary to ensure the pixels that escape the cell in the test case // were covered (fg=#8ae234, bg=#c4a000). const threshold = Math.floor((Math.abs(r - fgR) + Math.abs(g - fgG) + Math.abs(b - fgB)) / 12); // Set alpha channel of relevent pixels to 0 let isEmpty = true; for (let offset = 0; offset < imageData.data.length; offset += 4) { // Check exact match if (imageData.data[offset] === r && imageData.data[offset + 1] === g && imageData.data[offset + 2] === b) { imageData.data[offset + 3] = 0; } else { // Check the threshold based difference if (enableThresholdCheck && (Math.abs(imageData.data[offset] - r) + Math.abs(imageData.data[offset + 1] - g) + Math.abs(imageData.data[offset + 2] - b)) < threshold) { imageData.data[offset + 3] = 0; } else { isEmpty = false; } } } return isEmpty; } function checkCompletelyTransparent(imageData: ImageData): boolean { for (let offset = 0; offset < imageData.data.length; offset += 4) { if (imageData.data[offset + 3] > 0) { return false; } } return true; } function createCanvas(document: Document, width: number, height: number): HTMLCanvasElement { const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; return canvas; } ================================================ FILE: addons/addon-webgl/src/TypedArray.test.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { assert } from 'chai'; import { sliceFallback } from './TypedArray'; type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Int8Array | Int16Array | Int32Array | Float32Array | Float64Array; function deepEquals(a: TypedArray, b: TypedArray): void { assert.equal(a.length, b.length); for (let i = 0; i < a.length; ++i) { assert.equal(a[i], b[i]); } } describe('polyfill conformance tests', function(): void { describe('TypedArray.slice', () => { describe('should work with all typed array types', () => { it('Uint8Array', () => { const a = new Uint8Array(5); deepEquals(sliceFallback(a, 2), a.slice(2)); deepEquals(sliceFallback(a, 65535), a.slice(65535)); deepEquals(sliceFallback(a, -1), a.slice(-1)); }); it('Uint16Array', () => { const u161 = new Uint16Array(5); const u162 = new Uint16Array(5); deepEquals(sliceFallback(u161, 2), u162.slice(2)); deepEquals(sliceFallback(u161, 65535), u162.slice(65535)); deepEquals(sliceFallback(u161, -1), u162.slice(-1)); }); it('Uint32Array', () => { const u321 = new Uint32Array(5); const u322 = new Uint32Array(5); deepEquals(sliceFallback(u321, 2), u322.slice(2)); deepEquals(sliceFallback(u321, 65537), u322.slice(65537)); deepEquals(sliceFallback(u321, -1), u322.slice(-1)); }); it('Int8Array', () => { const i81 = new Int8Array(5); const i82 = new Int8Array(5); deepEquals(sliceFallback(i81, 2), i82.slice(2)); deepEquals(sliceFallback(i81, 65537), i82.slice(65537)); deepEquals(sliceFallback(i81, -1), i82.slice(-1)); }); it('Int16Array', () => { const i161 = new Int16Array(5); const i162 = new Int16Array(5); deepEquals(sliceFallback(i161, 2), i162.slice(2)); deepEquals(sliceFallback(i161, 65535), i162.slice(65535)); deepEquals(sliceFallback(i161, -1), i162.slice(-1)); }); it('Int32Array', () => { const i321 = new Int32Array(5); const i322 = new Int32Array(5); deepEquals(sliceFallback(i321, 2), i322.slice(2)); deepEquals(sliceFallback(i321, 65537), i322.slice(65537)); deepEquals(sliceFallback(i321, -1), i322.slice(-1)); }); it('Float32Array', () => { const f321 = new Float32Array(5); const f322 = new Float32Array(5); deepEquals(sliceFallback(f321, 2), f322.slice(2)); deepEquals(sliceFallback(f321, 65537), f322.slice(65537)); deepEquals(sliceFallback(f321, -1), f322.slice(-1)); }); it('Float64Array', () => { const f641 = new Float64Array(5); const f642 = new Float64Array(5); deepEquals(sliceFallback(f641, 2), f642.slice(2)); deepEquals(sliceFallback(f641, 65537), f642.slice(65537)); deepEquals(sliceFallback(f641, -1), f642.slice(-1)); }); it('Uint8ClampedArray', () => { const u8Clamped1 = new Uint8ClampedArray(5); const u8Clamped2 = new Uint8ClampedArray(5); deepEquals(sliceFallback(u8Clamped1, 2), u8Clamped2.slice(2)); deepEquals(sliceFallback(u8Clamped1, 65537), u8Clamped2.slice(65537)); deepEquals(sliceFallback(u8Clamped1, -1), u8Clamped2.slice(-1)); }); }); it('start', () => { const arr = new Uint32Array([1, 2, 3, 4, 5]); deepEquals(sliceFallback(arr, -1), arr.slice(-1)); deepEquals(sliceFallback(arr, 0), arr.slice(0)); deepEquals(sliceFallback(arr, 1), arr.slice(1)); deepEquals(sliceFallback(arr, 2), arr.slice(2)); deepEquals(sliceFallback(arr, 3), arr.slice(3)); deepEquals(sliceFallback(arr, 4), arr.slice(4)); deepEquals(sliceFallback(arr, 5), arr.slice(5)); }); it('end', () => { const arr = new Uint32Array([1, 2, 3, 4, 5]); deepEquals(sliceFallback(arr, -1, -2), arr.slice(-1, -2)); deepEquals(sliceFallback(arr, 0, -2), arr.slice(0, -2)); deepEquals(sliceFallback(arr, 1, -2), arr.slice(1, -2)); deepEquals(sliceFallback(arr, 2, -2), arr.slice(2, -2)); deepEquals(sliceFallback(arr, 3, -2), arr.slice(3, -2)); deepEquals(sliceFallback(arr, 4, -2), arr.slice(4, -2)); deepEquals(sliceFallback(arr, 5, -2), arr.slice(5, -2)); deepEquals(sliceFallback(arr, -1, 3), arr.slice(-1, 3)); deepEquals(sliceFallback(arr, 0, 3), arr.slice(0, 3)); deepEquals(sliceFallback(arr, 1, 3), arr.slice(1, 3)); deepEquals(sliceFallback(arr, 2, 3), arr.slice(2, 3)); deepEquals(sliceFallback(arr, 3, 3), arr.slice(3, 3)); deepEquals(sliceFallback(arr, 4, 3), arr.slice(4, 3)); deepEquals(sliceFallback(arr, 5, 3), arr.slice(5, 3)); deepEquals(sliceFallback(arr, -1, 8), arr.slice(-1, 8)); deepEquals(sliceFallback(arr, 0, 8), arr.slice(0, 8)); deepEquals(sliceFallback(arr, 1, 8), arr.slice(1, 8)); deepEquals(sliceFallback(arr, 2, 8), arr.slice(2, 8)); deepEquals(sliceFallback(arr, 3, 8), arr.slice(3, 8)); deepEquals(sliceFallback(arr, 4, 8), arr.slice(4, 8)); deepEquals(sliceFallback(arr, 5, 8), arr.slice(5, 8)); }); }); }); ================================================ FILE: addons/addon-webgl/src/TypedArray.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ export type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Int8Array | Int16Array | Int32Array | Float32Array | Float64Array; export function slice(array: T, start?: number, end?: number): T { // all modern engines that support .slice if (array.slice) { return array.slice(start, end) as T; } return sliceFallback(array, start, end); } export function sliceFallback(array: T, start: number = 0, end: number = array.length): T { if (start < 0) { start = (array.length + start) % array.length; } if (end >= array.length) { end = array.length; } else { end = (array.length + end) % array.length; } start = Math.min(start, end); const result: T = new (array.constructor as any)(end - start); for (let i = 0; i < end - start; ++i) { result[i] = array[i + start]; } return result; } ================================================ FILE: addons/addon-webgl/src/Types.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { FontWeight } from '@xterm/xterm'; import { IColorSet } from 'browser/Types'; import { ISelectionRenderModel } from 'browser/renderer/shared/Types'; import { CursorInactiveStyle, CursorStyle, type IDisposable } from 'common/Types'; import type { IEvent } from 'common/Event'; export interface IRenderModel { cells: Uint32Array; lineLengths: Uint32Array; selection: ISelectionRenderModel; cursor?: ICursorRenderModel; } export interface ICursorRenderModel { x: number; y: number; width: number; style: CursorStyle | CursorInactiveStyle; cursorWidth: number; dpr: number; } export interface IWebGL2RenderingContext extends WebGLRenderingContext { vertexAttribDivisor(index: number, divisor: number): void; createVertexArray(): IWebGLVertexArrayObject; bindVertexArray(vao: IWebGLVertexArrayObject): void; drawElementsInstanced(mode: number, count: number, type: number, offset: number, instanceCount: number): void; } export interface IWebGLVertexArrayObject { } export interface ICharAtlasConfig { customGlyphs: boolean; devicePixelRatio: number; deviceMaxTextureSize: number; letterSpacing: number; lineHeight: number; fontSize: number; fontFamily: string; fontWeight: FontWeight; fontWeightBold: FontWeight; deviceCellWidth: number; deviceCellHeight: number; deviceCharWidth: number; deviceCharHeight: number; allowTransparency: boolean; drawBoldTextInBrightColors: boolean; minimumContrastRatio: number; colors: IColorSet; } export interface ITextureAtlas extends IDisposable { readonly pages: { canvas: HTMLCanvasElement, version: number }[]; onAddTextureAtlasCanvas: IEvent; onRemoveTextureAtlasCanvas: IEvent; /** * Warm up the texture atlas, adding common glyphs to avoid slowing early frame. */ warmUp(): void; /** * Call when a frame is being drawn, this will return true if the atlas was cleared to make room * for a new set of glyphs. */ beginFrame(): boolean; /** * Clear all glyphs from the texture atlas. */ clearTexture(): void; getRasterizedGlyph(code: number, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph; getRasterizedGlyphCombinedChar(chars: string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph; } /** * Represents a rasterized glyph within a texture atlas. Some numbers are * tracked in CSS pixels as well in order to reduce calculations during the * render loop. */ export interface IRasterizedGlyph { /** * The x and y offset between the glyph's top/left and the top/left of a cell * in pixels. */ offset: IVector; /** * The index of the texture page that the glyph is on. */ texturePage: number; /** * the x and y position of the glyph in the texture in pixels. */ texturePosition: IVector; /** * the x and y position of the glyph in the texture in clip space coordinates. */ texturePositionClipSpace: IVector; /** * The width and height of the glyph in the texture in pixels. */ size: IVector; /** * The width and height of the glyph in the texture in clip space coordinates. */ sizeClipSpace: IVector; } export interface IVector { x: number; y: number; } export interface IBoundingBox { top: number; left: number; right: number; bottom: number; } ================================================ FILE: addons/addon-webgl/src/WebglAddon.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import type { ITerminalAddon, Terminal } from '@xterm/xterm'; import type { IWebglAddonOptions, WebglAddon as IWebglApi } from '@xterm/addon-webgl'; import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services'; import { ITerminal } from 'browser/Types'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { getSafariVersion, isSafari } from 'common/Platform'; import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; import { IWebGL2RenderingContext } from './Types'; import { WebglRenderer } from './WebglRenderer'; import { Emitter, EventUtils } from 'common/Event'; export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi { private _terminal?: Terminal; private _renderer?: WebglRenderer; private readonly _onChangeTextureAtlas = this._register(new Emitter()); public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event; private readonly _onAddTextureAtlasCanvas = this._register(new Emitter()); public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event; private readonly _onRemoveTextureAtlasCanvas = this._register(new Emitter()); public readonly onRemoveTextureAtlasCanvas = this._onRemoveTextureAtlasCanvas.event; private readonly _onContextLoss = this._register(new Emitter()); public readonly onContextLoss = this._onContextLoss.event; private readonly _customGlyphs: boolean; private readonly _preserveDrawingBuffer?: boolean; constructor(options?: IWebglAddonOptions) { if (isSafari && getSafariVersion() < 16) { // Perform an extra check to determine if Webgl2 is manually enabled in developer settings const contextAttributes = { antialias: false, depth: false, preserveDrawingBuffer: true }; const gl = document.createElement('canvas').getContext('webgl2', contextAttributes) as IWebGL2RenderingContext; if (!gl) { throw new Error('Webgl2 is only supported on Safari 16 and above'); } } super(); this._customGlyphs = options?.customGlyphs ?? true; this._preserveDrawingBuffer = options?.preserveDrawingBuffer; } 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: ICoreService = core.coreService; const optionsService: IOptionsService = core.optionsService; const unsafeCore = core as any; 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 themeService: IThemeService = unsafeCore._themeService; this._renderer = this._register(new WebglRenderer( terminal, characterJoinerService, charSizeService, coreBrowserService, coreService, decorationService, optionsService, themeService, this._customGlyphs, this._preserveDrawingBuffer )); this._register(EventUtils.forward(this._renderer.onContextLoss, this._onContextLoss)); this._register(EventUtils.forward(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas)); this._register(EventUtils.forward(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas)); this._register(EventUtils.forward(this._renderer.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas)); renderService.setRenderer(this._renderer); this._register(toDisposable(() => { if ((this._terminal as any)._core._store._isDisposed) { return; } const renderService: IRenderService = (this._terminal as any)._core._renderService; renderService.setRenderer((this._terminal as any)._core._createRenderer()); renderService.handleResize(terminal.cols, terminal.rows); })); } public get textureAtlas(): HTMLCanvasElement | undefined { return this._renderer?.textureAtlas; } public clearTextureAtlas(): void { this._renderer?.clearTextureAtlas(); } } ================================================ FILE: addons/addon-webgl/src/WebglRenderer.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { ITerminal } from 'browser/Types'; import { CellColorResolver } from './CellColorResolver'; import { acquireTextureAtlas, removeTerminalFromCache } from './CharAtlasCache'; import { CursorBlinkStateManager } from './CursorBlinkStateManager'; import { observeDevicePixelDimensions } from './DevicePixelObserver'; import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/shared/Types'; import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { CharData, IBufferLine, ICellData } from 'common/Types'; import { AttributeData } from 'common/buffer/AttributeData'; import { CellData } from 'common/buffer/CellData'; import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants'; import { TextBlinkStateManager } from 'browser/renderer/shared/TextBlinkStateManager'; import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; import { Terminal } from '@xterm/xterm'; import { GlyphRenderer } from './GlyphRenderer'; import { RectangleRenderer } from './RectangleRenderer'; import { COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_EXT_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL, RenderModel } from './RenderModel'; import { IWebGL2RenderingContext, type ITextureAtlas } from './Types'; import { LinkRenderLayer } from './renderLayer/LinkRenderLayer'; import { IRenderLayer } from './renderLayer/Types'; import { Emitter, EventUtils } from 'common/Event'; import { addDisposableListener } from 'browser/Dom'; import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from 'common/Lifecycle'; import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils'; export class WebglRenderer extends Disposable implements IRenderer { private _renderLayers: IRenderLayer[]; private _cursorBlinkStateManager: MutableDisposable = new MutableDisposable(); private _textBlinkStateManager: TextBlinkStateManager; private _charAtlasDisposable = this._register(new MutableDisposable()); private _charAtlas: ITextureAtlas | undefined; private _devicePixelRatio: number; private _deviceMaxTextureSize: number; private _observerDisposable = this._register(new MutableDisposable()); private _model: RenderModel = new RenderModel(); private _rowHasBlinkingCells: boolean[] = []; private _rowHasBlinkingCellsCount: number = 0; private _workCell: ICellData = new CellData(); private _cellColorResolver: CellColorResolver; private _canvas: HTMLCanvasElement; private _gl: IWebGL2RenderingContext; private _rectangleRenderer: MutableDisposable = this._register(new MutableDisposable()); private _glyphRenderer: MutableDisposable = this._register(new MutableDisposable()); public readonly dimensions: IRenderDimensions; private _core: ITerminal; private _isAttached: boolean; private _contextRestorationTimeout: number | undefined; private readonly _onChangeTextureAtlas = this._register(new Emitter()); public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event; private readonly _onAddTextureAtlasCanvas = this._register(new Emitter()); public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event; private readonly _onRemoveTextureAtlasCanvas = this._register(new Emitter()); public readonly onRemoveTextureAtlasCanvas = this._onRemoveTextureAtlasCanvas.event; private readonly _onRequestRedraw = this._register(new Emitter()); public readonly onRequestRedraw = this._onRequestRedraw.event; private readonly _onContextLoss = this._register(new Emitter()); public readonly onContextLoss = this._onContextLoss.event; constructor( private _terminal: Terminal, private readonly _characterJoinerService: ICharacterJoinerService, private readonly _charSizeService: ICharSizeService, private readonly _coreBrowserService: ICoreBrowserService, private readonly _coreService: ICoreService, private readonly _decorationService: IDecorationService, private readonly _optionsService: IOptionsService, private readonly _themeService: IThemeService, private readonly _customGlyphs: boolean = true, preserveDrawingBuffer?: boolean ) { super(); // IMPORTANT: Canvas initialization and fetching of the context must be first in order to // prevent possible listeners leaking and continuing to operate after the WebglRenderer has been // discarded. this._canvas = this._coreBrowserService.mainDocument.createElement('canvas'); const contextAttributes = { antialias: false, depth: false, preserveDrawingBuffer }; this._gl = this._canvas.getContext('webgl2', contextAttributes) as IWebGL2RenderingContext; if (!this._gl) { throw new Error('WebGL2 not supported ' + this._gl); } this._register(this._themeService.onChangeColors(() => this._handleColorChange())); this._cellColorResolver = new CellColorResolver(this._terminal, this._optionsService, this._model.selection, this._decorationService, this._coreBrowserService, this._themeService); this._core = (this._terminal as any)._core; this._renderLayers = [ new LinkRenderLayer(this._core.screenElement!, 2, this._terminal, this._core.linkifier!, this._coreBrowserService, _optionsService, this._themeService) ]; this.dimensions = createRenderDimensions(); this._devicePixelRatio = this._coreBrowserService.dpr; this._updateDimensions(); this._updateCursorBlink(); this._register(_optionsService.onOptionChange(() => this._handleOptionsChanged())); this._textBlinkStateManager = this._register(new TextBlinkStateManager( () => this._requestRedrawViewport(), this._coreBrowserService, this._optionsService )); this._resetBlinkingRowState(); this._deviceMaxTextureSize = this._gl.getParameter(this._gl.MAX_TEXTURE_SIZE); this._register(addDisposableListener(this._canvas, 'webglcontextlost', (e) => { console.log('webglcontextlost event received'); // Prevent the default behavior in order to enable WebGL context restoration. e.preventDefault(); // Wait a few seconds to see if the 'webglcontextrestored' event is fired. // If not, dispatch the onContextLoss notification to observers. this._contextRestorationTimeout = setTimeout(() => { this._contextRestorationTimeout = undefined; console.warn('webgl context not restored; firing onContextLoss'); this._onContextLoss.fire(e); }, 3000 /* ms */); })); this._register(addDisposableListener(this._canvas, 'webglcontextrestored', (e) => { console.warn('webglcontextrestored event received'); clearTimeout(this._contextRestorationTimeout); this._contextRestorationTimeout = undefined; // The texture atlas and glyph renderer must be fully reinitialized // because their contents have been lost. removeTerminalFromCache(this._terminal); this._initializeWebGLState(); this._requestRedrawViewport(); })); this._observerDisposable.value = observeDevicePixelDimensions(this._canvas, this._coreBrowserService.window, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); this._register(this._coreBrowserService.onWindowChange(w => { this._observerDisposable.value = observeDevicePixelDimensions(this._canvas, w, (w, h) => this._setCanvasDevicePixelDimensions(w, h)); })); this._register(addDisposableListener(this._coreBrowserService.mainDocument, 'mousedown', () => this._cursorBlinkStateManager.value?.restartBlinkAnimation())); this._core.screenElement!.appendChild(this._canvas); [this._rectangleRenderer.value, this._glyphRenderer.value] = this._initializeWebGLState(); this._isAttached = this._core.screenElement!.isConnected; this._register(toDisposable(() => { for (const l of this._renderLayers) { l.dispose(); } this._canvas.parentElement?.removeChild(this._canvas); removeTerminalFromCache(this._terminal); })); } public get textureAtlas(): HTMLCanvasElement | undefined { return this._charAtlas?.pages[0].canvas; } private _handleColorChange(): void { this._refreshCharAtlas(); // Force a full refresh this._clearModel(true); } 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._terminal.cols, this._terminal.rows); } } public handleResize(cols: number, rows: number): void { // Update character and canvas dimensions this._updateDimensions(); this._model.resize(this._terminal.cols, this._terminal.rows); this._resetBlinkingRowState(); // Resize all render layers for (const l of this._renderLayers) { l.resize(this._terminal, this.dimensions); } // Resize the canvas this._canvas.width = this.dimensions.device.canvas.width; this._canvas.height = this.dimensions.device.canvas.height; this._canvas.style.width = `${this.dimensions.css.canvas.width}px`; this._canvas.style.height = `${this.dimensions.css.canvas.height}px`; // Resize the screen this._core.screenElement!.style.width = `${this.dimensions.css.canvas.width}px`; this._core.screenElement!.style.height = `${this.dimensions.css.canvas.height}px`; this._rectangleRenderer.value?.setDimensions(this.dimensions); this._rectangleRenderer.value?.handleResize(); this._glyphRenderer.value?.setDimensions(this.dimensions); this._glyphRenderer.value?.handleResize(); this._refreshCharAtlas(); // Force a full refresh. Resizing `_glyphRenderer` should clear it already, // so there is no need to clear it again here. this._clearModel(false); // Render synchronously to avoid flicker when the canvas is cleared this._onRequestRedraw.fire({ start: 0, end: this._terminal.rows - 1, sync: true }); } public handleCharSizeChanged(): void { this.handleResize(this._terminal.cols, this._terminal.rows); } public handleBlur(): void { for (const l of this._renderLayers) { l.handleBlur(this._terminal); } this._cursorBlinkStateManager.value?.pause(); // Request a redraw for active/inactive selection background this._requestRedrawViewport(); } public handleFocus(): void { for (const l of this._renderLayers) { l.handleFocus(this._terminal); } this._cursorBlinkStateManager.value?.resume(); // Request a redraw for active/inactive selection background this._requestRedrawViewport(); } public handleViewportVisibilityChange(isVisible: boolean): void { this._textBlinkStateManager.setViewportVisible(isVisible); } public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void { for (const l of this._renderLayers) { l.handleSelectionChanged(this._terminal, start, end, columnSelectMode); } this._model.selection.update(this._core, start, end, columnSelectMode); this._requestRedrawViewport(); } public handleCursorMove(): void { for (const l of this._renderLayers) { l.handleCursorMove(this._terminal); } this._cursorBlinkStateManager.value?.restartBlinkAnimation(); } private _handleOptionsChanged(): void { this._updateDimensions(); this._refreshCharAtlas(); this._updateCursorBlink(); } /** * Initializes members dependent on WebGL context state. */ private _initializeWebGLState(): [RectangleRenderer, GlyphRenderer] { this._rectangleRenderer.value = new RectangleRenderer(this._terminal, this._gl, this.dimensions, this._themeService); this._glyphRenderer.value = new GlyphRenderer(this._terminal, this._gl, this.dimensions, this._optionsService); // Update dimensions and acquire char atlas this.handleCharSizeChanged(); return [this._rectangleRenderer.value, this._glyphRenderer.value]; } /** * Refreshes the char atlas, aquiring a new one if necessary. */ private _refreshCharAtlas(): void { if (this.dimensions.device.char.width <= 0 && this.dimensions.device.char.height <= 0) { // Mark as not attached so char atlas gets refreshed on next render this._isAttached = false; return; } const atlas = acquireTextureAtlas( this._terminal, this._optionsService.rawOptions, this._themeService.colors, this.dimensions.device.cell.width, this.dimensions.device.cell.height, this.dimensions.device.char.width, this.dimensions.device.char.height, this._coreBrowserService.dpr, this._deviceMaxTextureSize, this._customGlyphs ); if (this._charAtlas !== atlas) { this._onChangeTextureAtlas.fire(atlas.pages[0].canvas); this._charAtlasDisposable.value = combinedDisposable( EventUtils.forward(atlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas), EventUtils.forward(atlas.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas) ); } this._charAtlas = atlas; this._charAtlas.warmUp(); this._glyphRenderer.value?.setAtlas(this._charAtlas); } /** * Clear the model. * @param clearGlyphRenderer Whether to also clear the glyph renderer. This * should be true generally to make sure it is in the same state as the model. */ private _clearModel(clearGlyphRenderer: boolean): void { this._model.clear(); if (clearGlyphRenderer) { this._glyphRenderer.value?.clear(); } } public clearTextureAtlas(): void { this._charAtlas?.clearTexture(); this._clearModel(true); this._requestRedrawViewport(); } public clear(): void { this._clearModel(true); for (const l of this._renderLayers) { l.reset(this._terminal); } this._resetBlinkingRowState(); this._textBlinkStateManager.setNeedsBlinkInViewport(false); this._cursorBlinkStateManager.value?.restartBlinkAnimation(); this._updateCursorBlink(); } public renderRows(start: number, end: number): void { if (!this._isAttached) { if (this._core.screenElement?.isConnected && this._charSizeService.width && this._charSizeService.height) { this._updateDimensions(); this._refreshCharAtlas(); this._isAttached = true; } else { return; } } // Update render layers for (const l of this._renderLayers) { l.handleGridChanged(this._terminal, start, end); } if (!this._glyphRenderer.value || !this._rectangleRenderer.value) { return; } // Tell renderer the frame is beginning // upon a model clear also refresh the full viewport model // (also triggered by an atlas page merge, part of #4480) if (this._glyphRenderer.value.beginFrame()) { this._clearModel(true); this._updateModel(0, this._terminal.rows - 1); } else { // just update changed lines to draw this._updateModel(start, end); } // Render this._rectangleRenderer.value.renderBackgrounds(); this._glyphRenderer.value.render(this._model); if (!this._cursorBlinkStateManager.value || this._cursorBlinkStateManager.value.isCursorVisible) { this._rectangleRenderer.value.renderCursor(); } } private _updateCursorBlink(): void { if (this._coreService.decPrivateModes.cursorBlink ?? this._terminal.options.cursorBlink) { this._cursorBlinkStateManager.value = new CursorBlinkStateManager(() => { this._requestRedrawCursor(); }, this._coreBrowserService); } else { this._cursorBlinkStateManager.clear(); } // Request a refresh from the terminal as management of rendering is being // moved back to the terminal this._requestRedrawCursor(); } private _updateModel(start: number, end: number): void { const terminal = this._core; let cell: ICellData = this._workCell; // Declare variable ahead of time to avoid garbage collection let lastBg: number; let y: number; let row: number; let line: IBufferLine; let joinedRanges: [number, number][]; let isJoined: boolean; let skipJoinedCheckUntilX: number = 0; let isValidJoinRange: boolean = true; let lastCharX: number; let range: [number, number]; let isCursorRow: boolean; let chars: string; let code: number; let width: number; let i: number; let x: number; let j: number; start = clamp(start, terminal.rows - 1, 0); end = clamp(end, terminal.rows - 1, 0); const cursorStyle = this._coreService.decPrivateModes.cursorStyle ?? terminal.options.cursorStyle ?? 'block'; const cursorY = this._terminal.buffer.active.baseY + this._terminal.buffer.active.cursorY; const viewportRelativeCursorY = cursorY - terminal.buffer.ydisp; // in case cursor.x == cols adjust visual cursor to cols - 1 const cursorX = Math.min(this._terminal.buffer.active.cursorX, terminal.cols - 1); let lastCursorX = -1; const isCursorVisible = this._coreService.isCursorInitialized && !this._coreService.isCursorHidden && (!this._cursorBlinkStateManager.value || this._cursorBlinkStateManager.value.isCursorVisible); this._model.cursor = undefined; let modelUpdated = false; for (y = start; y <= end; y++) { row = y + terminal.buffer.ydisp; line = terminal.buffer.lines.get(row)!; let rowHasBlinkingCells = false; this._model.lineLengths[y] = 0; isCursorRow = cursorY === row; skipJoinedCheckUntilX = 0; joinedRanges = this._characterJoinerService.getJoinedCharacters(row); for (x = 0; x < terminal.cols; x++) { lastBg = this._cellColorResolver.result.bg; line.loadCell(x, cell); if (x === 0) { lastBg = this._cellColorResolver.result.bg; } // If true, indicates that the current character(s) to draw were joined. isJoined = false; // Indicates whether this cell is part of a joined range that should be ignored as it cannot // be rendered entirely, like the selection state differs across the range. isValidJoinRange = (x >= skipJoinedCheckUntilX); lastCharX = x; // 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. if (joinedRanges.length > 0 && x === joinedRanges[0][0] && isValidJoinRange) { range = joinedRanges.shift()!; // If the ligature's selection state is not consistent, don't join it. This helps the // selection render correctly regardless whether they should be joined. const firstSelectionState = this._model.selection.isCellSelected(this._terminal, range[0], row); for (i = range[0] + 1; i < range[1]; i++) { isValidJoinRange &&= (firstSelectionState === this._model.selection.isCellSelected(this._terminal, i, row)); } // Similarly, if the cursor is in the ligature, don't join it. isValidJoinRange &&= !isCursorRow || cursorX < range[0] || cursorX >= range[1]; if (!isValidJoinRange) { skipJoinedCheckUntilX = range[1]; } else { isJoined = true; // We already know the exact start and end column of the joined range, // so we get the string and width representing it directly. cell = new JoinedCellData( cell, line!.translateToString(true, range[0], range[1]), range[1] - range[0] ); // Skip over the cells occupied by this range in the loop lastCharX = range[1] - 1; } } chars = cell.getChars(); code = cell.getCode(); i = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; if (!rowHasBlinkingCells && cell.isBlink()) { rowHasBlinkingCells = true; } // Load colors/resolve overrides into work colors this._cellColorResolver.resolve(cell, x, row, this.dimensions.device.cell.width, this.dimensions.device.cell.height); // Override colors for cursor cell if (isCursorVisible && row === cursorY) { if (x === cursorX) { this._model.cursor = { x: cursorX, y: viewportRelativeCursorY, width: cell.getWidth(), style: this._coreBrowserService.isFocused ? cursorStyle : terminal.options.cursorInactiveStyle, cursorWidth: terminal.options.cursorWidth, dpr: this._devicePixelRatio }; lastCursorX = cursorX + cell.getWidth() - 1; } if (x >= cursorX && x <= lastCursorX && ((this._coreBrowserService.isFocused && cursorStyle === 'block') || (this._coreBrowserService.isFocused === false && terminal.options.cursorInactiveStyle === 'block')) ) { this._cellColorResolver.result.fg = Attributes.CM_RGB | (this._themeService.colors.cursorAccent.rgba >> 8 & Attributes.RGB_MASK); this._cellColorResolver.result.bg = Attributes.CM_RGB | (this._themeService.colors.cursor.rgba >> 8 & Attributes.RGB_MASK); } } if (this._textBlinkStateManager.isEnabled && !this._textBlinkStateManager.isBlinkOn && cell.isBlink()) { this._cellColorResolver.result.fg |= FgFlags.INVISIBLE; } if (code !== NULL_CELL_CODE) { this._model.lineLengths[y] = x + 1; } // Nothing has changed, no updates needed if (this._model.cells[i] === code && this._model.cells[i + RENDER_MODEL_BG_OFFSET] === this._cellColorResolver.result.bg && this._model.cells[i + RENDER_MODEL_FG_OFFSET] === this._cellColorResolver.result.fg && this._model.cells[i + RENDER_MODEL_EXT_OFFSET] === this._cellColorResolver.result.ext) { continue; } modelUpdated = true; // Flag combined chars with a bit mask so they're easily identifiable if (chars.length > 1) { code |= COMBINED_CHAR_BIT_MASK; } // Cache the results in the model this._model.cells[i] = code; this._model.cells[i + RENDER_MODEL_BG_OFFSET] = this._cellColorResolver.result.bg; this._model.cells[i + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg; this._model.cells[i + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext; width = cell.getWidth(); this._glyphRenderer.value!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, width, lastBg); if (isJoined) { // Restore work cell cell = this._workCell; // Null out non-first cells for (x++; x <= lastCharX; x++) { j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; this._glyphRenderer.value!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0, 0); this._model.cells[j] = NULL_CELL_CODE; // Don't re-resolve the cell color since multi-colored ligature backgrounds are not // supported this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._cellColorResolver.result.bg; this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg; this._model.cells[j + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext; } x--; // Go back to the previous update cell for next iteration } } this._setRowBlinkState(y, rowHasBlinkingCells); } if (modelUpdated) { this._rectangleRenderer.value!.updateBackgrounds(this._model); } this._rectangleRenderer.value!.updateCursor(this._model); this._updateTextBlinkState(); } private _resetBlinkingRowState(): void { this._rowHasBlinkingCells = new Array(this._terminal.rows).fill(false); this._rowHasBlinkingCellsCount = 0; } private _setRowBlinkState(row: number, hasBlinkingCells: boolean): void { const previous = this._rowHasBlinkingCells[row]; if (previous === hasBlinkingCells) { return; } this._rowHasBlinkingCells[row] = hasBlinkingCells; this._rowHasBlinkingCellsCount += hasBlinkingCells ? 1 : -1; } private _updateTextBlinkState(): void { this._textBlinkStateManager.setNeedsBlinkInViewport(this._rowHasBlinkingCellsCount > 0); } /** * Recalculates the character and canvas dimensions. */ private _updateDimensions(): void { // Perform a new measure if the CharMeasure dimensions are not yet available if (!this._charSizeService.width || !this._charSizeService.height) { return; } // Calculate the device character width. Width is floored as it must be drawn to an integer grid // in order for the char atlas glyphs to not be blurry. this.dimensions.device.char.width = Math.floor(this._charSizeService.width * this._devicePixelRatio); // Calculate the device character height. Height is ceiled in case devicePixelRatio is a // floating point number in order to ensure there is enough space to draw the character to the // cell. this.dimensions.device.char.height = Math.ceil(this._charSizeService.height * this._devicePixelRatio); // Calculate the device cell height, if lineHeight is _not_ 1, the resulting value will be // floored since lineHeight can never be lower then 1, this guarentees the device cell height // will always be larger than device char height. this.dimensions.device.cell.height = Math.floor(this.dimensions.device.char.height * this._optionsService.rawOptions.lineHeight); // Calculate the y offset within a cell that glyph should draw at in order for it to be centered // correctly within the cell. this.dimensions.device.char.top = this._optionsService.rawOptions.lineHeight === 1 ? 0 : Math.round((this.dimensions.device.cell.height - this.dimensions.device.char.height) / 2); // Calculate the device cell width, taking the letterSpacing into account. this.dimensions.device.cell.width = this.dimensions.device.char.width + Math.round(this._optionsService.rawOptions.letterSpacing); // Calculate the x offset with a cell that text should draw from in order for it to be centered // correctly within the cell. this.dimensions.device.char.left = Math.floor(this._optionsService.rawOptions.letterSpacing / 2); // Recalculate the canvas dimensions, the device dimensions define the actual number of pixel in // the canvas this.dimensions.device.canvas.height = this._terminal.rows * this.dimensions.device.cell.height; this.dimensions.device.canvas.width = this._terminal.cols * this.dimensions.device.cell.width; // The the size of the canvas on the page. It's important that this rounds to nearest integer // and not ceils as browsers often have floating point precision issues where // `window.devicePixelRatio` ends up being something like `1.100000023841858` for example, when // it's actually 1.1. Ceiling may causes blurriness as the backing canvas image is 1 pixel too // large for the canvas element size. this.dimensions.css.canvas.height = Math.round(this.dimensions.device.canvas.height / this._devicePixelRatio); this.dimensions.css.canvas.width = Math.round(this.dimensions.device.canvas.width / this._devicePixelRatio); // Get the CSS dimensions of an individual cell. This needs to be derived from the calculated // device pixel canvas value above. CharMeasure.width/height by itself is insufficient when the // page is not at 100% zoom level as CharMeasure is measured in CSS pixels, but the actual char // size on the canvas can differ. this.dimensions.css.cell.height = this.dimensions.device.cell.height / this._devicePixelRatio; this.dimensions.css.cell.width = this.dimensions.device.cell.width / this._devicePixelRatio; } private _setCanvasDevicePixelDimensions(width: number, height: number): void { if (this._canvas.width === width && this._canvas.height === height) { return; } // While the actual canvas size has changed, keep device canvas dimensions as the value before // the change as it's an exact multiple of the cell sizes. this._canvas.width = width; this._canvas.height = height; // Render synchronously to avoid flicker when the canvas is cleared this._onRequestRedraw.fire({ start: 0, end: this._terminal.rows - 1, sync: true }); } private _requestRedrawViewport(): void { this._onRequestRedraw.fire({ start: 0, end: this._terminal.rows - 1 }); } private _requestRedrawCursor(): void { const cursorY = this._terminal.buffer.active.cursorY; this._onRequestRedraw.fire({ start: cursorY, end: cursorY }); } } // TODO: Share impl with core export class JoinedCellData extends AttributeData implements ICellData { private _width: number; // .content carries no meaning for joined CellData, simply nullify it // thus we have to overload all other .content accessors public content: number = 0; public fg: number; public bg: number; public combinedData: string = ''; constructor(firstCell: ICellData, chars: string, width: number) { super(); this.fg = firstCell.fg; this.bg = firstCell.bg; this.combinedData = chars; this._width = width; } public isCombined(): number { // always mark joined cell data as combined return Content.IS_COMBINED_MASK; } public getWidth(): number { return this._width; } public getChars(): string { return this.combinedData; } public getCode(): number { // code always gets the highest possible fake codepoint (read as -1) // this is needed as code is used by caches as identifier return 0x1FFFFF; } public setFromCharData(value: CharData): void { throw new Error('not implemented'); } public getAsCharData(): CharData { return [this.fg, this.getChars(), this.getWidth(), this.getCode()]; } } function clamp(value: number, max: number, min: number = 0): number { return Math.max(Math.min(value, max), min); } ================================================ FILE: addons/addon-webgl/src/WebglUtils.ts ================================================ /** * Copyright (c) 2018 The xterm.js authors. All rights reserved. * @license MIT */ import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; /** * A matrix that when multiplies will translate 0-1 coordinates (left to right, * top to bottom) to clip space. */ export const PROJECTION_MATRIX = new Float32Array([ 2, 0, 0, 0, 0, -2, 0, 0, 0, 0, 1, 0, -1, 1, 0, 1 ]); export function createProgram(gl: WebGLRenderingContext, vertexSource: string, fragmentSource: string): WebGLProgram | undefined { const program = throwIfFalsy(gl.createProgram()); gl.attachShader(program, throwIfFalsy(createShader(gl, gl.VERTEX_SHADER, vertexSource))); gl.attachShader(program, throwIfFalsy(createShader(gl, gl.FRAGMENT_SHADER, fragmentSource))); gl.linkProgram(program); const success = gl.getProgramParameter(program, gl.LINK_STATUS); if (success) { return program; } console.error(gl.getProgramInfoLog(program)); gl.deleteProgram(program); } export function createShader(gl: WebGLRenderingContext, type: number, source: string): WebGLShader | undefined { const shader = throwIfFalsy(gl.createShader(type)); gl.shaderSource(shader, source); gl.compileShader(shader); const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (success) { return shader; } console.error(gl.getShaderInfoLog(shader)); gl.deleteShader(shader); } export function expandFloat32Array(source: Float32Array, max: number): Float32Array { const newLength = Math.min(source.length * 2, max); const newArray = new Float32Array(newLength); for (let i = 0; i < source.length; i++) { newArray[i] = source[i]; } return newArray; } export class GLTexture { public texture: WebGLTexture; public version: number; constructor(texture: WebGLTexture) { this.texture = texture; this.version = -1; } } ================================================ FILE: addons/addon-webgl/src/customGlyphs/CustomGlyphDefinitions.ts ================================================ /** * Copyright (c) 2021 The xterm.js authors. All rights reserved. * @license MIT */ import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphCharacterDefinition, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition } from './Types'; /* eslint-disable max-len */ const enum Shapes { /** │ */ TOP_TO_BOTTOM = 'M.5,0 L.5,1', /** ─ */ LEFT_TO_RIGHT = 'M0,.5 L1,.5', /** └ */ TOP_TO_RIGHT = 'M.5,0 L.5,.5 L1,.5', /** ┘ */ TOP_TO_LEFT = 'M.5,0 L.5,.5 L0,.5', /** ┐ */ LEFT_TO_BOTTOM = 'M0,.5 L.5,.5 L.5,1', /** ┌ */ RIGHT_TO_BOTTOM = 'M0.5,1 L.5,.5 L1,.5', /** ╵ */ MIDDLE_TO_TOP = 'M.5,.5 L.5,0', /** ╴ */ MIDDLE_TO_LEFT = 'M.5,.5 L0,.5', /** ╶ */ MIDDLE_TO_RIGHT = 'M.5,.5 L1,.5', /** ╷ */ MIDDLE_TO_BOTTOM = 'M.5,.5 L.5,1', /** ┴ */ T_TOP = 'M0,.5 L1,.5 M.5,.5 L.5,0', /** ┤ */ T_LEFT = 'M.5,0 L.5,1 M.5,.5 L0,.5', /** ├ */ T_RIGHT = 'M.5,0 L.5,1 M.5,.5 L1,.5', /** ┬ */ T_BOTTOM = 'M0,.5 L1,.5 M.5,.5 L.5,1', /** ┼ */ CROSS = 'M0,.5 L1,.5 M.5,0 L.5,1', /** ╌ */ TWO_DASHES_HORIZONTAL = 'M.1,.5 L.4,.5 M.6,.5 L.9,.5', // .2 empty, .3 filled /** ┄ */ THREE_DASHES_HORIZONTAL = 'M.0667,.5 L.2667,.5 M.4,.5 L.6,.5 M.7333,.5 L.9333,.5', // .1333 empty, .2 filled /** ┉ */ FOUR_DASHES_HORIZONTAL = 'M.05,.5 L.2,.5 M.3,.5 L.45,.5 M.55,.5 L.7,.5 M.8,.5 L.95,.5', // .1 empty, .15 filled /** ╎ */ TWO_DASHES_VERTICAL = 'M.5,.1 L.5,.4 M.5,.6 L.5,.9', /** ┆ */ THREE_DASHES_VERTICAL = 'M.5,.0667 L.5,.2667 M.5,.4 L.5,.6 M.5,.7333 L.5,.9333', /** ┊ */ FOUR_DASHES_VERTICAL = 'M.5,.05 L.5,.2 M.5,.3 L.5,.45 L.5,.55 M.5,.7 L.5,.95', } namespace GitBranchSymbolsParts { // Lines export const LINE_H: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_RIGHT, strokeWidth: 1 }); export const LINE_V: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_BOTTOM, strokeWidth: 1 }); // Fading lines export const FADE_RIGHT: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,.5 L.28,.5 M.32,.5 L.52,.5 M.60,.5 L.72,.5 M.84,.5 L.90,.5', strokeWidth: 1 }); export const FADE_LEFT: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.10,.5 L.16,.5 M.28,.5 L.40,.5 M.48,.5 L.68,.5 M.72,.5 L1,.5', strokeWidth: 1 }); export const FADE_DOWN: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,0 L.5,.28 M.5,.32 L.5,.52 M.5,.60 L.5,.72 M.5,.84 L.5,.90', strokeWidth: 1 }); export const FADE_UP: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,.10 L.5,.16 M.5,.28 L.5,.40 M.5,.48 L.5,.68 M.5,.72 L.5,1', strokeWidth: 1 }); // Curved corners export const CURVE_DOWN_RIGHT: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp: number, yp: number) => `M.5,1 L.5,${.5 + (yp / .15 * .5)} C.5,${.5 + (yp / .15 * .5)},.5,.5,1,.5`, strokeWidth: 1 }); export const CURVE_DOWN_LEFT: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp: number, yp: number) => `M.5,1 L.5,${.5 + (yp / .15 * .5)} C.5,${.5 + (yp / .15 * .5)},.5,.5,0,.5`, strokeWidth: 1 }); export const CURVE_UP_RIGHT: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp: number, yp: number) => `M.5,0 L.5,${.5 - (yp / .15 * .5)} C.5,${.5 - (yp / .15 * .5)},.5,.5,1,.5`, strokeWidth: 1 }); export const CURVE_UP_LEFT: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp: number, yp: number) => `M.5,0 L.5,${.5 - (yp / .15 * .5)} C.5,${.5 - (yp / .15 * .5)},.5,.5,0,.5`, strokeWidth: 1 }); // Node parts export const NODE_FILL: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH, data: 'M.85,.5 A.35,.175,0,1,1,.15,.5 A.35,.175,0,1,1,.85,.5' }); export const NODE_STROKE: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH, data: 'M.85,.5 A.35,.175,0,1,1,.15,.5 A.35,.175,0,1,1,.85,.5', strokeWidth: 1 }); export const NODE_LINE_RIGHT: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH, data: 'M1,.5 L.85,.5', strokeWidth: 1 }); export const NODE_LINE_LEFT: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH, data: 'M0,.5 L.15,.5', strokeWidth: 1 }); export const NODE_LINE_DOWN: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH, data: 'M.5,1 L.5,.7', strokeWidth: 1 }); export const NODE_LINE_UP: CustomGlyphDefinitionPart = Object.freeze({ type: CustomGlyphDefinitionType.PATH, data: 'M.5,0 L.5,.3', strokeWidth: 1 }); } export const customGlyphDefinitions: { [index: string]: CustomGlyphCharacterDefinition | undefined } = { // #region Box Drawing (2500-257F) // https://www.unicode.org/charts/PDF/U2500.pdf // Light and heavy solid lines (2500-2503) '─': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_RIGHT, strokeWidth: 1 }, '━': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_RIGHT, strokeWidth: 3 }, '│': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_BOTTOM, strokeWidth: 1 }, '┃': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_BOTTOM, strokeWidth: 3 }, // Light and heavy dashed lines (2504-250B) '┄': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.THREE_DASHES_HORIZONTAL, strokeWidth: 1 }, '┅': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.THREE_DASHES_HORIZONTAL, strokeWidth: 3 }, '┆': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.THREE_DASHES_VERTICAL, strokeWidth: 1 }, '┇': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.THREE_DASHES_VERTICAL, strokeWidth: 3 }, '┈': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.FOUR_DASHES_HORIZONTAL, strokeWidth: 1 }, '┉': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.FOUR_DASHES_HORIZONTAL, strokeWidth: 3 }, '┊': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.FOUR_DASHES_VERTICAL, strokeWidth: 1 }, '┋': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.FOUR_DASHES_VERTICAL, strokeWidth: 3 }, // Light and heavy line box components (250C-254B) '┌': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.RIGHT_TO_BOTTOM, strokeWidth: 1 }, '┍': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 3 }], '┎': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 3 }], '┏': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.RIGHT_TO_BOTTOM, strokeWidth: 3 }, '┐': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_BOTTOM, strokeWidth: 1 }, '┑': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 3 }], '┒': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 3 }], '┓': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_BOTTOM, strokeWidth: 3 }, '└': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_RIGHT, strokeWidth: 1 }, '┕': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 3 }], '┖': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 3 }], '┗': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_RIGHT, strokeWidth: 3 }, '┘': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_LEFT, strokeWidth: 1 }, '┙': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 3 }], '┚': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 3 }], '┛': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_LEFT, strokeWidth: 3 }, '├': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.T_RIGHT, strokeWidth: 1 }, '┝': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 3 }], '┞': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.RIGHT_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 3 }], '┟': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 3 }], '┠': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_BOTTOM, strokeWidth: 3 }], '┡': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_RIGHT, strokeWidth: 3 }], '┢': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.RIGHT_TO_BOTTOM, strokeWidth: 3 }], '┣': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.T_RIGHT, strokeWidth: 3 }, '┤': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.T_LEFT, strokeWidth: 1 }, '┥': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 3 }], '┦': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 3 }], '┧': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_LEFT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 3 }], '┨': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_BOTTOM, strokeWidth: 3 }], '┩': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_LEFT, strokeWidth: 3 }], '┪': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_BOTTOM, strokeWidth: 3 }], '┫': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.T_LEFT, strokeWidth: 3 }, '┬': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.T_BOTTOM, strokeWidth: 1 }, '┭': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.RIGHT_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 3 }], '┮': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 3 }], '┯': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_RIGHT, strokeWidth: 3 }], '┰': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 3 }], '┱': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_BOTTOM, strokeWidth: 3 }], '┲': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.RIGHT_TO_BOTTOM, strokeWidth: 3 }], '┳': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.T_BOTTOM, strokeWidth: 3 }, '┴': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.T_TOP, strokeWidth: 1 }, '┵': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 3 }], '┶': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_LEFT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 3 }], '┷': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_RIGHT, strokeWidth: 3 }], '┸': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 3 }], '┹': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_LEFT, strokeWidth: 3 }], '┺': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_RIGHT, strokeWidth: 3 }], '┻': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.T_TOP, strokeWidth: 3 }, '┼': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.CROSS, strokeWidth: 1 }, '┽': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_RIGHT}`, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 3 }], '┾': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_LEFT}`, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 3 }], '┿': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_RIGHT, strokeWidth: 3 }], '╀': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: `${Shapes.LEFT_TO_RIGHT} ${Shapes.MIDDLE_TO_BOTTOM}`, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 3 }], '╁': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: `${Shapes.MIDDLE_TO_TOP} ${Shapes.LEFT_TO_RIGHT}`, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 3 }], '╂': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_BOTTOM, strokeWidth: 3 }], '╃': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.RIGHT_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_LEFT, strokeWidth: 3 }], '╄': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_RIGHT, strokeWidth: 3 }], '╅': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.LEFT_TO_BOTTOM, strokeWidth: 3 }], '╆': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TOP_TO_LEFT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.RIGHT_TO_BOTTOM, strokeWidth: 3 }], '╇': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: `${Shapes.MIDDLE_TO_TOP} ${Shapes.LEFT_TO_RIGHT}`, strokeWidth: 3 }], '╈': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: `${Shapes.LEFT_TO_RIGHT} ${Shapes.MIDDLE_TO_BOTTOM}`, strokeWidth: 3 }], '╉': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_LEFT}`, strokeWidth: 3 }], '╊': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: `${Shapes.TOP_TO_BOTTOM} ${Shapes.MIDDLE_TO_RIGHT}`, strokeWidth: 3 }], '╋': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.CROSS, strokeWidth: 3 }, // Light and heavy dashed lines (254C-254F) '╌': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TWO_DASHES_HORIZONTAL, strokeWidth: 1 }, '╍': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TWO_DASHES_HORIZONTAL, strokeWidth: 3 }, '╎': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TWO_DASHES_VERTICAL, strokeWidth: 1 }, '╏': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.TWO_DASHES_VERTICAL, strokeWidth: 3 }, // Double lines (2550-2551) '═': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp}`, strokeWidth: 1 }, '║': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1`, strokeWidth: 1 }, // Light and double line box components (2552-256C) '╒': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M.5,1 L.5,${.5 - yp} L1,${.5 - yp} M.5,${.5 + yp} L1,${.5 + yp}`, strokeWidth: 1 }, '╓': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M${.5 - xp},1 L${.5 - xp},.5 L1,.5 M${.5 + xp},.5 L${.5 + xp},1`, strokeWidth: 1 }, '╔': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M1,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1`, strokeWidth: 1 }, '╕': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M0,${.5 - yp} L.5,${.5 - yp} L.5,1 M0,${.5 + yp} L.5,${.5 + yp}`, strokeWidth: 1 }, '╖': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M${.5 + xp},1 L${.5 + xp},.5 L0,.5 M${.5 - xp},.5 L${.5 - xp},1`, strokeWidth: 1 }, '╗': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M0,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},1`, strokeWidth: 1 }, '╘': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M.5,0 L.5,${.5 + yp} L1,${.5 + yp} M.5,${.5 - yp} L1,${.5 - yp}`, strokeWidth: 1 }, '╙': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M1,.5 L${.5 - xp},.5 L${.5 - xp},0 M${.5 + xp},.5 L${.5 + xp},0`, strokeWidth: 1 }, '╚': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0 M1,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},0`, strokeWidth: 1 }, '╛': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M0,${.5 + yp} L.5,${.5 + yp} L.5,0 M0,${.5 - yp} L.5,${.5 - yp}`, strokeWidth: 1 }, '╜': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M0,.5 L${.5 + xp},.5 L${.5 + xp},0 M${.5 - xp},.5 L${.5 - xp},0`, strokeWidth: 1 }, '╝': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0 M0,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},0`, strokeWidth: 1 }, '╞': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `${Shapes.TOP_TO_BOTTOM} M.5,${.5 - yp} L1,${.5 - yp} M.5,${.5 + yp} L1,${.5 + yp}`, strokeWidth: 1 }, '╟': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1 M${.5 + xp},.5 L1,.5`, strokeWidth: 1 }, '╠': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M${.5 - xp},0 L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1 M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0`, strokeWidth: 1 }, '╡': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `${Shapes.TOP_TO_BOTTOM} M0,${.5 - yp} L.5,${.5 - yp} M0,${.5 + yp} L.5,${.5 + yp}`, strokeWidth: 1 }, '╢': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M0,.5 L${.5 - xp},.5 M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1`, strokeWidth: 1 }, '╣': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M${.5 + xp},0 L${.5 + xp},1 M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0`, strokeWidth: 1 }, '╤': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp} M.5,${.5 + yp} L.5,1`, strokeWidth: 1 }, '╥': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `${Shapes.LEFT_TO_RIGHT} M${.5 - xp},.5 L${.5 - xp},1 M${.5 + xp},.5 L${.5 + xp},1`, strokeWidth: 1 }, '╦': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1`, strokeWidth: 1 }, '╧': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M.5,0 L.5,${.5 - yp} M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp}`, strokeWidth: 1 }, '╨': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `${Shapes.LEFT_TO_RIGHT} M${.5 - xp},.5 L${.5 - xp},0 M${.5 + xp},.5 L${.5 + xp},0`, strokeWidth: 1 }, '╩': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M0,${.5 + yp} L1,${.5 + yp} M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0 M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0`, strokeWidth: 1 }, '╪': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `${Shapes.TOP_TO_BOTTOM} M0,${.5 - yp} L1,${.5 - yp} M0,${.5 + yp} L1,${.5 + yp}`, strokeWidth: 1 }, '╫': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `${Shapes.LEFT_TO_RIGHT} M${.5 - xp},0 L${.5 - xp},1 M${.5 + xp},0 L${.5 + xp},1`, strokeWidth: 1 }, '╬': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M0,${.5 + yp} L${.5 - xp},${.5 + yp} L${.5 - xp},1 M1,${.5 + yp} L${.5 + xp},${.5 + yp} L${.5 + xp},1 M0,${.5 - yp} L${.5 - xp},${.5 - yp} L${.5 - xp},0 M1,${.5 - yp} L${.5 + xp},${.5 - yp} L${.5 + xp},0`, strokeWidth: 1 }, // Character cell arcs (256D-2570) '╭': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M.5,1 L.5,${.5 + (yp / .15 * .5)} C.5,${.5 + (yp / .15 * .5)},.5,.5,1,.5`, strokeWidth: 1 }, '╮': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M.5,1 L.5,${.5 + (yp / .15 * .5)} C.5,${.5 + (yp / .15 * .5)},.5,.5,0,.5`, strokeWidth: 1 }, '╯': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M.5,0 L.5,${.5 - (yp / .15 * .5)} C.5,${.5 - (yp / .15 * .5)},.5,.5,0,.5`, strokeWidth: 1 }, '╰': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: (xp, yp) => `M.5,0 L.5,${.5 - (yp / .15 * .5)} C.5,${.5 - (yp / .15 * .5)},.5,.5,1,.5`, strokeWidth: 1 }, // Character cell diagonals (2571-2573) '╱': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M1,0 L0,1', strokeWidth: 1 }, '╲': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,0 L1,1', strokeWidth: 1 }, '╳': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M1,0 L0,1 M0,0 L1,1', strokeWidth: 1 }, // Light and heavy half lines (2574-257B) '╴': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 1 }, '╵': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 1 }, '╶': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 1 }, '╷': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 1 }, '╸': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 3 }, '╹': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 3 }, '╺': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 3 }, '╻': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 3 }, // Mixed light and heavy lines (257C-257F) '╼': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 3 }], '╽': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 3 }], '╾': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_RIGHT, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_LEFT, strokeWidth: 3 }], '╿': [{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_BOTTOM, strokeWidth: 1 }, { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: Shapes.MIDDLE_TO_TOP, strokeWidth: 3 }], // #endregion // #region Block elements (2580-259F) // https://www.unicode.org/charts/PDF/U2580.pdf // Block elements (2580-2590) '▀': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 4 }] }, // UPPER HALF BLOCK '▁': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 7, w: 8, h: 1 }] }, // LOWER ONE EIGHTH BLOCK '▂': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 6, w: 8, h: 2 }] }, // LOWER ONE QUARTER BLOCK '▃': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 5, w: 8, h: 3 }] }, // LOWER THREE EIGHTHS BLOCK '▄': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 4, w: 8, h: 4 }] }, // LOWER HALF BLOCK '▅': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 3, w: 8, h: 5 }] }, // LOWER FIVE EIGHTHS BLOCK '▆': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 2, w: 8, h: 6 }] }, // LOWER THREE QUARTERS BLOCK '▇': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 1, w: 8, h: 7 }] }, // LOWER SEVEN EIGHTHS BLOCK '█': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 8 }] }, // FULL BLOCK (=solid -> 25A0=black square) '▉': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 7, h: 8 }] }, // LEFT SEVEN EIGHTHS BLOCK '▊': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 6, h: 8 }] }, // LEFT THREE QUARTERS BLOCK '▋': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 5, h: 8 }] }, // LEFT FIVE EIGHTHS BLOCK '▌': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 4, h: 8 }] }, // LEFT HALF BLOCK '▍': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 3, h: 8 }] }, // LEFT THREE EIGHTHS BLOCK '▎': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 2, h: 8 }] }, // LEFT ONE QUARTER BLOCK '▏': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 1, h: 8 }] }, // LEFT ONE EIGHTH BLOCK '▐': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 0, w: 4, h: 8 }] }, // RIGHT HALF BLOCK // Shade characters (2591-2593) '░': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // LIGHT SHADE (25%) [1, 0], [0, 0] ] }, '▒': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // MEDIUM SHADE (=speckles fill, dotted fill, 50%, used in mapping to cp949, -> 1FB90 inverse medium shade) [1, 0], [0, 1] ] }, '▓': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // DARK SHADE (75%) [1, 1], [1, 0] ] }, // Block elements (2594-2595) '▔': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 1 }] }, // UPPER ONE EIGHTH BLOCK '▕': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 7, y: 0, w: 1, h: 8 }] }, // RIGHT ONE EIGHTH BLOCK // Terminal graphic characters (2596-259F) '▖': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 4, w: 4, h: 4 }] }, // QUADRANT LOWER LEFT '▗': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 4, w: 4, h: 4 }] }, // QUADRANT LOWER RIGHT '▘': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 4, h: 4 }] }, // QUADRANT UPPER LEFT '▙': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 4, h: 8 }, { x: 0, y: 4, w: 8, h: 4 }] }, // QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT (-> 1F67F reverse checker board, -> 1FB95 checker board fill) '▚': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 4, h: 4 }, { x: 4, y: 4, w: 4, h: 4 }] }, // QUADRANT UPPER LEFT AND LOWER RIGHT '▛': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 4, h: 8 }, { x: 4, y: 0, w: 4, h: 4 }] }, // QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT '▜': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 4 }, { x: 4, y: 0, w: 4, h: 8 }] }, // QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT '▝': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 0, w: 4, h: 4 }] }, // QUADRANT UPPER RIGHT '▞': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 0, w: 4, h: 4 }, { x: 0, y: 4, w: 4, h: 4 }] }, // QUADRANT UPPER RIGHT AND LOWER LEFT (-> 1F67E checker board, 1FB96 inverse checker board fill) '▟': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 0, w: 4, h: 8 }, { x: 0, y: 4, w: 8, h: 4 }] }, // QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT // #endregion // #region Powerline Symbols (E0A0-E0BF) // This contains the definitions of the primarily used box drawing characters as vector shapes. // The reason these characters are defined specially is to avoid common problems if a user's font // has not been patched with powerline characters and also to get pixel perfect rendering as // rendering issues can occur around AA/SPAA. // // The line variants draw beyond the cell and get clipped to ensure the end of the line is not // visible. // // Original symbols defined in https://github.com/powerline/fontpatcher // Git branch '\u{E0A0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.3,1 L.03,1 L.03,.88 C.03,.82,.06,.78,.11,.73 C.15,.7,.2,.68,.28,.65 L.43,.6 C.49,.58,.53,.56,.56,.53 C.59,.5,.6,.47,.6,.43 L.6,.27 L.4,.27 L.69,.1 L.98,.27 L.78,.27 L.78,.46 C.78,.52,.76,.56,.72,.61 C.68,.66,.63,.67,.56,.7 L.48,.72 C.42,.74,.38,.76,.35,.78 C.32,.8,.31,.84,.31,.88 L.31,1 M.3,.5 L.03,.59 L.03,.09 L.3,.09 L.3,.655', type: CustomGlyphVectorType.FILL } }, // LN (Line Number) '\u{E0A1}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.7,.4 L.7,.47 L.2,.47 L.2,.03 L.355,.03 L.355,.4 L.705,.4 M.7,.5 L.86,.5 L.86,.95 L.69,.95 L.44,.66 L.46,.86 L.46,.95 L.3,.95 L.3,.49 L.46,.49 L.71,.78 L.69,.565 L.69,.5', type: CustomGlyphVectorType.FILL } }, // Lock '\u{E0A2}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.25,.94 C.16,.94,.11,.92,.11,.87 L.11,.53 C.11,.48,.15,.455,.23,.45 L.23,.3 C.23,.25,.26,.22,.31,.19 C.36,.16,.43,.15,.51,.15 C.59,.15,.66,.16,.71,.19 C.77,.22,.79,.26,.79,.3 L.79,.45 C.87,.45,.91,.48,.91,.53 L.91,.87 C.91,.92,.86,.94,.77,.94 L.24,.94 M.53,.2 C.49,.2,.45,.21,.42,.23 C.39,.25,.38,.27,.38,.3 L.38,.45 L.68,.45 L.68,.3 C.68,.27,.67,.25,.64,.23 C.61,.21,.58,.2,.53,.2 M.58,.82 L.58,.66 C.63,.65,.65,.63,.65,.6 C.65,.58,.64,.57,.61,.56 C.58,.55,.56,.54,.52,.54 C.48,.54,.46,.55,.43,.56 C.4,.57,.39,.59,.39,.6 C.39,.63,.41,.64,.46,.66 L.46,.82 L.57,.82', type: CustomGlyphVectorType.FILL } }, // CN (Column Number) '\u{E0A3}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.7,.4 L.7,.47 L.2,.47 L.2,.03 L.7,.03 L.7,.1 L.355,.1 L.355,.4 L.705,.4 M.7,.5 L.86,.5 L.86,.95 L.69,.95 L.44,.66 L.46,.86 L.46,.95 L.3,.95 L.3,.49 L.46,.49 L.71,.78 L.69,.565 L.69,.5', type: CustomGlyphVectorType.FILL } }, // Right triangle solid '\u{E0B0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L1,.5 L0,1', type: CustomGlyphVectorType.FILL, rightPadding: 2 } }, // Right triangle line '\u{E0B1}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M-1,-.5 L1,.5 L-1,1.5', type: CustomGlyphVectorType.STROKE, leftPadding: 1, rightPadding: 1 } }, // Left triangle solid '\u{E0B2}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0 L0,.5 L1,1', type: CustomGlyphVectorType.FILL, leftPadding: 2 } }, // Left triangle line '\u{E0B3}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M2,-.5 L0,.5 L2,1.5', type: CustomGlyphVectorType.STROKE, leftPadding: 1, rightPadding: 1 } }, // Powerline Extra Symbols // Original symbols defined in https://github.com/ryanoasis/powerline-extra-symbols // Right semi-circle solid '\u{E0B4}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L0,1 C0.552,1,1,0.776,1,.5 C1,0.224,0.552,0,0,0', type: CustomGlyphVectorType.FILL, rightPadding: 1 } }, // Right semi-circle line '\u{E0B5}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.2,1 C.422,1,.8,.826,.78,.5 C.8,.174,0.422,0,.2,0', type: CustomGlyphVectorType.STROKE, rightPadding: 1 } }, // Left semi-circle solid '\u{E0B6}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0 L1,1 C0.448,1,0,0.776,0,.5 C0,0.224,0.448,0,1,0', type: CustomGlyphVectorType.FILL, leftPadding: 1 } }, // Left semi-circle line '\u{E0B7}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.8,1 C0.578,1,0.2,.826,.22,.5 C0.2,0.174,0.578,0,0.8,0', type: CustomGlyphVectorType.STROKE, leftPadding: 1 } }, // Lower left triangle '\u{E0B8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M-.5,-.5 L1.5,1.5 L-.5,1.5', type: CustomGlyphVectorType.FILL } }, // Backslash separator '\u{E0B9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M-.5,-.5 L1.5,1.5', type: CustomGlyphVectorType.STROKE, leftPadding: 1, rightPadding: 1 } }, // Lower right triangle '\u{E0BA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1.5,-.5 L-.5,1.5 L1.5,1.5', type: CustomGlyphVectorType.FILL } }, // Forward slash separator redundant (identical to E0BD) '\u{E0BB}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1.5,-.5 L-.5,1.5', type: CustomGlyphVectorType.STROKE, leftPadding: 1, rightPadding: 1 } }, // Upper left triangle '\u{E0BC}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1.5,-.5 L-.5,1.5 L-.5,-.5', type: CustomGlyphVectorType.FILL } }, // Forward slash separator '\u{E0BD}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1.5,-.5 L-.5,1.5', type: CustomGlyphVectorType.STROKE, leftPadding: 1, rightPadding: 1 } }, // Upper right triangle '\u{E0BE}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M-.5,-.5 L1.5,1.5 L1.5,-.5', type: CustomGlyphVectorType.FILL } }, // Backslash separator redundant (identical to E0B9) '\u{E0BF}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M-.5,-.5 L1.5,1.5', type: CustomGlyphVectorType.STROKE, leftPadding: 1, rightPadding: 1 } }, '\u{E0C0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.8069,0.6075 Q0.7991,0.612,0.7815,0.6218 T0.7542,0.6369 T0.7282,0.6508 T0.7016,0.6644 T0.6778,0.6758 T0.6551,0.6854 T0.637,0.6911 T0.622,0.6936 Q0.6186,0.6936,0.6128,0.6942 T0.6018,0.6952 T0.5894,0.6956 T0.5757,0.6952 T0.561,0.693 T0.5451,0.6879 Q0.5342,0.6834,0.5193,0.6756 T0.4925,0.6614 T0.4673,0.6485 T0.4436,0.6389 T0.4239,0.6363 T0.4089,0.6422 Q0.4045,0.6467,0.3958,0.6554 T0.3843,0.6669 T0.3748,0.6746 T0.3613,0.683 T0.3442,0.6903 L0.3499,0.6956 Q0.3584,0.7042,0.3679,0.7177 T0.3836,0.7403 T0.4019,0.7583 T0.4295,0.7703 Q0.4688,0.7785,0.5203,0.7605 Q0.4902,0.7817,0.449,0.7911 T0.3696,0.8001 Q0.3567,0.7997,0.341,0.7942 T0.3111,0.7813 T0.2805,0.766 T0.2493,0.753 T0.218,0.7472 T0.187,0.754 Q0.1762,0.7589,0.1653,0.7681 T0.1453,0.7895 T0.1308,0.818 T0.1264,0.8507 L0.1264,0.858 Q0.1453,0.8462,0.1607,0.8433 T0.1899,0.8452 T0.2226,0.8625 Q0.2344,0.8707,0.2485,0.8752 T0.2813,0.8831 T0.3089,0.889 Q0.2981,0.8915,0.2846,0.8956 T0.2603,0.9037 T0.2368,0.9115 T0.2129,0.9174 T0.1894,0.9194 T0.1653,0.9155 Q0.1504,0.9115,0.1328,0.9196 T0.0986,0.9421 T0.0655,0.9708 T0.0317,0.9943 T0,1 Q0.001,0.94,0.0017,0.6508 T0.0024,0.2322 Q0.002,0.2109,0.0007,0.1379 T0,0.0249 Q0.0058,0.0253,0.009,0.0257 T0.0156,0.0288 T0.0203,0.0333 T0.0264,0.0422 T0.0349,0.0539 Q0.044,0.0657,0.0542,0.072 T0.0752,0.0789 T0.096,0.0775 T0.1175,0.0694 T0.1379,0.0573 T0.1579,0.0432 Q0.2141,0,0.2673,0.0318 Q0.2588,0.0318,0.2517,0.033 T0.239,0.0361 T0.2276,0.0424 T0.218,0.0504 T0.2085,0.0612 T0.1997,0.0732 T0.1899,0.0875 T0.1795,0.1028 Q0.1748,0.1093,0.1507,0.1234 T0.1186,0.1448 Q0.0999,0.1612,0.1108,0.1783 Q0.1162,0.1877,0.1238,0.1928 T0.1404,0.1979 T0.158,0.1967 T0.1775,0.1895 T0.1963,0.1791 T0.2151,0.1663 T0.2314,0.1534 T0.2458,0.141 T0.2561,0.1322 Q0.2815,0.1106,0.3017,0.101 T0.344,0.0926 T0.3919,0.1077 Q0.4099,0.1175,0.4265,0.1206 T0.4588,0.1202 T0.488,0.1098 T0.5176,0.0914 Q0.5071,0.1032,0.4963,0.1122 T0.4722,0.1285 T0.4497,0.1401 T0.4223,0.1518 T0.3943,0.1636 Q0.374,0.1726,0.3623,0.1863 T0.3482,0.2162 Q0.3445,0.2403,0.3608,0.2605 T0.4072,0.2876 Q0.4194,0.2909,0.4326,0.2899 T0.457,0.2856 T0.4805,0.2764 T0.5032,0.264 T0.5256,0.2497 T0.5478,0.2352 T0.5698,0.2222 T0.5921,0.2118 Q0.6982,0.173,0.81,0.2436 Q0.833,0.2583,0.8496,0.2674 T0.887,0.2846 T0.925,0.294 T0.9617,0.2911 T1,0.2746 Q0.9817,0.2946,0.96,0.3056 T0.9175,0.3191 T0.8731,0.3209 T0.8277,0.3164 T0.7818,0.3109 T0.7359,0.3101 T0.6907,0.3197 T0.647,0.3448 Q0.6443,0.3472,0.6333,0.3562 T0.6194,0.3674 T0.6076,0.3766 T0.594,0.386 T0.5815,0.3931 T0.5661,0.4007 T0.5501,0.4064 L0.5556,0.4121 Q0.5627,0.4198,0.5722,0.4251 T0.5926,0.4337 T0.6131,0.439 T0.6343,0.4431 T0.6521,0.4468 Q0.6762,0.4529,0.7056,0.4463 Q0.6772,0.4623,0.6543,0.471 T0.604,0.4814 Q0.5962,0.4818,0.5815,0.4825 T0.5591,0.4835 T0.5393,0.4857 T0.519,0.4908 T0.5007,0.4996 T0.4812,0.5141 T0.4627,0.5353 L0.4549,0.5451 L0.4661,0.543 Q0.4759,0.541,0.4909,0.55 T0.5315,0.5796 T0.5684,0.6083 Q0.5799,0.6161,0.5915,0.6212 T0.6181,0.6308 L0.6331,0.6353 Q0.6514,0.6418,0.7004,0.633 T0.8069,0.6075 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0C1}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.011,0.0469 Q0.0152,0.0412,0.02,0.0361 T0.0292,0.0281 T0.0365,0.0253 Q0.0426,0.0257,0.0461,0.0263 T0.0534,0.03 T0.0582,0.0345 T0.0645,0.0434 T0.0723,0.0546 Q0.0807,0.0653,0.0897,0.0712 T0.1081,0.0775 T0.1255,0.0765 T0.1438,0.0695 T0.1605,0.0593 T0.1772,0.0473 Q0.2388,0,0.2927,0.0326 L0.2614,0.0767 L0.2581,0.0767 Q0.2494,0.0767,0.2444,0.0773 T0.2359,0.0789 T0.2291,0.0842 T0.2233,0.0922 T0.2146,0.1054 T0.2026,0.1236 Q0.1943,0.1354,0.1843,0.1444 T0.1675,0.1574 T0.1526,0.1654 T0.1429,0.1701 Q0.1436,0.1717,0.1446,0.1733 Q0.1504,0.1823,0.1581,0.1866 T0.1733,0.1915 T0.1909,0.1884 T0.2085,0.1809 T0.2265,0.169 T0.2427,0.1564 T0.2575,0.1436 T0.2688,0.1338 Q0.2969,0.1101,0.3195,0.0995 T0.3643,0.0901 T0.4114,0.1048 Q0.4279,0.1138,0.4426,0.1168 T0.4705,0.117 T0.4945,0.1089 T0.5186,0.0942 L0.5211,0.0926 L0.5289,0.0922 Q0.5411,0.095,0.5189,0.1203 Q0.505,0.1358,0.4913,0.1474 T0.4634,0.167 T0.4389,0.1796 T0.4121,0.1909 T0.3866,0.2011 Q0.3782,0.2047,0.3727,0.2092 Q0.3717,0.2117,0.3714,0.2141 Q0.3695,0.2263,0.3751,0.2388 T0.394,0.2616 T0.4253,0.2761 Q0.4398,0.2785,0.4513,0.2779 T0.4756,0.2724 T0.4987,0.2616 T0.5216,0.2476 T0.5453,0.2321 T0.5703,0.217 T0.5976,0.2043 Q0.7015,0.166,0.8099,0.2345 Q0.8361,0.2512,0.8572,0.2616 T0.9006,0.2779 T0.9432,0.281 T0.9819,0.2663 L0.9887,0.2671 Q1,0.272,0.9768,0.2969 Q0.9558,0.3193,0.9319,0.333 T0.8858,0.3513 T0.8391,0.3569 T0.793,0.3546 T0.7485,0.3497 T0.7065,0.3473 T0.6681,0.3522 T0.6344,0.3699 Q0.5963,0.4005,0.5847,0.4082 Q0.595,0.4139,0.6079,0.4174 T0.6367,0.4237 T0.6586,0.4278 Q0.6793,0.4331,0.7044,0.4278 Q0.7057,0.4278,0.707,0.4274 Q0.7067,0.429,0.7062,0.4313 T0.7036,0.4398 T0.6993,0.4513 T0.6931,0.4619 T0.6851,0.4698 Q0.6573,0.4857,0.6336,0.4947 T0.5831,0.5053 Q0.577,0.5057,0.5615,0.5065 T0.5394,0.508 T0.5221,0.51 T0.5042,0.5139 T0.4905,0.5204 Q0.5008,0.5232,0.515,0.5328 T0.549,0.5585 T0.5799,0.5824 Q0.5905,0.5897,0.6013,0.5946 T0.6262,0.6036 T0.6409,0.6081 Q0.6567,0.6134,0.7035,0.605 T0.8028,0.5816 L0.7974,0.5922 T0.7867,0.6134 L0.7812,0.624 Q0.7799,0.6248,0.7786,0.6252 Q0.7709,0.6297,0.7573,0.6372 T0.7346,0.6499 T0.7123,0.6621 T0.6897,0.6741 T0.6686,0.6847 T0.648,0.6941 T0.6297,0.7013 T0.6129,0.7062 T0.5995,0.7076 Q0.5992,0.7076,0.5831,0.7092 T0.5529,0.7092 T0.525,0.7019 Q0.5105,0.6958,0.4824,0.6811 T0.4356,0.6586 T0.4079,0.6533 Q0.3872,0.6741,0.3827,0.6782 Q0.3872,0.6835,0.3934,0.6929 T0.4037,0.7078 T0.4142,0.7198 T0.4284,0.7304 T0.4466,0.7365 Q0.4824,0.7443,0.5292,0.728 L0.5292,0.7294 T0.529,0.7333 T0.5284,0.7392 T0.5266,0.7463 T0.5236,0.7539 T0.5186,0.7612 T0.5111,0.7679 Q0.5086,0.77,0.5056,0.7716 Q0.4466,0.8104,0.3591,0.8091 Q0.344,0.8091,0.3246,0.802 T0.2893,0.7863 T0.2543,0.77 T0.2206,0.76 T0.1894,0.7643 Q0.1804,0.7684,0.1733,0.7753 Q0.1591,0.7892,0.1601,0.8169 Q0.1784,0.8067,0.1938,0.8053 T0.2212,0.8087 T0.2507,0.825 Q0.2614,0.8324,0.2744,0.8365 T0.3053,0.844 T0.332,0.8499 L0.303,0.894 L0.2998,0.8948 Q0.2904,0.8968,0.278,0.9009 T0.2554,0.9086 T0.233,0.9158 T0.2099,0.9213 T0.1868,0.9229 T0.1633,0.9192 Q0.1517,0.916,0.1367,0.9241 T0.1065,0.9462 T0.0753,0.9737 T0.041,0.9955 T0.0061,1 Q0,0.9984,0.0068,0.9845 Q0.009,0.9804,0.0123,0.9755 Q0.0187,0.9666,0.0261,0.9606 T0.0374,0.9555 Q0.0481,0.958,0.0629,0.949 T0.0931,0.9262 T0.1247,0.8987 T0.1597,0.8777 T0.1949,0.8752 Q0.213,0.8805,0.2343,0.8772 Q0.2249,0.8736,0.2172,0.8683 Q0.1914,0.8511,0.1738,0.8497 T0.1349,0.8617 Q0.1304,0.865,0.1273,0.8646 T0.1242,0.8605 L0.1239,0.8536 Q0.1226,0.8295,0.1354,0.8016 T0.1723,0.7496 Q0.1914,0.7308,0.2104,0.7219 Q0.2259,0.7145,0.2425,0.7141 T0.273,0.7182 T0.3022,0.7296 T0.3301,0.7439 T0.3569,0.7571 T0.3821,0.7643 Q0.373,0.7561,0.3598,0.7367 T0.3382,0.7088 L0.3327,0.7035 Q0.3282,0.699,0.3353,0.6884 T0.3517,0.6692 L0.3611,0.6607 Q0.3711,0.657,0.3751,0.6554 T0.3837,0.6507 T0.3898,0.6458 T0.3979,0.637 T0.4111,0.6236 Q0.4172,0.6175,0.4234,0.6138 Q0.4327,0.6081,0.4439,0.6077 T0.4655,0.6105 T0.4884,0.6201 T0.5113,0.633 T0.5347,0.6468 T0.5573,0.6578 Q0.5705,0.6635,0.5833,0.665 T0.6112,0.665 T0.6286,0.6635 Q0.6421,0.6631,0.6718,0.6501 Q0.626,0.6578,0.6089,0.6517 Q0.6063,0.6509,0.6002,0.6491 T0.5884,0.6454 T0.5757,0.6409 T0.561,0.6344 T0.5466,0.6256 Q0.536,0.6183,0.5108,0.5989 T0.4722,0.5712 T0.4527,0.564 L0.4418,0.5661 Q0.4308,0.5681,0.4366,0.5563 Q0.4401,0.5485,0.4476,0.5392 L0.455,0.5298 Q0.4631,0.5188,0.4714,0.51 T0.4866,0.4947 T0.5021,0.4831 T0.516,0.4749 T0.53,0.4694 T0.5428,0.4657 T0.5558,0.4637 T0.5674,0.4629 T0.5795,0.4625 Q0.5489,0.4543,0.534,0.4384 L0.5286,0.4327 Q0.5244,0.4282,0.5318,0.4174 T0.5486,0.398 L0.5579,0.3899 Q0.567,0.387,0.5749,0.3834 T0.5878,0.3772 T0.6012,0.3687 T0.6126,0.3603 T0.6268,0.3487 T0.6421,0.3361 Q0.6567,0.3242,0.6712,0.3159 T0.7009,0.3034 T0.7277,0.2973 T0.7556,0.2961 T0.7809,0.2977 T0.8074,0.301 Q0.8096,0.3014,0.8109,0.3016 T0.8143,0.302 T0.818,0.3026 Q0.8012,0.2936,0.7767,0.2781 Q0.6718,0.2117,0.5744,0.2476 Q0.5637,0.2516,0.5492,0.2604 T0.5218,0.2779 T0.4923,0.2965 T0.461,0.3124 T0.4282,0.3216 T0.394,0.3206 Q0.3637,0.3136,0.3474,0.2936 T0.3353,0.2459 Q0.3385,0.2247,0.355,0.2023 Q0.3646,0.1892,0.3763,0.179 Q0.3898,0.1672,0.4063,0.1595 Q0.3927,0.1562,0.3785,0.1485 Q0.3604,0.1387,0.3453,0.1354 T0.3161,0.1354 T0.2899,0.1458 T0.2627,0.166 Q0.262,0.1664,0.254,0.1733 T0.2414,0.1841 T0.2267,0.196 T0.2094,0.2086 T0.1915,0.2196 T0.1725,0.2288 T0.1546,0.2337 T0.1373,0.2343 T0.1223,0.228 T0.1097,0.2145 Q0.0962,0.1929,0.1258,0.1582 Q0.131,0.1517,0.1371,0.1464 Q0.1462,0.1387,0.156,0.1321 T0.1752,0.1205 T0.1878,0.1134 Q0.1891,0.1117,0.1947,0.103 T0.2035,0.0899 T0.2125,0.0781 T0.2243,0.064 Q0.1949,0.0616,0.1672,0.0828 Q0.153,0.0934,0.1412,0.1009 T0.1146,0.1146 T0.0873,0.1213 T0.0616,0.116 T0.0374,0.0962 Q0.0339,0.0909,0.0295,0.0846 T0.024,0.0767 T0.0202,0.0728 T0.0148,0.0704 T0.0068,0.0697 Q0.0003,0.0693,0.0058,0.0567 Q0.0077,0.0522,0.011,0.0469 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0C2}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.1934,0.6075 Q0.2012,0.612,0.2188,0.6218 T0.2459,0.6369 T0.2718,0.6508 T0.2983,0.6644 T0.322,0.6758 T0.3447,0.6854 T0.363,0.6911 T0.3783,0.6936 Q0.3796,0.6936,0.396,0.6952 T0.4265,0.6952 T0.4548,0.6879 Q0.4656,0.6834,0.4805,0.6756 T0.5073,0.6614 T0.5327,0.6485 T0.5564,0.6389 T0.5759,0.6363 T0.5909,0.6422 Q0.5953,0.6467,0.604,0.6554 T0.6155,0.6669 T0.6251,0.6746 T0.6387,0.683 T0.6556,0.6903 L0.6498,0.6956 Q0.6414,0.7042,0.6319,0.7177 T0.6162,0.7403 T0.5979,0.7583 T0.5703,0.7703 Q0.5313,0.7785,0.4799,0.7605 Q0.5097,0.7817,0.5508,0.7911 T0.6302,0.8001 Q0.6431,0.7997,0.659,0.7942 T0.6888,0.7813 T0.7194,0.766 T0.7506,0.753 T0.7819,0.7472 T0.8131,0.754 Q0.8239,0.7589,0.8346,0.7681 T0.8544,0.7895 T0.8689,0.818 T0.8737,0.8507 L0.8733,0.858 Q0.8591,0.849,0.8474,0.845 T0.8242,0.8421 T0.8021,0.8482 T0.7772,0.8625 Q0.7653,0.8707,0.7513,0.8752 T0.7184,0.8831 T0.6908,0.889 Q0.7017,0.8915,0.7152,0.8956 T0.7396,0.9037 T0.7633,0.9115 T0.7872,0.9174 T0.8107,0.9194 T0.8347,0.9155 Q0.8493,0.9115,0.8671,0.9196 T0.9015,0.9421 T0.9345,0.9708 T0.9682,0.9943 T0.9997,1 Q0.999,0.94,0.9983,0.6508 T0.9976,0.2322 Q0.9976,0.2109,0.9992,0.1379 T1,0.0249 Q0.9949,0.0249,0.9917,0.0257 T0.9863,0.0273 T0.9817,0.0308 T0.9776,0.0357 T0.9721,0.0437 T0.9651,0.0539 Q0.956,0.0657,0.9456,0.072 T0.9247,0.0789 T0.904,0.0775 T0.8823,0.0694 T0.862,0.0573 T0.8422,0.0432 Q0.7856,0,0.7328,0.0318 Q0.7392,0.0318,0.7452,0.0324 T0.756,0.0345 T0.7657,0.0386 T0.7741,0.0437 T0.7821,0.0506 T0.7894,0.0588 T0.7966,0.0683 T0.8039,0.0787 T0.8119,0.0904 T0.8202,0.1028 Q0.8249,0.1093,0.849,0.1234 T0.8811,0.1448 Q0.8998,0.1612,0.8893,0.1783 Q0.8835,0.1877,0.8759,0.1928 T0.8593,0.1979 T0.8417,0.1967 T0.8222,0.1895 T0.8034,0.1791 T0.7848,0.1663 T0.7687,0.1534 T0.7543,0.141 T0.744,0.1322 Q0.7186,0.1106,0.6984,0.101 T0.6559,0.0926 T0.6082,0.1077 Q0.5899,0.1175,0.5733,0.1206 T0.5411,0.1202 T0.5122,0.1098 T0.4822,0.0914 Q0.4927,0.1032,0.5036,0.1122 T0.5276,0.1285 T0.5503,0.1401 T0.5777,0.1518 T0.6055,0.1636 Q0.6258,0.1726,0.6375,0.1863 T0.6519,0.2162 Q0.6556,0.2403,0.6392,0.2605 T0.5926,0.2876 Q0.5804,0.2909,0.5672,0.2899 T0.5428,0.2856 T0.5193,0.2764 T0.4966,0.264 T0.4744,0.2497 T0.4523,0.2352 T0.4301,0.2222 T0.4077,0.2118 Q0.3021,0.173,0.1903,0.2436 Q0.1669,0.2583,0.1504,0.2674 T0.1129,0.2846 T0.075,0.294 T0.0384,0.2911 T0,0.2746 Q0.021,0.297,0.0459,0.3082 T0.0953,0.3207 T0.146,0.3193 T0.1988,0.3131 T0.2508,0.3095 T0.3029,0.3176 T0.3529,0.3448 Q0.3566,0.348,0.3652,0.355 T0.3771,0.3645 T0.3877,0.3731 T0.3993,0.3815 T0.4103,0.3884 T0.4226,0.3953 T0.4352,0.4009 T0.4497,0.4064 L0.4443,0.4121 Q0.4372,0.4198,0.4277,0.4251 T0.4072,0.4337 T0.3867,0.439 T0.3656,0.4431 T0.3481,0.4468 Q0.3241,0.4529,0.2943,0.4463 Q0.3227,0.4623,0.3458,0.471 T0.3959,0.4814 Q0.404,0.4818,0.4187,0.4825 T0.4409,0.4835 T0.4605,0.4857 T0.4809,0.4908 T0.4992,0.4996 T0.5186,0.5141 T0.5371,0.5353 L0.5449,0.5451 L0.5337,0.543 Q0.5242,0.541,0.5091,0.55 T0.4683,0.5796 T0.4314,0.6083 Q0.4203,0.6161,0.4086,0.6212 T0.382,0.6308 T0.3667,0.6353 Q0.3485,0.6418,0.2995,0.633 T0.1934,0.6075 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0C3}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.989,0.0469 Q0.9829,0.0379,0.9753,0.0316 T0.9635,0.0253 Q0.9574,0.0257,0.9539,0.0263 T0.9466,0.03 T0.9418,0.0345 T0.9355,0.0434 T0.9277,0.0546 Q0.9193,0.0653,0.9103,0.0712 T0.8919,0.0775 T0.8745,0.0765 T0.8564,0.0695 T0.8396,0.0593 T0.8228,0.0473 Q0.7612,0,0.7073,0.0326 L0.7386,0.0767 L0.7419,0.0767 Q0.7506,0.0767,0.7556,0.0773 T0.7641,0.0789 T0.7709,0.0842 T0.7767,0.0922 T0.7854,0.1054 T0.7974,0.1236 Q0.8057,0.1354,0.8157,0.1444 T0.8325,0.1574 T0.8474,0.1654 T0.8571,0.1701 Q0.8564,0.1717,0.8554,0.1733 Q0.8503,0.1815,0.844,0.186 T0.8301,0.1909 T0.8153,0.1903 T0.7993,0.1845 T0.7836,0.1758 T0.768,0.1648 T0.754,0.1535 T0.741,0.1425 T0.7312,0.1338 Q0.7031,0.1101,0.6805,0.0995 T0.6357,0.0901 T0.5886,0.1048 Q0.5721,0.1138,0.5574,0.1168 T0.5295,0.117 T0.5055,0.1089 T0.4814,0.0942 L0.4789,0.0926 L0.4711,0.0922 Q0.4589,0.095,0.4811,0.1203 Q0.495,0.1358,0.5087,0.1474 T0.5366,0.167 T0.5611,0.1796 T0.5879,0.1909 T0.6134,0.2011 Q0.6218,0.2047,0.6273,0.2092 Q0.6283,0.2117,0.6286,0.2141 Q0.6305,0.2263,0.6249,0.2388 T0.606,0.2616 T0.5747,0.2761 Q0.5602,0.2785,0.5487,0.2779 T0.5245,0.2724 T0.5015,0.2616 T0.4784,0.2476 T0.4547,0.2321 T0.4297,0.217 T0.4024,0.2043 Q0.2985,0.166,0.1901,0.2345 Q0.1694,0.248,0.1523,0.2569 T0.1165,0.2726 T0.0818,0.2812 T0.0494,0.2796 T0.0181,0.2663 L0.0116,0.2671 Q0,0.272,0.0232,0.2969 Q0.0468,0.3222,0.0742,0.3365 T0.1278,0.3542 T0.1807,0.3564 T0.233,0.352 T0.2815,0.3473 T0.3269,0.3509 T0.3656,0.3699 Q0.4037,0.4005,0.4153,0.4082 Q0.405,0.4139,0.3921,0.4174 T0.3633,0.4237 T0.3414,0.4278 Q0.3207,0.4331,0.2956,0.4278 Q0.2943,0.4278,0.293,0.4274 Q0.2933,0.429,0.2938,0.4313 T0.2964,0.4398 T0.3007,0.4513 T0.3069,0.4619 T0.3149,0.4698 Q0.3427,0.4857,0.3664,0.4947 T0.4169,0.5053 Q0.423,0.5057,0.4385,0.5065 T0.4606,0.508 T0.4779,0.51 T0.4958,0.5139 T0.5095,0.5204 Q0.4992,0.5232,0.485,0.5328 T0.451,0.5585 T0.4201,0.5824 Q0.4095,0.5897,0.3987,0.5946 T0.3738,0.6036 T0.3591,0.6081 Q0.3433,0.6134,0.2965,0.605 T0.1972,0.5816 L0.2026,0.5922 T0.2133,0.6134 L0.2188,0.624 Q0.2201,0.6248,0.2214,0.6252 Q0.2291,0.6297,0.2486,0.6405 T0.2786,0.657 T0.3066,0.6721 T0.3353,0.6866 T0.3598,0.6972 T0.383,0.7051 T0.4005,0.7076 Q0.4008,0.7076,0.4169,0.7092 T0.4471,0.7092 T0.475,0.7019 Q0.4895,0.6958,0.5176,0.6811 T0.5644,0.6586 T0.5921,0.6533 Q0.6128,0.6741,0.6173,0.6782 Q0.6131,0.6835,0.6068,0.6929 T0.5963,0.7078 T0.5858,0.7198 T0.5716,0.7304 T0.5534,0.7365 Q0.5179,0.7443,0.4708,0.728 L0.4708,0.7294 T0.471,0.7333 T0.4716,0.7392 T0.4734,0.7463 T0.4764,0.7539 T0.4814,0.7612 T0.4889,0.7679 Q0.4914,0.77,0.4944,0.7716 Q0.5534,0.8104,0.6409,0.8091 Q0.6521,0.8091,0.6654,0.8053 T0.6907,0.7959 T0.7157,0.7838 T0.741,0.772 T0.7652,0.7631 T0.7888,0.7594 T0.8106,0.7643 Q0.8196,0.7684,0.8267,0.7753 Q0.8409,0.7892,0.8399,0.8169 Q0.8216,0.8067,0.8062,0.8053 T0.7788,0.8087 T0.7493,0.825 Q0.7386,0.8324,0.7256,0.8365 T0.6947,0.844 T0.668,0.8499 L0.697,0.894 L0.7002,0.8948 Q0.7099,0.8968,0.7222,0.9009 T0.7446,0.9086 T0.767,0.9158 T0.7901,0.9213 T0.8132,0.9229 T0.8367,0.9192 Q0.8483,0.916,0.8633,0.9241 T0.8937,0.9462 T0.9248,0.9737 T0.959,0.9955 T0.9939,1 Q1,0.9984,0.9932,0.9845 Q0.991,0.9804,0.9877,0.9755 Q0.9813,0.9666,0.9739,0.9606 T0.9626,0.9555 Q0.9519,0.958,0.9372,0.949 T0.9071,0.9262 T0.8753,0.8987 T0.8403,0.8777 T0.8051,0.8752 Q0.787,0.8805,0.7657,0.8772 Q0.7751,0.8736,0.7828,0.8683 Q0.8086,0.8511,0.8262,0.8497 T0.8651,0.8617 Q0.8696,0.865,0.8727,0.8646 T0.8758,0.8605 L0.8761,0.8536 Q0.8774,0.8295,0.8646,0.8016 T0.8277,0.7496 Q0.8086,0.7308,0.7896,0.7219 Q0.7741,0.7145,0.7575,0.7141 T0.727,0.7182 T0.6978,0.7296 T0.6699,0.7439 T0.6433,0.7571 T0.6179,0.7643 Q0.627,0.7561,0.6402,0.7367 T0.6618,0.7088 L0.6673,0.7035 Q0.6718,0.699,0.6647,0.6884 T0.6483,0.6692 L0.6389,0.6607 Q0.6289,0.657,0.6249,0.6554 T0.6163,0.6507 T0.6102,0.6458 T0.6021,0.637 T0.5889,0.6236 Q0.5828,0.6175,0.5766,0.6138 Q0.5673,0.6081,0.5561,0.6077 T0.5345,0.6105 T0.5116,0.6201 T0.4887,0.633 T0.4653,0.6468 T0.4427,0.6578 Q0.4295,0.6635,0.4167,0.665 T0.3888,0.665 T0.3714,0.6635 Q0.3579,0.6631,0.3282,0.6501 Q0.374,0.6578,0.3911,0.6517 Q0.3937,0.6509,0.4024,0.6482 T0.4179,0.6434 T0.4348,0.6364 T0.4534,0.6256 Q0.464,0.6183,0.4892,0.5989 T0.5278,0.5712 T0.5476,0.564 L0.5582,0.5661 Q0.5692,0.5681,0.5634,0.5563 Q0.5599,0.5485,0.5524,0.5392 L0.545,0.5298 Q0.5369,0.5188,0.5286,0.51 T0.5134,0.4947 T0.4979,0.4831 T0.484,0.4749 T0.47,0.4694 T0.4572,0.4657 T0.4442,0.4637 T0.4326,0.4629 T0.4205,0.4625 Q0.4511,0.4543,0.466,0.4384 L0.4714,0.4327 Q0.4795,0.4237,0.4469,0.394 L0.4421,0.3899 Q0.433,0.387,0.4251,0.3834 T0.4122,0.3772 T0.3988,0.3687 T0.3874,0.3603 T0.3732,0.3487 T0.3579,0.3361 Q0.3433,0.3242,0.3288,0.3159 T0.2993,0.3034 T0.2725,0.2973 T0.2446,0.2961 T0.2193,0.2977 T0.1926,0.301 Q0.1855,0.3022,0.182,0.3026 Q0.1991,0.2936,0.2233,0.2781 Q0.3282,0.2117,0.4256,0.2476 Q0.4363,0.2516,0.451,0.2604 T0.4784,0.2779 T0.5077,0.2965 T0.539,0.3124 T0.5718,0.3216 T0.606,0.3206 Q0.6363,0.3136,0.6526,0.2936 T0.6647,0.2459 Q0.6615,0.2247,0.645,0.2023 Q0.6354,0.1892,0.6237,0.179 Q0.6102,0.1672,0.5937,0.1595 Q0.6073,0.1562,0.6215,0.1485 Q0.6396,0.1387,0.6547,0.1354 T0.6839,0.1354 T0.7101,0.1458 T0.7373,0.166 Q0.738,0.1664,0.746,0.1733 T0.7586,0.1841 T0.7733,0.196 T0.7907,0.2086 T0.8086,0.2196 T0.8275,0.2288 T0.8454,0.2337 T0.8627,0.2343 T0.8777,0.228 T0.8903,0.2145 Q0.9038,0.1929,0.8742,0.1582 Q0.869,0.1517,0.8629,0.1464 Q0.8538,0.1387,0.844,0.1321 T0.8248,0.1205 T0.8122,0.1134 Q0.8109,0.1117,0.8053,0.103 T0.7965,0.0899 T0.7875,0.0781 T0.7757,0.064 Q0.8051,0.0616,0.8328,0.0828 Q0.847,0.0934,0.8588,0.1009 T0.8854,0.1146 T0.9127,0.1213 T0.9384,0.116 T0.9626,0.0962 Q0.9664,0.0909,0.9706,0.0846 T0.976,0.0767 T0.9798,0.0728 T0.9852,0.0704 T0.9932,0.0697 Q0.9997,0.0693,0.9942,0.0567 Q0.9923,0.0522,0.989,0.0469 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0C4}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.4116,0.0126 L0.4116,0.0776 L0.4871,0.0776 L0.4871,0.0126 L0.4116,0.0126 Z M0.4043,0 L0.4944,0 Q0.4973,0,0.4995,0.0021 T0.5017,0.0063 L0.5017,0.0839 Q0.5017,0.0864,0.4993,0.0883 T0.4944,0.0902 L0.4043,0.0902 Q0.4014,0.0902,0.3992,0.0881 T0.397,0.0839 L0.397,0.0063 Q0.397,0.0038,0.3994,0.0019 T0.4043,0 Z M0.4116,0.0126 L0.4116,0.0776 L0.4871,0.0776 L0.4871,0.0126 L0.4116,0.0126 Z M0.4043,0 L0.4944,0 Q0.4973,0,0.4995,0.0021 T0.5017,0.0063 L0.5017,0.0839 Q0.5017,0.0864,0.4993,0.0883 T0.4944,0.0902 L0.4043,0.0902 Q0.4014,0.0902,0.3992,0.0881 T0.397,0.0839 L0.397,0.0063 Q0.397,0.0038,0.3994,0.0019 T0.4043,0 Z M0.4116,0.0126 L0.4116,0.0776 L0.4871,0.0776 L0.4871,0.0126 L0.4116,0.0126 Z M0.4043,0 L0.4944,0 Q0.4973,0,0.4995,0.0021 T0.5017,0.0063 L0.5017,0.0839 Q0.5017,0.0864,0.4993,0.0883 T0.4944,0.0902 L0.4043,0.0902 Q0.4014,0.0902,0.3992,0.0881 T0.397,0.0839 L0.397,0.0063 Q0.397,0.0038,0.3994,0.0019 T0.4043,0 Z M0.9099,0.0084 L0.9099,0.0818 L0.9951,0.0818 L0.9951,0.0084 L0.9099,0.0084 Z M0.9075,0.0042 L0.9976,0.0042 Q1,0.0042,1,0.0063 L1,0.0839 Q1,0.086,0.9976,0.086 L0.9075,0.086 Q0.905,0.086,0.905,0.0839 L0.905,0.0063 Q0.905,0.0042,0.9075,0.0042 Z M0.9099,0.0084 L0.9099,0.0818 L0.9951,0.0818 L0.9951,0.0084 L0.9099,0.0084 Z M0.9075,0.0042 L0.9976,0.0042 Q1,0.0042,1,0.0063 L1,0.0839 Q1,0.086,0.9976,0.086 L0.9075,0.086 Q0.905,0.086,0.905,0.0839 L0.905,0.0063 Q0.905,0.0042,0.9075,0.0042 Z M0.9099,0.0084 L0.9099,0.0818 L0.9951,0.0818 L0.9951,0.0084 L0.9099,0.0084 Z M0.9075,0.0042 L0.9976,0.0042 Q1,0.0042,1,0.0063 L1,0.0839 Q1,0.086,0.9976,0.086 L0.9075,0.086 Q0.905,0.086,0.905,0.0839 L0.905,0.0063 Q0.905,0.0042,0.9075,0.0042 Z M0.66,0.0063 L0.7501,0.0063 L0.7501,0.0839 L0.66,0.0839 L0.66,0.0063 Z M0.66,0.0063 L0.7501,0.0063 L0.7501,0.0839 L0.66,0.0839 L0.66,0.0063 Z M0.66,0.0063 L0.7501,0.0063 L0.7501,0.0839 L0.66,0.0839 L0.66,0.0063 Z M0.5309,0.0063 L0.621,0.0063 L0.621,0.0839 L0.5309,0.0839 L0.5309,0.0063 Z M0.5309,0.0063 L0.621,0.0063 L0.621,0.0839 L0.5309,0.0839 L0.5309,0.0063 Z M0.5309,0.0063 L0.621,0.0063 L0.621,0.0839 L0.5309,0.0839 L0.5309,0.0063 Z M0.2689,0.0063 L0.359,0.0063 L0.359,0.0839 L0.2689,0.0839 L0.2689,0.0063 Z M0.2689,0.0063 L0.359,0.0063 L0.359,0.0839 L0.2689,0.0839 L0.2689,0.0063 Z M0.2689,0.0063 L0.359,0.0063 L0.359,0.0839 L0.2689,0.0839 L0.2689,0.0063 Z M0.1374,0.0063 L0.2275,0.0063 L0.2275,0.0839 L0.1374,0.0839 L0.1374,0.0063 Z M0.1374,0.0063 L0.2275,0.0063 L0.2275,0.0839 L0.1374,0.0839 L0.1374,0.0063 Z M0.1374,0.0063 L0.2275,0.0063 L0.2275,0.0839 L0.1374,0.0839 L0.1374,0.0063 Z M0,0.0063 L0.0901,0.0063 L0.0901,0.0839 L0,0.0839 L0,0.0063 Z M0,0.0063 L0.0901,0.0063 L0.0901,0.0839 L0,0.0839 L0,0.0063 Z M0,0.0063 L0.0901,0.0063 L0.0901,0.0839 L0,0.0839 L0,0.0063 Z M0.4087,0.9224 L0.4087,0.9958 L0.4939,0.9958 L0.4939,0.9224 L0.4087,0.9224 Z M0.4062,0.9182 L0.4963,0.9182 Q0.4988,0.9182,0.4988,0.9203 L0.4988,0.9979 Q0.4988,1,0.4963,1 L0.4062,1 Q0.4038,1,0.4038,0.9979 L0.4038,0.9203 Q0.4038,0.9182,0.4062,0.9182 Z M0.4087,0.9224 L0.4087,0.9958 L0.4939,0.9958 L0.4939,0.9224 L0.4087,0.9224 Z M0.4062,0.9182 L0.4963,0.9182 Q0.4988,0.9182,0.4988,0.9203 L0.4988,0.9979 Q0.4988,1,0.4963,1 L0.4062,1 Q0.4038,1,0.4038,0.9979 L0.4038,0.9203 Q0.4038,0.9182,0.4062,0.9182 Z M0.4087,0.9224 L0.4087,0.9958 L0.4939,0.9958 L0.4939,0.9224 L0.4087,0.9224 Z M0.4062,0.9182 L0.4963,0.9182 Q0.4988,0.9182,0.4988,0.9203 L0.4988,0.9979 Q0.4988,1,0.4963,1 L0.4062,1 Q0.4038,1,0.4038,0.9979 L0.4038,0.9203 Q0.4038,0.9182,0.4062,0.9182 Z M0.1374,0.9203 L0.2275,0.9203 L0.2275,0.9979 L0.1374,0.9979 L0.1374,0.9203 Z M0.1374,0.9203 L0.2275,0.9203 L0.2275,0.9979 L0.1374,0.9979 L0.1374,0.9203 Z M0.1374,0.9203 L0.2275,0.9203 L0.2275,0.9979 L0.1374,0.9979 L0.1374,0.9203 Z M0,0.9203 L0.0901,0.9203 L0.0901,0.9979 L0,0.9979 L0,0.9203 Z M0,0.9203 L0.0901,0.9203 L0.0901,0.9979 L0,0.9979 L0,0.9203 Z M0,0.9203 L0.0901,0.9203 L0.0901,0.9979 L0,0.9979 L0,0.9203 Z M0.9099,0.1158 L0.9099,0.1888 L0.9951,0.1888 L0.9951,0.1158 L0.9099,0.1158 Z M0.9075,0.1116 L0.9976,0.1116 Q1,0.1116,1,0.1137 L1,0.1909 Q1,0.193,0.9976,0.193 L0.9075,0.193 Q0.905,0.193,0.905,0.1909 L0.905,0.1137 Q0.905,0.1116,0.9075,0.1116 Z M0.7852,0.6086 L0.7852,0.682 L0.8704,0.682 L0.8704,0.6086 L0.7852,0.6086 Z M0.7828,0.6044 L0.8729,0.6044 Q0.8753,0.6044,0.8753,0.6065 L0.8753,0.6841 Q0.8753,0.6862,0.8729,0.6862 L0.7828,0.6862 Q0.7803,0.6862,0.7803,0.6841 L0.7803,0.6065 Q0.7803,0.6044,0.7828,0.6044 Z M0.66,0.1137 L0.7501,0.1137 L0.7501,0.1909 L0.66,0.1909 L0.66,0.1137 Z M0.6624,0.4102 L0.6624,0.4836 L0.7477,0.4836 L0.7477,0.4102 L0.6624,0.4102 Z M0.66,0.406 L0.7501,0.406 Q0.7526,0.406,0.7526,0.4081 L0.7526,0.4857 Q0.7526,0.4878,0.7501,0.4878 L0.66,0.4878 Q0.6576,0.4878,0.6576,0.4857 L0.6576,0.4081 Q0.6576,0.406,0.66,0.406 Z M0.6624,0.7131 L0.6624,0.7865 L0.7477,0.7865 L0.7477,0.7131 L0.6624,0.7131 Z M0.66,0.7089 L0.7501,0.7089 Q0.7526,0.7089,0.7526,0.711 L0.7526,0.7886 Q0.7526,0.7907,0.7501,0.7907 L0.66,0.7907 Q0.6576,0.7907,0.6576,0.7886 L0.6576,0.711 Q0.6576,0.7089,0.66,0.7089 Z M0.5407,0.2227 L0.5407,0.2836 L0.6113,0.2836 L0.6113,0.2227 L0.5407,0.2227 Z M0.5309,0.206 L0.621,0.206 Q0.623,0.206,0.6249,0.2068 T0.6281,0.2089 T0.6301,0.2116 T0.6308,0.2143 L0.6308,0.2919 Q0.6308,0.2957,0.6276,0.298 T0.621,0.3003 L0.5309,0.3003 Q0.5265,0.3003,0.5239,0.2976 T0.5212,0.2919 L0.5212,0.2143 Q0.5212,0.211,0.5244,0.2085 T0.5309,0.206 Z M0.5407,0.3184 L0.5407,0.3792 L0.6113,0.3792 L0.6113,0.3184 L0.5407,0.3184 Z M0.5309,0.3016 L0.621,0.3016 Q0.6254,0.3016,0.6281,0.3043 T0.6308,0.31 L0.6308,0.3876 Q0.6308,0.3914,0.6276,0.3937 T0.621,0.396 L0.5309,0.396 Q0.5265,0.396,0.5239,0.3932 T0.5212,0.3876 L0.5212,0.31 Q0.5212,0.3062,0.5244,0.3039 T0.5309,0.3016 Z M0.5309,0.4081 L0.621,0.4081 L0.621,0.4857 L0.5309,0.4857 L0.5309,0.4081 Z M0.5309,0.5059 L0.621,0.5059 L0.621,0.5835 L0.5309,0.5835 L0.5309,0.5059 Z M0.5407,0.6149 L0.5407,0.6758 L0.6113,0.6758 L0.6113,0.6149 L0.5407,0.6149 Z M0.5309,0.5982 L0.621,0.5982 Q0.6254,0.5982,0.6281,0.6009 T0.6308,0.6065 L0.6308,0.6841 Q0.6308,0.6879,0.6276,0.6902 T0.621,0.6925 L0.5309,0.6925 Q0.5265,0.6925,0.5239,0.6898 T0.5212,0.6841 L0.5212,0.6065 Q0.5212,0.6028,0.5244,0.6005 T0.5309,0.5982 Z M0.4062,0.1137 L0.4963,0.1137 L0.4963,0.1909 L0.4062,0.1909 L0.4062,0.1137 Z M0.4062,0.2143 L0.4963,0.2143 L0.4963,0.2919 L0.4062,0.2919 L0.4062,0.2143 Z M0.4184,0.4186 L0.4184,0.4753 L0.4842,0.4753 L0.4842,0.4186 L0.4184,0.4186 Z M0.4062,0.3977 L0.4963,0.3977 Q0.5017,0.3977,0.5051,0.401 T0.5085,0.4081 L0.5085,0.4857 Q0.5085,0.4904,0.5046,0.4933 T0.4963,0.4962 L0.4062,0.4962 Q0.4009,0.4962,0.3975,0.4929 T0.3941,0.4857 L0.3941,0.4081 Q0.3941,0.4035,0.398,0.4006 T0.4062,0.3977 Z M0.4062,0.5059 L0.4963,0.5059 L0.4963,0.5835 L0.4062,0.5835 L0.4062,0.5059 Z M0.4087,0.7131 L0.4087,0.7865 L0.4939,0.7865 L0.4939,0.7131 L0.4087,0.7131 Z M0.4062,0.7089 L0.4963,0.7089 Q0.4988,0.7089,0.4988,0.711 L0.4988,0.7886 Q0.4988,0.7907,0.4963,0.7907 L0.4062,0.7907 Q0.4038,0.7907,0.4038,0.7886 L0.4038,0.711 Q0.4038,0.7089,0.4062,0.7089 Z M0.2689,0.1137 L0.359,0.1137 L0.359,0.1909 L0.2689,0.1909 L0.2689,0.1137 Z M0.2689,0.2143 L0.359,0.2143 L0.359,0.2919 L0.2689,0.2919 L0.2689,0.2143 Z M0.2689,0.8154 L0.359,0.8154 L0.359,0.8926 L0.2689,0.8926 L0.2689,0.8154 Z M0.2689,0.31 L0.359,0.31 L0.359,0.3876 L0.2689,0.3876 L0.2689,0.31 Z M0.2689,0.8154 L0.359,0.8154 L0.359,0.8926 L0.2689,0.8926 L0.2689,0.8154 Z M0.2689,0.4081 L0.359,0.4081 L0.359,0.4857 L0.2689,0.4857 L0.2689,0.4081 Z M0.2689,0.5059 L0.359,0.5059 L0.359,0.5835 L0.2689,0.5835 L0.2689,0.5059 Z M0.2689,0.711 L0.359,0.711 L0.359,0.7886 L0.2689,0.7886 L0.2689,0.711 Z M0.2689,0.8154 L0.359,0.8154 L0.359,0.8926 L0.2689,0.8926 L0.2689,0.8154 Z M0.1374,0.1137 L0.2275,0.1137 L0.2275,0.1909 L0.1374,0.1909 L0.1374,0.1137 Z M0.1374,0.2143 L0.2275,0.2143 L0.2275,0.2919 L0.1374,0.2919 L0.1374,0.2143 Z M0.1374,0.8154 L0.2275,0.8154 L0.2275,0.8926 L0.1374,0.8926 L0.1374,0.8154 Z M0.1374,0.31 L0.2275,0.31 L0.2275,0.3876 L0.1374,0.3876 L0.1374,0.31 Z M0.1374,0.8154 L0.2275,0.8154 L0.2275,0.8926 L0.1374,0.8926 L0.1374,0.8154 Z M0.1374,0.4081 L0.2275,0.4081 L0.2275,0.4857 L0.1374,0.4857 L0.1374,0.4081 Z M0.1374,0.5059 L0.2275,0.5059 L0.2275,0.5835 L0.1374,0.5835 L0.1374,0.5059 Z M0.1374,0.6065 L0.2275,0.6065 L0.2275,0.6841 L0.1374,0.6841 L0.1374,0.6065 Z M0.1374,0.711 L0.2275,0.711 L0.2275,0.7886 L0.1374,0.7886 L0.1374,0.711 Z M0.1374,0.8154 L0.2275,0.8154 L0.2275,0.8926 L0.1374,0.8926 L0.1374,0.8154 Z M0,0.1137 L0.0901,0.1137 L0.0901,0.1909 L0,0.1909 L0,0.1137 Z M0,0.2143 L0.0901,0.2143 L0.0901,0.2919 L0,0.2919 L0,0.2143 Z M0,0.8154 L0.0901,0.8154 L0.0901,0.8926 L0,0.8926 L0,0.8154 Z M0,0.31 L0.0901,0.31 L0.0901,0.3876 L0,0.3876 L0,0.31 Z M0,0.8154 L0.0901,0.8154 L0.0901,0.8926 L0,0.8926 L0,0.8154 Z M0,0.4081 L0.0901,0.4081 L0.0901,0.4857 L0,0.4857 L0,0.4081 Z M0,0.5059 L0.0901,0.5059 L0.0901,0.5835 L0,0.5835 L0,0.5059 Z M0,0.6065 L0.0901,0.6065 L0.0901,0.6841 L0,0.6841 L0,0.6065 Z M0,0.711 L0.0901,0.711 L0.0901,0.7886 L0,0.7886 L0,0.711 Z M0,0.8154 L0.0901,0.8154 L0.0901,0.8926 L0,0.8926 L0,0.8154 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0C5}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.5884,0.0126 L0.5884,0.0776 L0.5129,0.0776 L0.5129,0.0126 L0.5884,0.0126 Z M0.5957,0 L0.5056,0 Q0.5027,0,0.5005,0.0021 T0.4983,0.0063 L0.4983,0.0839 Q0.4983,0.0864,0.5007,0.0883 T0.5056,0.0902 L0.5957,0.0902 Q0.5986,0.0902,0.6008,0.0881 T0.603,0.0839 L0.603,0.0063 Q0.603,0.0038,0.6006,0.0019 T0.5957,0 Z M0.5884,0.0126 L0.5884,0.0776 L0.5129,0.0776 L0.5129,0.0126 L0.5884,0.0126 Z M0.5957,0 L0.5056,0 Q0.5027,0,0.5005,0.0021 T0.4983,0.0063 L0.4983,0.0839 Q0.4983,0.0864,0.5007,0.0883 T0.5056,0.0902 L0.5957,0.0902 Q0.5986,0.0902,0.6008,0.0881 T0.603,0.0839 L0.603,0.0063 Q0.603,0.0038,0.6006,0.0019 T0.5957,0 Z M0.5884,0.0126 L0.5884,0.0776 L0.5129,0.0776 L0.5129,0.0126 L0.5884,0.0126 Z M0.5957,0 L0.5056,0 Q0.5027,0,0.5005,0.0021 T0.4983,0.0063 L0.4983,0.0839 Q0.4983,0.0864,0.5007,0.0883 T0.5056,0.0902 L0.5957,0.0902 Q0.5986,0.0902,0.6008,0.0881 T0.603,0.0839 L0.603,0.0063 Q0.603,0.0038,0.6006,0.0019 T0.5957,0 Z M0.0901,0.0084 L0.0901,0.0818 L0.0049,0.0818 L0.0049,0.0084 L0.0901,0.0084 Z M0.0925,0.0042 L0.0024,0.0042 Q0,0.0042,0,0.0063 L0,0.0839 Q0,0.086,0.0024,0.086 L0.0925,0.086 Q0.095,0.086,0.095,0.0839 L0.095,0.0063 Q0.095,0.0042,0.0925,0.0042 Z M0.0901,0.0084 L0.0901,0.0818 L0.0049,0.0818 L0.0049,0.0084 L0.0901,0.0084 Z M0.0925,0.0042 L0.0024,0.0042 Q0,0.0042,0,0.0063 L0,0.0839 Q0,0.086,0.0024,0.086 L0.0925,0.086 Q0.095,0.086,0.095,0.0839 L0.095,0.0063 Q0.095,0.0042,0.0925,0.0042 Z M0.0901,0.0084 L0.0901,0.0818 L0.0049,0.0818 L0.0049,0.0084 L0.0901,0.0084 Z M0.0925,0.0042 L0.0024,0.0042 Q0,0.0042,0,0.0063 L0,0.0839 Q0,0.086,0.0024,0.086 L0.0925,0.086 Q0.095,0.086,0.095,0.0839 L0.095,0.0063 Q0.095,0.0042,0.0925,0.0042 Z M0.34,0.0063 L0.2499,0.0063 L0.2499,0.0839 L0.34,0.0839 L0.34,0.0063 Z M0.34,0.0063 L0.2499,0.0063 L0.2499,0.0839 L0.34,0.0839 L0.34,0.0063 Z M0.34,0.0063 L0.2499,0.0063 L0.2499,0.0839 L0.34,0.0839 L0.34,0.0063 Z M0.4691,0.0063 L0.379,0.0063 L0.379,0.0839 L0.4691,0.0839 L0.4691,0.0063 Z M0.4691,0.0063 L0.379,0.0063 L0.379,0.0839 L0.4691,0.0839 L0.4691,0.0063 Z M0.4691,0.0063 L0.379,0.0063 L0.379,0.0839 L0.4691,0.0839 L0.4691,0.0063 Z M0.7311,0.0063 L0.641,0.0063 L0.641,0.0839 L0.7311,0.0839 L0.7311,0.0063 Z M0.7311,0.0063 L0.641,0.0063 L0.641,0.0839 L0.7311,0.0839 L0.7311,0.0063 Z M0.7311,0.0063 L0.641,0.0063 L0.641,0.0839 L0.7311,0.0839 L0.7311,0.0063 Z M0.8626,0.0063 L0.7725,0.0063 L0.7725,0.0839 L0.8626,0.0839 L0.8626,0.0063 Z M0.8626,0.0063 L0.7725,0.0063 L0.7725,0.0839 L0.8626,0.0839 L0.8626,0.0063 Z M0.8626,0.0063 L0.7725,0.0063 L0.7725,0.0839 L0.8626,0.0839 L0.8626,0.0063 Z M1,0.0063 L0.9099,0.0063 L0.9099,0.0839 L1,0.0839 L1,0.0063 Z M1,0.0063 L0.9099,0.0063 L0.9099,0.0839 L1,0.0839 L1,0.0063 Z M1,0.0063 L0.9099,0.0063 L0.9099,0.0839 L1,0.0839 L1,0.0063 Z M0.5913,0.9224 L0.5913,0.9958 L0.5061,0.9958 L0.5061,0.9224 L0.5913,0.9224 Z M0.5938,0.9182 L0.5037,0.9182 Q0.5012,0.9182,0.5012,0.9203 L0.5012,0.9979 Q0.5012,1,0.5037,1 L0.5938,1 Q0.5962,1,0.5962,0.9979 L0.5962,0.9203 Q0.5962,0.9182,0.5938,0.9182 Z M0.5913,0.9224 L0.5913,0.9958 L0.5061,0.9958 L0.5061,0.9224 L0.5913,0.9224 Z M0.5938,0.9182 L0.5037,0.9182 Q0.5012,0.9182,0.5012,0.9203 L0.5012,0.9979 Q0.5012,1,0.5037,1 L0.5938,1 Q0.5962,1,0.5962,0.9979 L0.5962,0.9203 Q0.5962,0.9182,0.5938,0.9182 Z M0.5913,0.9224 L0.5913,0.9958 L0.5061,0.9958 L0.5061,0.9224 L0.5913,0.9224 Z M0.5938,0.9182 L0.5037,0.9182 Q0.5012,0.9182,0.5012,0.9203 L0.5012,0.9979 Q0.5012,1,0.5037,1 L0.5938,1 Q0.5962,1,0.5962,0.9979 L0.5962,0.9203 Q0.5962,0.9182,0.5938,0.9182 Z M0.8626,0.9203 L0.7725,0.9203 L0.7725,0.9979 L0.8626,0.9979 L0.8626,0.9203 Z M0.8626,0.9203 L0.7725,0.9203 L0.7725,0.9979 L0.8626,0.9979 L0.8626,0.9203 Z M0.8626,0.9203 L0.7725,0.9203 L0.7725,0.9979 L0.8626,0.9979 L0.8626,0.9203 Z M1,0.9203 L0.9099,0.9203 L0.9099,0.9979 L1,0.9979 L1,0.9203 Z M1,0.9203 L0.9099,0.9203 L0.9099,0.9979 L1,0.9979 L1,0.9203 Z M1,0.9203 L0.9099,0.9203 L0.9099,0.9979 L1,0.9979 L1,0.9203 Z M0.0901,0.1158 L0.0901,0.1888 L0.0049,0.1888 L0.0049,0.1158 L0.0901,0.1158 Z M0.0925,0.1116 L0.0024,0.1116 Q0,0.1116,0,0.1137 L0,0.1909 Q0,0.193,0.0024,0.193 L0.0925,0.193 Q0.095,0.193,0.095,0.1909 L0.095,0.1137 Q0.095,0.1116,0.0925,0.1116 Z M0.2148,0.6086 L0.2148,0.682 L0.1296,0.682 L0.1296,0.6086 L0.2148,0.6086 Z M0.2172,0.6044 L0.1271,0.6044 Q0.1247,0.6044,0.1247,0.6065 L0.1247,0.6841 Q0.1247,0.6862,0.1271,0.6862 L0.2172,0.6862 Q0.2197,0.6862,0.2197,0.6841 L0.2197,0.6065 Q0.2197,0.6044,0.2172,0.6044 Z M0.34,0.1137 L0.2499,0.1137 L0.2499,0.1909 L0.34,0.1909 L0.34,0.1137 Z M0.3376,0.4102 L0.3376,0.4836 L0.2523,0.4836 L0.2523,0.4102 L0.3376,0.4102 Z M0.34,0.406 L0.2499,0.406 Q0.2474,0.406,0.2474,0.4081 L0.2474,0.4857 Q0.2474,0.4878,0.2499,0.4878 L0.34,0.4878 Q0.3424,0.4878,0.3424,0.4857 L0.3424,0.4081 Q0.3424,0.406,0.34,0.406 Z M0.3376,0.7131 L0.3376,0.7865 L0.2523,0.7865 L0.2523,0.7131 L0.3376,0.7131 Z M0.34,0.7089 L0.2499,0.7089 Q0.2474,0.7089,0.2474,0.711 L0.2474,0.7886 Q0.2474,0.7907,0.2499,0.7907 L0.34,0.7907 Q0.3424,0.7907,0.3424,0.7886 L0.3424,0.711 Q0.3424,0.7089,0.34,0.7089 Z M0.4593,0.2227 L0.4593,0.2836 L0.3887,0.2836 L0.3887,0.2227 L0.4593,0.2227 Z M0.4691,0.206 L0.379,0.206 Q0.3746,0.206,0.3719,0.2087 T0.3692,0.2143 L0.3692,0.2919 Q0.3692,0.2957,0.3724,0.298 T0.379,0.3003 L0.4691,0.3003 Q0.4735,0.3003,0.4761,0.2976 T0.4788,0.2919 L0.4788,0.2143 Q0.4788,0.211,0.4756,0.2085 T0.4691,0.206 Z M0.4593,0.3184 L0.4593,0.3792 L0.3887,0.3792 L0.3887,0.3184 L0.4593,0.3184 Z M0.4691,0.3016 L0.379,0.3016 Q0.3746,0.3016,0.3719,0.3043 T0.3692,0.31 L0.3692,0.3876 Q0.3692,0.3914,0.3724,0.3937 T0.379,0.396 L0.4691,0.396 Q0.4735,0.396,0.4761,0.3932 T0.4788,0.3876 L0.4788,0.31 Q0.4788,0.3062,0.4756,0.3039 T0.4691,0.3016 Z M0.4691,0.4081 L0.379,0.4081 L0.379,0.4857 L0.4691,0.4857 L0.4691,0.4081 Z M0.4691,0.5059 L0.379,0.5059 L0.379,0.5835 L0.4691,0.5835 L0.4691,0.5059 Z M0.4593,0.6149 L0.4593,0.6758 L0.3887,0.6758 L0.3887,0.6149 L0.4593,0.6149 Z M0.4691,0.5982 L0.379,0.5982 Q0.3746,0.5982,0.3719,0.6009 T0.3692,0.6065 L0.3692,0.6841 Q0.3692,0.6879,0.3724,0.6902 T0.379,0.6925 L0.4691,0.6925 Q0.4735,0.6925,0.4761,0.6898 T0.4788,0.6841 L0.4788,0.6065 Q0.4788,0.6028,0.4756,0.6005 T0.4691,0.5982 Z M0.5938,0.1137 L0.5037,0.1137 L0.5037,0.1909 L0.5938,0.1909 L0.5938,0.1137 Z M0.5938,0.2143 L0.5037,0.2143 L0.5037,0.2919 L0.5938,0.2919 L0.5938,0.2143 Z M0.5816,0.4186 L0.5816,0.4753 L0.5158,0.4753 L0.5158,0.4186 L0.5816,0.4186 Z M0.5938,0.3977 L0.5037,0.3977 Q0.4983,0.3977,0.4949,0.401 T0.4915,0.4081 L0.4915,0.4857 Q0.4915,0.4904,0.4954,0.4933 T0.5037,0.4962 L0.5938,0.4962 Q0.5991,0.4962,0.6025,0.4929 T0.6059,0.4857 L0.6059,0.4081 Q0.6059,0.4035,0.602,0.4006 T0.5938,0.3977 Z M0.5938,0.5059 L0.5037,0.5059 L0.5037,0.5835 L0.5938,0.5835 L0.5938,0.5059 Z M0.5913,0.7131 L0.5913,0.7865 L0.5061,0.7865 L0.5061,0.7131 L0.5913,0.7131 Z M0.5938,0.7089 L0.5037,0.7089 Q0.5012,0.7089,0.5012,0.711 L0.5012,0.7886 Q0.5012,0.7907,0.5037,0.7907 L0.5938,0.7907 Q0.5962,0.7907,0.5962,0.7886 L0.5962,0.711 Q0.5962,0.7089,0.5938,0.7089 Z M0.7311,0.1137 L0.641,0.1137 L0.641,0.1909 L0.7311,0.1909 L0.7311,0.1137 Z M0.7311,0.2143 L0.641,0.2143 L0.641,0.2919 L0.7311,0.2919 L0.7311,0.2143 Z M0.7311,0.8154 L0.641,0.8154 L0.641,0.8926 L0.7311,0.8926 L0.7311,0.8154 Z M0.7311,0.31 L0.641,0.31 L0.641,0.3876 L0.7311,0.3876 L0.7311,0.31 Z M0.7311,0.8154 L0.641,0.8154 L0.641,0.8926 L0.7311,0.8926 L0.7311,0.8154 Z M0.7311,0.4081 L0.641,0.4081 L0.641,0.4857 L0.7311,0.4857 L0.7311,0.4081 Z M0.7311,0.5059 L0.641,0.5059 L0.641,0.5835 L0.7311,0.5835 L0.7311,0.5059 Z M0.7311,0.711 L0.641,0.711 L0.641,0.7886 L0.7311,0.7886 L0.7311,0.711 Z M0.7311,0.8154 L0.641,0.8154 L0.641,0.8926 L0.7311,0.8926 L0.7311,0.8154 Z M0.8626,0.1137 L0.7725,0.1137 L0.7725,0.1909 L0.8626,0.1909 L0.8626,0.1137 Z M0.8626,0.2143 L0.7725,0.2143 L0.7725,0.2919 L0.8626,0.2919 L0.8626,0.2143 Z M0.8626,0.8154 L0.7725,0.8154 L0.7725,0.8926 L0.8626,0.8926 L0.8626,0.8154 Z M0.8626,0.31 L0.7725,0.31 L0.7725,0.3876 L0.8626,0.3876 L0.8626,0.31 Z M0.8626,0.8154 L0.7725,0.8154 L0.7725,0.8926 L0.8626,0.8926 L0.8626,0.8154 Z M0.8626,0.4081 L0.7725,0.4081 L0.7725,0.4857 L0.8626,0.4857 L0.8626,0.4081 Z M0.8626,0.5059 L0.7725,0.5059 L0.7725,0.5835 L0.8626,0.5835 L0.8626,0.5059 Z M0.8626,0.6065 L0.7725,0.6065 L0.7725,0.6841 L0.8626,0.6841 L0.8626,0.6065 Z M0.8626,0.711 L0.7725,0.711 L0.7725,0.7886 L0.8626,0.7886 L0.8626,0.711 Z M0.8626,0.8154 L0.7725,0.8154 L0.7725,0.8926 L0.8626,0.8926 L0.8626,0.8154 Z M1,0.1137 L0.9099,0.1137 L0.9099,0.1909 L1,0.1909 L1,0.1137 Z M1,0.2143 L0.9099,0.2143 L0.9099,0.2919 L1,0.2919 L1,0.2143 Z M1,0.8154 L0.9099,0.8154 L0.9099,0.8926 L1,0.8926 L1,0.8154 Z M1,0.31 L0.9099,0.31 L0.9099,0.3876 L1,0.3876 L1,0.31 Z M1,0.8154 L0.9099,0.8154 L0.9099,0.8926 L1,0.8926 L1,0.8154 Z M1,0.4081 L0.9099,0.4081 L0.9099,0.4857 L1,0.4857 L1,0.4081 Z M1,0.5059 L0.9099,0.5059 L0.9099,0.5835 L1,0.5835 L1,0.5059 Z M1,0.6065 L0.9099,0.6065 L0.9099,0.6841 L1,0.6841 L1,0.6065 Z M1,0.711 L0.9099,0.711 L0.9099,0.7886 L1,0.7886 L1,0.711 Z M1,0.8154 L0.9099,0.8154 L0.9099,0.8926 L1,0.8926 L1,0.8154 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0C6}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.6529,0.018 L0.6529,0.112 L0.7724,0.112 L0.7724,0.018 L0.6529,0.018 Z M0.6411,0 L0.7837,0 Q0.7873,0,0.7902,0.0015 T0.7943,0.005 T0.7956,0.0092 L0.7956,0.1208 Q0.7956,0.125,0.7917,0.1275 T0.7837,0.13 L0.6411,0.13 Q0.6359,0.13,0.6326,0.1271 T0.6292,0.1208 L0.6292,0.0092 Q0.6292,0.005,0.6331,0.0025 T0.6411,0 Z M0.6529,0.018 L0.6529,0.112 L0.7724,0.112 L0.7724,0.018 L0.6529,0.018 Z M0.6411,0 L0.7837,0 Q0.7873,0,0.7902,0.0015 T0.7943,0.005 T0.7956,0.0092 L0.7956,0.1208 Q0.7956,0.125,0.7917,0.1275 T0.7837,0.13 L0.6411,0.13 Q0.6359,0.13,0.6326,0.1271 T0.6292,0.1208 L0.6292,0.0092 Q0.6292,0.005,0.6331,0.0025 T0.6411,0 Z M0.6529,0.018 L0.6529,0.112 L0.7724,0.112 L0.7724,0.018 L0.6529,0.018 Z M0.6411,0 L0.7837,0 Q0.7873,0,0.7902,0.0015 T0.7943,0.005 T0.7956,0.0092 L0.7956,0.1208 Q0.7956,0.125,0.7917,0.1275 T0.7837,0.13 L0.6411,0.13 Q0.6359,0.13,0.6326,0.1271 T0.6292,0.1208 L0.6292,0.0092 Q0.6292,0.005,0.6331,0.0025 T0.6411,0 Z M0.4325,0.0143 L0.4325,0.1158 L0.5628,0.1158 L0.5628,0.0143 L0.4325,0.0143 Z M0.4264,0.0042 L0.5695,0.0042 Q0.5721,0.0042,0.5739,0.0057 T0.5757,0.0092 L0.5757,0.1208 Q0.5757,0.1221,0.5752,0.1231 T0.5736,0.1248 T0.5716,0.1256 T0.5695,0.1258 L0.4264,0.1258 Q0.4238,0.1258,0.422,0.1244 T0.4202,0.1208 L0.4202,0.0092 Q0.4202,0.008,0.4207,0.0069 T0.4222,0.0052 T0.4243,0.0044 T0.4264,0.0042 Z M0.4325,0.0143 L0.4325,0.1158 L0.5628,0.1158 L0.5628,0.0143 L0.4325,0.0143 Z M0.4264,0.0042 L0.5695,0.0042 Q0.5721,0.0042,0.5739,0.0057 T0.5757,0.0092 L0.5757,0.1208 Q0.5757,0.1221,0.5752,0.1231 T0.5736,0.1248 T0.5716,0.1256 T0.5695,0.1258 L0.4264,0.1258 Q0.4238,0.1258,0.422,0.1244 T0.4202,0.1208 L0.4202,0.0092 Q0.4202,0.008,0.4207,0.0069 T0.4222,0.0052 T0.4243,0.0044 T0.4264,0.0042 Z M0.4325,0.0143 L0.4325,0.1158 L0.5628,0.1158 L0.5628,0.0143 L0.4325,0.0143 Z M0.4264,0.0042 L0.5695,0.0042 Q0.5721,0.0042,0.5739,0.0057 T0.5757,0.0092 L0.5757,0.1208 Q0.5757,0.1221,0.5752,0.1231 T0.5736,0.1248 T0.5716,0.1256 T0.5695,0.1258 L0.4264,0.1258 Q0.4238,0.1258,0.422,0.1244 T0.4202,0.1208 L0.4202,0.0092 Q0.4202,0.008,0.4207,0.0069 T0.4222,0.0052 T0.4243,0.0044 T0.4264,0.0042 Z M0.2178,0.0092 L0.3605,0.0092 L0.3605,0.1208 L0.2178,0.1208 L0.2178,0.0092 Z M0.2178,0.0092 L0.3605,0.0092 L0.3605,0.1208 L0.2178,0.1208 L0.2178,0.0092 Z M0.2178,0.0092 L0.3605,0.0092 L0.3605,0.1208 L0.2178,0.1208 L0.2178,0.0092 Z M0,0.0092 L0.1432,0.0092 L0.1432,0.1208 L0,0.1208 L0,0.0092 Z M0,0.0092 L0.1432,0.0092 L0.1432,0.1208 L0,0.1208 L0,0.0092 Z M0,0.0092 L0.1432,0.0092 L0.1432,0.1208 L0,0.1208 L0,0.0092 Z M0.8574,0.3217 L0.8574,0.4098 L0.9691,0.4098 L0.9691,0.3217 L0.8574,0.3217 Z M0.8419,0.2978 L0.9846,0.2978 Q0.9892,0.2978,0.9928,0.2997 T0.9982,0.3043 T1,0.31 L1,0.4216 Q1,0.427,0.9951,0.4304 T0.9846,0.4337 L0.8419,0.4337 Q0.8352,0.4337,0.8308,0.4299 T0.8265,0.4216 L0.8265,0.31 Q0.8265,0.3062,0.829,0.3035 T0.835,0.2993 T0.8419,0.2978 Z M0.8574,0.888 L0.8574,0.9757 L0.9691,0.9757 L0.9691,0.888 L0.8574,0.888 Z M0.8419,0.8637 L0.9846,0.8637 Q0.9892,0.8637,0.9928,0.8658 T0.9982,0.8706 T1,0.8758 L1,0.9878 Q1,0.9929,0.9951,0.9964 T0.9846,1 L0.8419,1 Q0.8352,1,0.8308,0.996 T0.8265,0.9878 L0.8265,0.8758 Q0.8265,0.8708,0.8314,0.8672 T0.8419,0.8637 Z M0.6509,0.169 L0.6509,0.2706 L0.7806,0.2706 L0.7806,0.169 L0.6509,0.169 Z M0.6442,0.159 L0.7868,0.159 Q0.7899,0.159,0.7917,0.1604 T0.7935,0.164 L0.7935,0.2756 Q0.7935,0.2781,0.7915,0.2796 T0.7868,0.281 L0.6442,0.281 Q0.6416,0.281,0.6395,0.2794 T0.6375,0.2756 L0.6375,0.164 Q0.6375,0.1619,0.6398,0.1604 T0.6442,0.159 Z M0.6632,0.6044 L0.6632,0.6858 L0.7678,0.6858 L0.7678,0.6044 L0.6632,0.6044 Z M0.6442,0.5742 L0.7868,0.5742 Q0.7951,0.5742,0.8007,0.5791 T0.8064,0.5893 L0.8064,0.7013 Q0.8064,0.7076,0.8002,0.712 T0.7868,0.7164 L0.6442,0.7164 Q0.6359,0.7164,0.6305,0.7114 T0.6251,0.7013 L0.6251,0.5893 Q0.6251,0.5847,0.6282,0.5812 T0.6357,0.5759 T0.6442,0.5742 Z M0.4264,0.164 L0.5695,0.164 L0.5695,0.2756 L0.4264,0.2756 L0.4264,0.164 Z M0.4325,0.315 L0.4325,0.4165 L0.5628,0.4165 L0.5628,0.315 L0.4325,0.315 Z M0.4264,0.3049 L0.5695,0.3049 Q0.5721,0.3049,0.5739,0.3064 T0.5757,0.31 L0.5757,0.4216 Q0.5757,0.4237,0.5736,0.4251 T0.5695,0.4266 L0.4264,0.4266 Q0.4238,0.4266,0.422,0.4251 T0.4202,0.4216 L0.4202,0.31 Q0.4202,0.3087,0.4207,0.3077 T0.4222,0.306 T0.4243,0.3052 T0.4264,0.3049 Z M0.4264,0.4476 L0.5695,0.4476 L0.5695,0.5596 L0.4264,0.5596 L0.4264,0.4476 Z M0.4392,0.7408 L0.4392,0.8326 L0.5561,0.8326 L0.5561,0.7408 L0.4392,0.7408 Z M0.4264,0.7206 L0.5695,0.7206 Q0.5747,0.7206,0.5783,0.724 T0.5819,0.7307 L0.5819,0.8427 Q0.5819,0.8469,0.5778,0.8498 T0.5695,0.8528 L0.4264,0.8528 Q0.4207,0.8528,0.4171,0.8496 T0.4135,0.8427 L0.4135,0.7307 Q0.4135,0.7265,0.4176,0.7236 T0.4264,0.7206 Z M0.2178,0.164 L0.3605,0.164 L0.3605,0.2756 L0.2178,0.2756 L0.2178,0.164 Z M0.2178,0.31 L0.3605,0.31 L0.3605,0.4216 L0.2178,0.4216 L0.2178,0.31 Z M0.2178,0.5893 L0.3605,0.5893 L0.3605,0.7013 L0.2178,0.7013 L0.2178,0.5893 Z M0.2178,0.7307 L0.3605,0.7307 L0.3605,0.8427 L0.2178,0.8427 L0.2178,0.7307 Z M0.2178,0.8758 L0.3605,0.8758 L0.3605,0.9878 L0.2178,0.9878 L0.2178,0.8758 Z M0,0.164 L0.1432,0.164 L0.1432,0.2756 L0,0.2756 L0,0.164 Z M0,0.31 L0.1432,0.31 L0.1432,0.4216 L0,0.4216 L0,0.31 Z M0,0.4476 L0.1432,0.4476 L0.1432,0.5596 L0,0.5596 L0,0.4476 Z M0,0.5893 L0.1432,0.5893 L0.1432,0.7013 L0,0.7013 L0,0.5893 Z M0,0.7307 L0.1432,0.7307 L0.1432,0.8427 L0,0.8427 L0,0.7307 Z M0,0.8758 L0.1432,0.8758 L0.1432,0.9878 L0,0.9878 L0,0.8758 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0C7}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.3476,0.018 L0.3476,0.112 L0.2276,0.112 L0.2276,0.018 L0.3476,0.018 Z M0.3594,0 L0.2163,0 Q0.2132,0,0.2104,0.0015 T0.2062,0.005 T0.2049,0.0092 L0.2049,0.1208 Q0.2049,0.1237,0.2067,0.1258 T0.2111,0.129 T0.2163,0.13 L0.3594,0.13 Q0.3641,0.13,0.3674,0.1271 T0.3708,0.1208 L0.3708,0.0092 Q0.3708,0.005,0.3669,0.0025 T0.3594,0 Z M0.3476,0.018 L0.3476,0.112 L0.2276,0.112 L0.2276,0.018 L0.3476,0.018 Z M0.3594,0 L0.2163,0 Q0.2132,0,0.2104,0.0015 T0.2062,0.005 T0.2049,0.0092 L0.2049,0.1208 Q0.2049,0.1237,0.2067,0.1258 T0.2111,0.129 T0.2163,0.13 L0.3594,0.13 Q0.3641,0.13,0.3674,0.1271 T0.3708,0.1208 L0.3708,0.0092 Q0.3708,0.005,0.3669,0.0025 T0.3594,0 Z M0.3476,0.018 L0.3476,0.112 L0.2276,0.112 L0.2276,0.018 L0.3476,0.018 Z M0.3594,0 L0.2163,0 Q0.2132,0,0.2104,0.0015 T0.2062,0.005 T0.2049,0.0092 L0.2049,0.1208 Q0.2049,0.1237,0.2067,0.1258 T0.2111,0.129 T0.2163,0.13 L0.3594,0.13 Q0.3641,0.13,0.3674,0.1271 T0.3708,0.1208 L0.3708,0.0092 Q0.3708,0.005,0.3669,0.0025 T0.3594,0 Z M0.5675,0.0143 L0.5675,0.1158 L0.4377,0.1158 L0.4377,0.0143 L0.5675,0.0143 Z M0.5736,0.0042 L0.431,0.0042 Q0.4284,0.0042,0.4264,0.0057 T0.4243,0.0092 L0.4243,0.1208 Q0.4243,0.1221,0.4251,0.1231 T0.4269,0.1248 T0.4289,0.1256 T0.431,0.1258 L0.5736,0.1258 Q0.5767,0.1258,0.5785,0.1244 T0.5803,0.1208 L0.5803,0.0092 Q0.5803,0.008,0.5798,0.0069 T0.5783,0.0052 T0.5762,0.0044 T0.5736,0.0042 Z M0.5675,0.0143 L0.5675,0.1158 L0.4377,0.1158 L0.4377,0.0143 L0.5675,0.0143 Z M0.5736,0.0042 L0.431,0.0042 Q0.4284,0.0042,0.4264,0.0057 T0.4243,0.0092 L0.4243,0.1208 Q0.4243,0.1221,0.4251,0.1231 T0.4269,0.1248 T0.4289,0.1256 T0.431,0.1258 L0.5736,0.1258 Q0.5767,0.1258,0.5785,0.1244 T0.5803,0.1208 L0.5803,0.0092 Q0.5803,0.008,0.5798,0.0069 T0.5783,0.0052 T0.5762,0.0044 T0.5736,0.0042 Z M0.5675,0.0143 L0.5675,0.1158 L0.4377,0.1158 L0.4377,0.0143 L0.5675,0.0143 Z M0.5736,0.0042 L0.431,0.0042 Q0.4284,0.0042,0.4264,0.0057 T0.4243,0.0092 L0.4243,0.1208 Q0.4243,0.1221,0.4251,0.1231 T0.4269,0.1248 T0.4289,0.1256 T0.431,0.1258 L0.5736,0.1258 Q0.5767,0.1258,0.5785,0.1244 T0.5803,0.1208 L0.5803,0.0092 Q0.5803,0.008,0.5798,0.0069 T0.5783,0.0052 T0.5762,0.0044 T0.5736,0.0042 Z M0.7827,0.0092 L0.6395,0.0092 L0.6395,0.1208 L0.7827,0.1208 L0.7827,0.0092 Z M0.7827,0.0092 L0.6395,0.0092 L0.6395,0.1208 L0.7827,0.1208 L0.7827,0.0092 Z M0.7827,0.0092 L0.6395,0.0092 L0.6395,0.1208 L0.7827,0.1208 L0.7827,0.0092 Z M1,0.0092 L0.8574,0.0092 L0.8574,0.1208 L1,0.1208 L1,0.0092 Z M1,0.0092 L0.8574,0.0092 L0.8574,0.1208 L1,0.1208 L1,0.0092 Z M1,0.0092 L0.8574,0.0092 L0.8574,0.1208 L1,0.1208 L1,0.0092 Z M0.1432,0.3217 L0.1432,0.4098 L0.0309,0.4098 L0.0309,0.3217 L0.1432,0.3217 Z M0.1586,0.2978 L0.0154,0.2978 Q0.0088,0.2978,0.0044,0.3016 T0,0.31 L0,0.4216 Q0,0.427,0.0049,0.4304 T0.0154,0.4337 L0.1586,0.4337 Q0.1648,0.4337,0.1694,0.4299 T0.174,0.4216 L0.174,0.31 Q0.174,0.3062,0.1715,0.3035 T0.1653,0.2993 T0.1586,0.2978 Z M0.1432,0.888 L0.1432,0.9757 L0.0309,0.9757 L0.0309,0.888 L0.1432,0.888 Z M0.1586,0.8637 L0.0154,0.8637 Q0.0108,0.8637,0.0072,0.8658 T0.0018,0.8706 T0,0.8758 L0,0.9878 Q0,0.9929,0.0049,0.9964 T0.0154,1 L0.1586,1 Q0.1648,1,0.1694,0.996 T0.174,0.9878 L0.174,0.8758 Q0.174,0.8725,0.1715,0.8695 T0.1653,0.8651 T0.1586,0.8637 Z M0.3496,0.169 L0.3496,0.2706 L0.2194,0.2706 L0.2194,0.169 L0.3496,0.169 Z M0.3563,0.159 L0.2132,0.159 Q0.2106,0.159,0.2088,0.1604 T0.207,0.164 L0.207,0.2756 Q0.207,0.2781,0.2091,0.2796 T0.2132,0.281 L0.3563,0.281 Q0.3589,0.281,0.3607,0.2794 T0.3625,0.2756 L0.3625,0.164 Q0.3625,0.1619,0.3605,0.1604 T0.3563,0.159 Z M0.3368,0.6044 L0.3368,0.6858 L0.2327,0.6858 L0.2327,0.6044 L0.3368,0.6044 Z M0.3563,0.5742 L0.2132,0.5742 Q0.2049,0.5742,0.1993,0.5791 T0.1936,0.5893 L0.1936,0.7013 Q0.1936,0.7076,0.2001,0.712 T0.2132,0.7164 L0.3563,0.7164 Q0.3641,0.7164,0.3697,0.7114 T0.3754,0.7013 L0.3754,0.5893 Q0.3754,0.5847,0.3723,0.5812 T0.3648,0.5759 T0.3563,0.5742 Z M0.5736,0.164 L0.431,0.164 L0.431,0.2756 L0.5736,0.2756 L0.5736,0.164 Z M0.5675,0.315 L0.5675,0.4165 L0.4377,0.4165 L0.4377,0.315 L0.5675,0.315 Z M0.5736,0.3049 L0.431,0.3049 Q0.4284,0.3049,0.4264,0.3064 T0.4243,0.31 L0.4243,0.4216 Q0.4243,0.4237,0.4266,0.4251 T0.431,0.4266 L0.5736,0.4266 Q0.5767,0.4266,0.5785,0.4251 T0.5803,0.4216 L0.5803,0.31 Q0.5803,0.3087,0.5798,0.3077 T0.5783,0.306 T0.5762,0.3052 T0.5736,0.3049 Z M0.5736,0.4476 L0.431,0.4476 L0.431,0.5596 L0.5736,0.5596 L0.5736,0.4476 Z M0.5613,0.7408 L0.5613,0.8326 L0.4439,0.8326 L0.4439,0.7408 L0.5613,0.7408 Z M0.5736,0.7206 L0.431,0.7206 Q0.4253,0.7206,0.4217,0.724 T0.4181,0.7307 L0.4181,0.8427 Q0.4181,0.8469,0.4222,0.8498 T0.431,0.8528 L0.5736,0.8528 Q0.5778,0.8528,0.5808,0.8511 T0.5855,0.8471 T0.587,0.8427 L0.587,0.7307 Q0.587,0.7265,0.5826,0.7236 T0.5736,0.7206 Z M0.7827,0.164 L0.6395,0.164 L0.6395,0.2756 L0.7827,0.2756 L0.7827,0.164 Z M0.7827,0.31 L0.6395,0.31 L0.6395,0.4216 L0.7827,0.4216 L0.7827,0.31 Z M0.7827,0.5893 L0.6395,0.5893 L0.6395,0.7013 L0.7827,0.7013 L0.7827,0.5893 Z M0.7827,0.7307 L0.6395,0.7307 L0.6395,0.8427 L0.7827,0.8427 L0.7827,0.7307 Z M0.7827,0.8758 L0.6395,0.8758 L0.6395,0.9878 L0.7827,0.9878 L0.7827,0.8758 Z M1,0.164 L0.8574,0.164 L0.8574,0.2756 L1,0.2756 L1,0.164 Z M1,0.31 L0.8574,0.31 L0.8574,0.4216 L1,0.4216 L1,0.31 Z M1,0.4476 L0.8574,0.4476 L0.8574,0.5596 L1,0.5596 L1,0.4476 Z M1,0.5893 L0.8574,0.5893 L0.8574,0.7013 L1,0.7013 L1,0.5893 Z M1,0.7307 L0.8574,0.7307 L0.8574,0.8427 L1,0.8427 L1,0.7307 Z M1,0.8758 L0.8574,0.8758 L0.8574,0.9878 L1,0.9878 L1,0.8758 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0C8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.0008,0.9979 Q0.0008,1,0.0032,1 Q0.0482,1,0.0602,0.9975 Q0.0667,0.9963,0.0847,0.9917 T0.1084,0.9859 Q0.1124,0.9851,0.1221,0.9851 L0.1253,0.9851 L0.1285,0.9851 Q0.1518,0.9851,0.1655,0.9809 Q0.1695,0.9801,0.1731,0.9788 T0.1791,0.9766 T0.1839,0.9747 T0.1888,0.9732 L0.1936,0.972 T0.1988,0.9712 T0.2048,0.971 L0.2096,0.971 Q0.2201,0.9714,0.2233,0.9714 T0.2345,0.9693 Q0.2386,0.9685,0.2502,0.9674 T0.2715,0.966 L0.2811,0.9656 Q0.2916,0.9647,0.2916,0.9573 Q0.2916,0.954,0.2892,0.9515 T0.2819,0.9486 Q0.2755,0.9482,0.2683,0.9482 Q0.2643,0.9482,0.2582,0.9484 T0.2498,0.9486 Q0.2426,0.9486,0.2402,0.9473 Q0.2386,0.9469,0.2317,0.943 T0.2161,0.937 Q0.2104,0.9357,0.1992,0.933 T0.1831,0.9291 Q0.1807,0.9287,0.1783,0.9285 T0.1739,0.928 T0.1703,0.9276 T0.1675,0.9272 T0.1655,0.9266 T0.1639,0.9253 Q0.1598,0.9216,0.1462,0.9216 L0.143,0.9216 L0.1398,0.9216 L0.1365,0.9216 Q0.1197,0.9195,0.1197,0.9154 L0.1197,0.9154 Q0.1205,0.9121,0.1373,0.9088 T0.1703,0.9054 Q0.1791,0.9054,0.1863,0.904 T0.1984,0.9011 T0.2064,0.8992 Q0.2402,0.8967,0.2506,0.8934 Q0.2538,0.8926,0.2602,0.8899 T0.2707,0.8872 Q0.2715,0.8872,0.2731,0.8876 Q0.2795,0.8884,0.2884,0.8884 Q0.3068,0.8884,0.3092,0.8826 L0.3092,0.8797 Q0.3092,0.8772,0.3076,0.8747 T0.3044,0.8706 T0.3028,0.8681 Q0.3076,0.8656,0.3237,0.8656 Q0.3317,0.8656,0.3622,0.8623 Q0.3655,0.8623,0.3867,0.8617 T0.4217,0.8602 Q0.4241,0.8598,0.4289,0.8598 L0.4345,0.8598 L0.441,0.8598 Q0.4578,0.8598,0.4667,0.8586 Q0.4739,0.8573,0.4851,0.8567 T0.5004,0.8552 Q0.5012,0.8552,0.5108,0.855 T0.5269,0.853 T0.5333,0.8461 Q0.5333,0.8445,0.5325,0.8432 T0.5309,0.8409 T0.5289,0.8391 T0.5261,0.8378 L0.5229,0.837 T0.5185,0.8362 T0.5133,0.8355 T0.5076,0.8349 T0.502,0.8341 Q0.4932,0.8324,0.4859,0.8324 Q0.4827,0.8324,0.4787,0.8328 T0.4731,0.8333 Q0.4707,0.8333,0.4683,0.8324 Q0.4482,0.825,0.4394,0.825 Q0.4369,0.825,0.4345,0.8254 Q0.4225,0.827,0.4153,0.827 Q0.4088,0.827,0.4064,0.8258 Q0.3976,0.8204,0.3751,0.8204 Q0.3703,0.8204,0.3667,0.8214 T0.3622,0.8225 Q0.3598,0.8225,0.3558,0.82 Q0.351,0.8175,0.3402,0.8167 T0.3177,0.8156 T0.3028,0.8146 L0.2956,0.8121 T0.2871,0.8094 T0.2771,0.8073 T0.2635,0.8063 Q0.261,0.8063,0.2586,0.8067 Q0.2378,0.8071,0.2273,0.8071 Q0.2072,0.8071,0.2024,0.8051 Q0.1976,0.8034,0.1932,0.8026 T0.1867,0.8013 T0.1847,0.8001 L0.1847,0.7993 Q0.1855,0.7984,0.1855,0.7978 T0.1859,0.7966 T0.1867,0.7959 T0.188,0.7955 T0.1896,0.7951 T0.1916,0.7949 T0.1944,0.7945 T0.1984,0.7941 T0.2032,0.7939 Q0.212,0.7926,0.2325,0.7918 T0.2562,0.7905 Q0.2691,0.7893,0.2787,0.7847 Q0.2835,0.7827,0.2968,0.7791 T0.3149,0.7756 Q0.3189,0.7756,0.3305,0.7733 T0.3446,0.771 L0.3454,0.771 Q0.3462,0.771,0.3498,0.7721 T0.3574,0.774 T0.3655,0.7748 Q0.3703,0.7748,0.3743,0.7731 T0.3823,0.7708 T0.3916,0.77 T0.3992,0.7694 Q0.4008,0.7694,0.4076,0.7688 T0.4205,0.7675 L0.4305,0.7665 Q0.4345,0.7657,0.4402,0.7617 T0.4458,0.7545 Q0.4458,0.7503,0.4353,0.7487 Q0.4297,0.7478,0.4209,0.7462 T0.4044,0.7433 T0.3904,0.7416 L0.3847,0.7416 Q0.3807,0.7416,0.3747,0.7422 T0.3663,0.7428 T0.3606,0.7424 Q0.3582,0.742,0.3562,0.7418 T0.3522,0.7412 T0.3482,0.7406 T0.3446,0.7401 T0.341,0.7397 T0.3373,0.7393 T0.3341,0.7391 L0.3293,0.7391 Q0.3277,0.7395,0.3253,0.7395 Q0.3205,0.7395,0.3052,0.7387 T0.2835,0.7379 Q0.2755,0.7379,0.253,0.7325 Q0.2506,0.7316,0.2462,0.731 T0.2382,0.7296 T0.2305,0.7271 T0.2181,0.7234 T0.2048,0.7196 T0.2,0.7155 Q0.2,0.7142,0.2016,0.713 Q0.208,0.7059,0.2285,0.6978 T0.2618,0.6898 Q0.2739,0.6898,0.2831,0.6848 T0.2956,0.6794 Q0.3028,0.6786,0.3289,0.6765 T0.3566,0.674 Q0.3582,0.674,0.3855,0.6723 T0.4406,0.6688 T0.4755,0.6653 Q0.5004,0.6607,0.5124,0.6599 L0.5165,0.6599 Q0.5213,0.6599,0.5277,0.6605 T0.5373,0.6611 Q0.543,0.6611,0.5494,0.6599 Q0.592,0.6524,0.6,0.6524 Q0.6064,0.6524,0.61,0.6499 T0.6161,0.6474 Q0.6177,0.6474,0.6261,0.6495 T0.6418,0.6516 L0.6482,0.6516 Q0.653,0.6512,0.6651,0.6506 T0.6876,0.6493 T0.7084,0.6477 T0.7229,0.645 Q0.7269,0.6437,0.7333,0.6437 Q0.7373,0.6437,0.7458,0.6443 T0.7574,0.645 Q0.7614,0.645,0.7631,0.6445 T0.7655,0.6441 Q0.7671,0.6441,0.7723,0.6448 T0.7823,0.6454 Q0.788,0.6454,0.7952,0.6445 Q0.8233,0.6408,0.8723,0.6367 Q0.9012,0.6342,0.9092,0.6342 L0.9165,0.6342 L0.9205,0.6342 Q0.9277,0.6342,0.9382,0.6323 T0.9526,0.6304 Q0.9534,0.6304,0.9594,0.6307 T0.9695,0.6309 Q0.9976,0.6309,0.9992,0.6234 Q1,0.6201,1,0.618 Q1,0.6089,0.9775,0.608 Q0.9703,0.6076,0.9582,0.6076 Q0.9518,0.6076,0.9394,0.6078 T0.9213,0.608 Q0.9036,0.608,0.8972,0.6072 Q0.894,0.6068,0.8871,0.6058 T0.8751,0.6039 T0.8627,0.6024 T0.8506,0.6018 L0.8474,0.6018 Q0.841,0.6022,0.8281,0.6041 T0.8112,0.606 Q0.8096,0.606,0.808,0.6056 Q0.7783,0.6002,0.7647,0.6002 Q0.7598,0.6002,0.7454,0.6006 T0.7261,0.601 Q0.7221,0.601,0.7145,0.6022 T0.7036,0.6035 Q0.6996,0.6035,0.6964,0.6022 Q0.6723,0.5902,0.6442,0.5902 Q0.6426,0.5902,0.6386,0.5904 T0.6337,0.5906 Q0.6273,0.5906,0.6249,0.5894 L0.6177,0.5856 T0.6116,0.5838 T0.6016,0.5825 T0.5855,0.5823 Q0.5719,0.5823,0.5574,0.5798 T0.5373,0.5774 L0.5341,0.5774 Q0.5293,0.5778,0.5213,0.5786 T0.51,0.5794 Q0.5036,0.5794,0.4988,0.5769 Q0.4932,0.574,0.4771,0.574 Q0.4723,0.574,0.4631,0.5745 T0.4498,0.5749 Q0.4418,0.5749,0.4394,0.5736 Q0.4353,0.5724,0.4305,0.5715 T0.4225,0.5707 T0.4145,0.5705 T0.4056,0.5699 Q0.4008,0.5691,0.398,0.5689 T0.394,0.5689 T0.3908,0.5686 T0.3855,0.5674 Q0.3831,0.567,0.3811,0.5662 T0.3779,0.5645 T0.3743,0.563 T0.3695,0.5624 Q0.3655,0.5624,0.3594,0.5635 T0.3494,0.5645 T0.3406,0.5635 T0.3325,0.5624 Q0.3309,0.5624,0.3301,0.5628 L0.3141,0.5628 Q0.2635,0.5628,0.2506,0.5612 Q0.2474,0.5608,0.2434,0.5608 Q0.2402,0.5608,0.2349,0.561 T0.2273,0.5612 Q0.2209,0.5612,0.2177,0.5595 Q0.2145,0.557,0.2145,0.5558 Q0.2169,0.5537,0.2345,0.5521 Q0.2876,0.5475,0.3028,0.5425 Q0.3068,0.5413,0.3149,0.5396 T0.3309,0.5367 T0.3478,0.5338 T0.3606,0.5309 Q0.3631,0.5305,0.3755,0.5259 T0.3968,0.5214 Q0.3992,0.5214,0.4024,0.5218 T0.4096,0.5222 Q0.4161,0.5222,0.4213,0.5209 T0.4313,0.5185 T0.4402,0.5172 Q0.4418,0.5168,0.447,0.5168 L0.4558,0.5168 T0.4639,0.5162 T0.4707,0.5141 T0.4731,0.5102 Q0.4731,0.5089,0.4723,0.5081 Q0.4699,0.5019,0.4627,0.4994 T0.4369,0.4965 Q0.4273,0.4961,0.4193,0.4946 T0.4048,0.4915 T0.3952,0.4894 Q0.392,0.4886,0.388,0.4886 Q0.3847,0.4886,0.3775,0.4892 T0.3647,0.4898 Q0.3598,0.4898,0.355,0.4894 Q0.3165,0.4861,0.3141,0.4853 Q0.2779,0.4774,0.2715,0.4774 Q0.2699,0.4774,0.2683,0.4776 T0.2627,0.4778 Q0.2498,0.4778,0.2281,0.4762 Q0.2241,0.4749,0.2233,0.4737 Q0.2249,0.472,0.2321,0.4708 Q0.2402,0.4695,0.2554,0.4695 L0.2675,0.4695 L0.2795,0.4695 Q0.294,0.4695,0.2996,0.4679 Q0.3141,0.4629,0.3373,0.4616 Q0.3414,0.4612,0.3466,0.4606 T0.3582,0.4589 T0.3687,0.4562 T0.3727,0.4525 Q0.3727,0.4509,0.3703,0.4492 Q0.3639,0.4455,0.3474,0.4419 T0.3213,0.4384 L0.3157,0.4384 L0.31,0.4384 Q0.294,0.4384,0.2859,0.4363 Q0.2795,0.4347,0.2691,0.4347 L0.2618,0.4347 L0.2546,0.4347 Q0.2458,0.4347,0.2394,0.4338 Q0.2257,0.4318,0.2193,0.4303 T0.2092,0.4266 T0.2052,0.4224 T0.2024,0.4154 T0.196,0.4052 Q0.1912,0.3994,0.1867,0.3949 T0.1807,0.3884 T0.1791,0.3849 Q0.1791,0.3824,0.1839,0.3745 Q0.1871,0.3687,0.1944,0.3637 T0.2072,0.3561 T0.2221,0.3501 T0.2321,0.3463 Q0.2353,0.3455,0.241,0.3445 T0.2522,0.3428 T0.2639,0.3411 T0.2731,0.3384 T0.2956,0.3351 T0.3197,0.3326 Q0.3213,0.3322,0.3237,0.3322 T0.3325,0.3331 T0.3414,0.3339 Q0.3478,0.3339,0.3518,0.3314 Q0.3606,0.3264,0.3703,0.3264 Q0.3775,0.3264,0.3847,0.3293 Q0.3888,0.331,0.396,0.331 Q0.4024,0.331,0.4116,0.3295 T0.4265,0.3266 T0.4414,0.3227 T0.4514,0.3202 Q0.4578,0.3181,0.4839,0.3152 T0.5112,0.3123 T0.5177,0.3119 T0.5285,0.3111 T0.5398,0.3096 T0.549,0.3065 T0.5526,0.3015 Q0.5526,0.2999,0.5518,0.2982 Q0.547,0.2903,0.5386,0.2883 T0.5165,0.2862 Q0.5116,0.2862,0.5056,0.2866 T0.4972,0.287 Q0.4884,0.287,0.4787,0.2833 Q0.4594,0.2758,0.4265,0.2758 Q0.4169,0.2758,0.4129,0.2766 Q0.4112,0.2771,0.4104,0.2771 Q0.4088,0.2771,0.4056,0.2748 T0.3968,0.2721 Q0.3695,0.2696,0.359,0.2659 Q0.355,0.2646,0.351,0.2646 T0.3426,0.2661 T0.3365,0.2675 T0.3333,0.2659 Q0.3277,0.2605,0.3116,0.2553 T0.2859,0.2501 L0.2843,0.2501 L0.2819,0.2501 Q0.2779,0.2501,0.2723,0.2493 T0.2586,0.2472 T0.2474,0.2455 Q0.1799,0.236,0.1719,0.236 Q0.1663,0.236,0.1606,0.235 T0.1502,0.2316 T0.1454,0.2265 Q0.1454,0.2223,0.1558,0.2173 Q0.1679,0.2115,0.1859,0.2092 T0.2185,0.207 T0.2353,0.2057 Q0.2369,0.2049,0.2502,0.2041 T0.2799,0.2022 T0.302,0.2007 Q0.3084,0.1999,0.3245,0.1954 T0.347,0.1908 Q0.3502,0.1908,0.3534,0.1916 Q0.3663,0.197,0.388,0.197 Q0.404,0.197,0.4088,0.1929 Q0.4096,0.192,0.4108,0.192 T0.4177,0.1945 T0.4277,0.197 T0.4369,0.1949 Q0.4442,0.1908,0.4843,0.1875 Q0.4892,0.1871,0.4968,0.1839 T0.5084,0.1808 L0.5108,0.1808 Q0.5213,0.1825,0.5454,0.1825 Q0.5823,0.1825,0.5992,0.1779 Q0.6096,0.1746,0.6217,0.1746 Q0.6249,0.1746,0.6309,0.1748 T0.6394,0.175 Q0.6434,0.175,0.6466,0.1746 Q0.6474,0.1746,0.6594,0.1738 T0.6787,0.1713 T0.6892,0.1659 Q0.6924,0.1634,0.6924,0.1593 Q0.6924,0.1547,0.6888,0.151 T0.6795,0.1472 Q0.6779,0.1472,0.6763,0.1477 Q0.6699,0.1493,0.6667,0.1493 Q0.661,0.1493,0.6506,0.1456 Q0.6329,0.1394,0.6129,0.1394 Q0.6048,0.1394,0.5992,0.1377 T0.5888,0.136 T0.5803,0.1377 T0.5751,0.1394 Q0.5711,0.1394,0.5639,0.1369 Q0.5365,0.1273,0.5116,0.1273 Q0.4876,0.1273,0.4667,0.1253 Q0.4627,0.1248,0.4542,0.1211 T0.4337,0.1174 L0.4305,0.1174 Q0.4217,0.1178,0.4153,0.119 T0.408,0.1203 Q0.4056,0.1203,0.4,0.117 Q0.3976,0.1153,0.396,0.1136 T0.3932,0.1112 T0.39,0.1099 T0.3847,0.1095 Q0.3799,0.1095,0.3687,0.1103 Q0.355,0.1112,0.3434,0.1141 T0.3301,0.117 Q0.3261,0.117,0.3229,0.1145 Q0.3181,0.1116,0.3052,0.1087 T0.2787,0.1043 T0.2546,0.1029 Q0.2506,0.1029,0.2474,0.1033 Q0.2418,0.1037,0.2349,0.1054 T0.2249,0.107 Q0.2225,0.107,0.2193,0.1066 Q0.2024,0.1024,0.1863,0.1024 Q0.1823,0.1024,0.1562,0.0971 T0.1269,0.0917 T0.1161,0.0896 T0.106,0.0854 Q0.1076,0.0846,0.11,0.0842 Q0.1173,0.0821,0.1261,0.0821 Q0.1301,0.0821,0.1369,0.0825 T0.1462,0.083 Q0.1494,0.083,0.151,0.0825 T0.1831,0.0813 T0.2201,0.0784 T0.243,0.074 T0.2667,0.0718 Q0.2707,0.0718,0.2755,0.0724 T0.2843,0.073 T0.2936,0.0724 T0.3024,0.0705 T0.3072,0.068 T0.3092,0.0657 T0.3108,0.0647 Q0.3116,0.0643,0.3133,0.0643 L0.3157,0.0643 L0.3181,0.0643 Q0.3446,0.0643,0.3446,0.0556 T0.3205,0.0469 L0.3189,0.0469 Q0.3157,0.0469,0.3157,0.0465 Q0.3084,0.0427,0.2835,0.0386 Q0.2739,0.0369,0.2675,0.0342 T0.2538,0.0299 Q0.249,0.029,0.245,0.029 T0.2378,0.0303 T0.2321,0.0315 Q0.2305,0.0315,0.2273,0.0307 Q0.2201,0.0286,0.2004,0.0272 T0.1775,0.0245 Q0.1735,0.0228,0.1663,0.0226 T0.1522,0.0197 T0.1345,0.0145 T0.1124,0.0079 Q0.0996,0.0033,0.0671,0.0044 T0.0297,0.0041 Q0.0169,0,0.0024,0.0017 Q0,0.0021,0,0.0037 Z M0.0064,0.9954 L0.0056,0.0058 Q0.0177,0.005,0.0281,0.0083 Q0.0345,0.01,0.0675,0.0089 T0.1108,0.012 Q0.1221,0.0162,0.1329,0.0187 T0.1498,0.0236 T0.1606,0.0268 T0.1691,0.0274 T0.1759,0.0286 Q0.1807,0.0303,0.2,0.0315 T0.2257,0.0348 Q0.2305,0.0361,0.2337,0.0361 Q0.2361,0.0361,0.2398,0.0348 T0.2466,0.0336 Q0.249,0.0336,0.2522,0.034 Q0.2586,0.0353,0.2655,0.0382 T0.2827,0.0427 Q0.3068,0.0469,0.3124,0.0502 Q0.3157,0.0523,0.3237,0.0523 L0.3277,0.0523 L0.3309,0.0523 Q0.3398,0.0523,0.3398,0.0556 Q0.3398,0.0581,0.3341,0.0587 T0.3205,0.0593 T0.3092,0.0603 T0.3036,0.0643 T0.2932,0.068 Q0.2884,0.0684,0.2843,0.0684 Q0.2811,0.0684,0.2763,0.0678 T0.2667,0.0672 Q0.2586,0.0672,0.2422,0.0697 T0.2177,0.0747 Q0.2137,0.0759,0.1831,0.0767 T0.1494,0.0784 L0.1474,0.0784 T0.1398,0.078 T0.1285,0.0776 Q0.1173,0.0776,0.1084,0.08 Q0.1004,0.0817,0.1004,0.0846 Q0.1004,0.0867,0.104,0.0888 T0.1124,0.0923 T0.1213,0.0948 T0.1269,0.0958 T0.155,0.1014 T0.1863,0.107 Q0.2016,0.107,0.2185,0.1107 Q0.2217,0.1116,0.2257,0.1116 Q0.2289,0.1116,0.2365,0.1099 T0.2482,0.1074 L0.2538,0.1074 Q0.2683,0.1074,0.2912,0.1107 T0.3197,0.1182 Q0.3245,0.1215,0.3301,0.1215 Q0.3333,0.1215,0.3454,0.1184 T0.3687,0.1145 Q0.3735,0.1141,0.3779,0.1139 T0.3843,0.1134 T0.3871,0.1132 Q0.3896,0.1132,0.39,0.1136 T0.392,0.1161 T0.3968,0.1203 Q0.404,0.1248,0.408,0.1248 Q0.4096,0.1248,0.4165,0.1234 T0.4305,0.1215 L0.4337,0.1215 Q0.4442,0.1215,0.4518,0.1251 T0.4659,0.1294 Q0.4876,0.1319,0.5116,0.1319 T0.5622,0.141 Q0.5703,0.1439,0.5751,0.1439 Q0.5791,0.1439,0.5831,0.1423 T0.5888,0.1406 Q0.592,0.1406,0.5944,0.1412 T0.6012,0.1427 T0.6129,0.1435 Q0.6313,0.1435,0.649,0.1497 Q0.6602,0.1535,0.6667,0.1535 Q0.6715,0.1535,0.6779,0.1522 Q0.6787,0.1518,0.6795,0.1518 Q0.6827,0.1518,0.6847,0.1541 T0.6867,0.1593 Q0.6867,0.1618,0.6851,0.1634 Q0.6835,0.1655,0.6775,0.1667 T0.6598,0.169 T0.6458,0.1705 T0.6402,0.1709 Q0.6378,0.1709,0.6321,0.1705 T0.6225,0.1701 Q0.6096,0.1701,0.5968,0.1738 Q0.5823,0.1779,0.5446,0.1779 Q0.5205,0.1779,0.5116,0.1767 Q0.51,0.1763,0.5084,0.1763 Q0.5028,0.1763,0.4944,0.1796 T0.4835,0.1829 L0.4771,0.1833 T0.4659,0.1844 L0.4546,0.1858 T0.443,0.1881 T0.4337,0.1912 Q0.4321,0.1916,0.4305,0.1916 Q0.4273,0.1916,0.4209,0.1895 T0.412,0.1875 Q0.408,0.1875,0.4056,0.19 Q0.4024,0.1925,0.388,0.1925 Q0.3663,0.1925,0.3558,0.1879 Q0.3518,0.1862,0.3462,0.1862 Q0.3373,0.1862,0.3213,0.191 T0.3012,0.1962 Q0.2964,0.1966,0.2799,0.1976 T0.2494,0.1997 T0.2313,0.2024 L0.2313,0.2024 L0.2293,0.2024 L0.2261,0.2024 L0.2217,0.2024 Q0.204,0.2024,0.1851,0.2047 T0.1526,0.2136 Q0.1406,0.2198,0.1406,0.226 Q0.1406,0.2319,0.1518,0.2362 T0.1719,0.2406 Q0.1791,0.2406,0.2466,0.2497 Q0.2498,0.2501,0.2574,0.2516 T0.2711,0.2538 T0.2819,0.2547 L0.2851,0.2547 Q0.2851,0.2543,0.2859,0.2543 Q0.294,0.2543,0.3096,0.2592 T0.3285,0.2684 Q0.3317,0.2721,0.3373,0.2721 Q0.3406,0.2721,0.3462,0.2708 T0.3542,0.2696 Q0.3558,0.2696,0.3574,0.27 Q0.3679,0.2737,0.3968,0.2762 Q0.3984,0.2766,0.4004,0.2779 L0.4044,0.2804 T0.4096,0.2816 Q0.412,0.2816,0.4145,0.2808 Q0.4177,0.2804,0.4249,0.2804 Q0.4586,0.2804,0.4763,0.287 Q0.4867,0.2912,0.4972,0.2912 Q0.5004,0.2912,0.5064,0.2908 T0.5165,0.2903 Q0.5293,0.2903,0.5361,0.2922 T0.547,0.3003 L0.547,0.3011 Q0.547,0.3036,0.5414,0.3049 T0.5249,0.3067 T0.51,0.3082 Q0.5092,0.3082,0.4831,0.3111 T0.4498,0.3161 Q0.408,0.3264,0.3952,0.3264 Q0.3896,0.3264,0.3863,0.3252 Q0.3783,0.3223,0.3703,0.3223 Q0.359,0.3223,0.3494,0.3277 Q0.347,0.3293,0.343,0.3293 Q0.3414,0.3293,0.3353,0.3285 T0.3253,0.3277 T0.3181,0.3285 Q0.3149,0.3289,0.2952,0.3306 T0.2699,0.3347 Q0.2675,0.3364,0.2598,0.3372 T0.2434,0.3395 T0.2297,0.3426 L0.2205,0.3461 T0.2048,0.3523 T0.1908,0.3606 T0.1791,0.3725 Q0.1743,0.3803,0.1743,0.3845 Q0.1743,0.3874,0.1763,0.3901 T0.1827,0.3976 T0.1912,0.4073 T0.198,0.4189 T0.2032,0.4276 T0.2137,0.4326 T0.2386,0.438 Q0.2458,0.4392,0.2562,0.4392 L0.2639,0.4392 L0.2699,0.4392 Q0.2795,0.4392,0.2843,0.4405 Q0.2932,0.443,0.3092,0.443 L0.3157,0.443 L0.3213,0.443 Q0.3293,0.443,0.3458,0.4463 T0.3671,0.4525 Q0.3598,0.4558,0.3373,0.4571 Q0.3124,0.4587,0.298,0.4637 Q0.294,0.465,0.2811,0.465 L0.2703,0.465 L0.2586,0.465 Q0.241,0.465,0.2305,0.4666 Q0.2185,0.4687,0.2185,0.4728 Q0.2185,0.4745,0.2205,0.4764 T0.2241,0.4791 L0.2265,0.4803 L0.2273,0.4803 Q0.2506,0.4824,0.261,0.4824 Q0.2675,0.4824,0.2699,0.482 Q0.2699,0.4815,0.2707,0.4815 Q0.2771,0.4815,0.3124,0.4898 Q0.3173,0.4907,0.3229,0.4913 T0.3386,0.4925 T0.3542,0.4936 T0.3663,0.494 Q0.3711,0.494,0.3787,0.4936 T0.3888,0.4932 Q0.392,0.4932,0.3944,0.4936 T0.4032,0.4959 T0.4181,0.499 T0.4369,0.5006 Q0.449,0.501,0.455,0.5021 T0.4639,0.5048 T0.4675,0.5093 L0.4675,0.5097 Q0.4675,0.5106,0.4663,0.511 T0.4622,0.5116 L0.4566,0.512 T0.4486,0.5124 T0.4402,0.5127 Q0.4345,0.5131,0.4257,0.5153 T0.4096,0.5176 Q0.4064,0.5176,0.4024,0.5172 L0.3968,0.5172 Q0.3863,0.5172,0.3731,0.522 T0.359,0.5268 Q0.355,0.528,0.3317,0.5321 T0.3004,0.5388 Q0.2867,0.5433,0.2337,0.5479 Q0.2096,0.55,0.2096,0.5562 Q0.2096,0.5591,0.2145,0.5628 Q0.2193,0.5662,0.2289,0.5662 Q0.2313,0.5662,0.2373,0.5657 T0.2458,0.5653 L0.2498,0.5653 Q0.2635,0.5674,0.3124,0.5674 Q0.3261,0.5674,0.3301,0.567 L0.3317,0.567 Q0.3341,0.567,0.3394,0.568 T0.3486,0.5691 Q0.3542,0.5691,0.3606,0.5678 T0.3699,0.5666 T0.3759,0.5686 T0.3839,0.5718 T0.3908,0.573 T0.3964,0.5734 T0.4048,0.5742 T0.4165,0.5751 T0.4277,0.5757 T0.4369,0.5778 Q0.4426,0.5794,0.4538,0.5794 Q0.4586,0.5794,0.4679,0.5792 T0.4811,0.579 Q0.4924,0.579,0.4956,0.5807 Q0.502,0.584,0.5108,0.584 L0.5137,0.584 T0.5173,0.5838 L0.5205,0.5834 L0.5237,0.583 L0.5269,0.5825 T0.5305,0.5821 T0.5341,0.5819 L0.5365,0.5819 Q0.5414,0.5819,0.5562,0.5842 T0.5855,0.5865 Q0.5912,0.5865,0.5948,0.5867 T0.6012,0.5869 T0.6064,0.5873 T0.61,0.5877 T0.6133,0.5886 T0.6157,0.5896 L0.6185,0.591 T0.6217,0.5931 Q0.6265,0.5952,0.6369,0.5952 L0.6418,0.5952 L0.6466,0.5952 Q0.6723,0.5952,0.6932,0.606 Q0.698,0.608,0.7044,0.608 Q0.7084,0.608,0.7161,0.6068 T0.7261,0.6056 Q0.7309,0.6056,0.7454,0.6051 T0.7647,0.6047 Q0.7775,0.6047,0.8072,0.6101 Q0.8096,0.6105,0.812,0.6105 Q0.8177,0.6105,0.8297,0.6085 T0.8474,0.606 L0.8506,0.606 Q0.8562,0.606,0.8647,0.607 T0.8831,0.6095 T0.8964,0.6118 Q0.9036,0.6122,0.9221,0.6122 L0.9406,0.6122 L0.959,0.6122 L0.9767,0.6122 Q0.9952,0.613,0.9952,0.6172 Q0.9952,0.6176,0.9944,0.6221 Q0.9936,0.6259,0.9574,0.6259 L0.955,0.6259 L0.9526,0.6259 Q0.9478,0.6259,0.9373,0.628 T0.9205,0.63 L0.9173,0.63 Q0.9133,0.6296,0.9084,0.6296 Q0.9004,0.6296,0.8715,0.6321 Q0.8233,0.6367,0.7944,0.64 Q0.7888,0.6408,0.7839,0.6408 T0.7739,0.6402 T0.7671,0.6396 Q0.7639,0.6396,0.7622,0.64 Q0.7614,0.6404,0.759,0.6404 Q0.7574,0.6404,0.749,0.6398 T0.7357,0.6392 Q0.7261,0.6392,0.7213,0.6412 Q0.7173,0.6425,0.7032,0.6437 T0.6711,0.6458 T0.6474,0.647 Q0.645,0.6474,0.6418,0.6474 Q0.6353,0.6474,0.6277,0.6454 T0.6161,0.6433 Q0.6104,0.6433,0.6072,0.6456 T0.6,0.6479 Q0.5912,0.6479,0.5486,0.6557 Q0.5422,0.657,0.5373,0.657 Q0.5349,0.657,0.5289,0.6564 T0.5173,0.6557 L0.5124,0.6557 Q0.4996,0.6562,0.4739,0.6611 Q0.4675,0.6624,0.4398,0.6645 T0.3851,0.668 T0.3558,0.6698 Q0.355,0.6698,0.3285,0.6721 T0.2948,0.6752 Q0.2892,0.6761,0.2843,0.6786 T0.2747,0.6831 T0.2618,0.6852 Q0.253,0.6852,0.2394,0.6893 T0.2141,0.6993 T0.1968,0.7105 Q0.1944,0.713,0.1944,0.7155 Q0.1944,0.7196,0.2012,0.7229 T0.2177,0.7285 T0.2281,0.7308 Q0.2321,0.7329,0.2365,0.7339 T0.245,0.7356 T0.2514,0.7366 Q0.2522,0.737,0.2554,0.7379 T0.2614,0.7393 T0.2679,0.7406 T0.2755,0.7416 T0.2835,0.742 Q0.29,0.742,0.3048,0.7428 T0.3253,0.7437 L0.3301,0.7437 L0.3333,0.7437 Q0.3341,0.7437,0.3598,0.7466 Q0.3639,0.7474,0.3671,0.7474 T0.3759,0.7466 T0.3855,0.7457 Q0.388,0.7457,0.3904,0.7462 Q0.3984,0.7466,0.4137,0.7495 T0.4349,0.7532 T0.441,0.7557 Q0.441,0.7599,0.4289,0.7619 Q0.4257,0.7628,0.4197,0.7634 T0.4072,0.7644 T0.3984,0.7652 Q0.3968,0.7652,0.3912,0.7655 T0.3811,0.7665 T0.3719,0.7694 Q0.3695,0.7702,0.3655,0.7702 T0.3546,0.7686 T0.3462,0.7665 L0.3446,0.7665 Q0.3406,0.7665,0.3293,0.769 T0.3149,0.7715 Q0.3084,0.7715,0.2952,0.7748 T0.2763,0.781 Q0.2675,0.7847,0.2554,0.7864 Q0.253,0.7864,0.2325,0.7874 T0.2024,0.7893 Q0.2008,0.7893,0.1984,0.7897 T0.1948,0.7901 T0.1916,0.7903 T0.1888,0.7908 T0.1867,0.7914 T0.1847,0.7922 L0.1831,0.793 T0.1819,0.7943 L0.1811,0.7959 T0.1799,0.7976 Q0.1791,0.7993,0.1791,0.8001 Q0.1791,0.8034,0.1831,0.8051 T0.1932,0.8078 T0.2,0.8092 Q0.2072,0.8117,0.2257,0.8117 Q0.2337,0.8117,0.2586,0.8109 L0.2635,0.8109 Q0.2707,0.8109,0.2767,0.8117 T0.2859,0.8136 T0.2936,0.8163 T0.3012,0.8187 Q0.3068,0.82,0.3277,0.8206 T0.3526,0.8237 Q0.3582,0.8266,0.3622,0.8266 Q0.3647,0.8266,0.3687,0.8258 T0.3751,0.825 Q0.396,0.825,0.404,0.8291 Q0.408,0.8316,0.4161,0.8316 Q0.4217,0.8316,0.4353,0.8295 L0.4394,0.8295 Q0.4466,0.8295,0.4659,0.8366 Q0.4699,0.8378,0.4747,0.8378 Q0.4763,0.8378,0.4807,0.8374 T0.4884,0.837 Q0.494,0.837,0.5004,0.8382 Q0.5052,0.8391,0.5124,0.8399 Q0.5221,0.8411,0.5249,0.8422 T0.5277,0.8461 Q0.5277,0.8482,0.5233,0.849 T0.5112,0.8501 T0.4996,0.8511 Q0.4964,0.8519,0.4851,0.8523 T0.4659,0.8544 Q0.4578,0.8557,0.4402,0.8557 L0.4349,0.8557 L0.4297,0.8557 L0.4209,0.8557 Q0.408,0.8565,0.3867,0.8571 T0.3614,0.8582 Q0.3317,0.8611,0.3237,0.8611 L0.3225,0.8611 L0.3213,0.8611 Q0.2972,0.8611,0.2972,0.8681 Q0.2972,0.8714,0.3008,0.8747 T0.3044,0.8805 Q0.3044,0.881,0.3036,0.8814 Q0.3012,0.8839,0.2851,0.8839 Q0.2779,0.8839,0.2739,0.883 Q0.2723,0.8826,0.2699,0.8826 T0.2643,0.8835 T0.259,0.8853 T0.2542,0.8876 T0.249,0.8893 Q0.2386,0.8922,0.2056,0.8946 Q0.2016,0.8951,0.1916,0.898 T0.1703,0.9009 Q0.1518,0.9009,0.1337,0.9044 T0.1149,0.9146 L0.1149,0.9158 Q0.1149,0.9175,0.1157,0.9187 T0.1173,0.921 T0.1201,0.9226 T0.1233,0.9237 T0.1269,0.9245 L0.1305,0.9251 T0.1337,0.9255 L0.1357,0.9258 Q0.139,0.9262,0.1446,0.9262 T0.1542,0.9266 T0.1598,0.9282 Q0.1614,0.9303,0.1647,0.9314 T0.1739,0.9328 T0.1815,0.9336 Q0.1863,0.9345,0.1976,0.9372 T0.2145,0.9415 Q0.2225,0.9432,0.2285,0.9467 T0.2378,0.9515 Q0.2426,0.9531,0.2514,0.9531 Q0.2538,0.9531,0.2602,0.9529 T0.2707,0.9527 Q0.2763,0.9527,0.2815,0.9531 T0.2867,0.9569 T0.2811,0.9614 Q0.2434,0.9627,0.2329,0.9652 Q0.2249,0.9672,0.2241,0.9672 L0.2096,0.9668 L0.2056,0.9668 Q0.2,0.9668,0.196,0.9672 T0.1884,0.9687 T0.1815,0.9708 T0.1731,0.9737 T0.1639,0.9772 Q0.1518,0.9805,0.1285,0.9805 L0.1253,0.9805 Q0.1124,0.9805,0.1076,0.9818 Q0.1012,0.983,0.0831,0.9876 T0.0594,0.9934 Q0.049,0.9954,0.0064,0.9954 Z M0.0032,0.9979 L0.0024,0.0037 Q0.0161,0.0021,0.0289,0.0062 Q0.0337,0.0075,0.0671,0.0066 T0.1116,0.01 T0.1337,0.0166 T0.151,0.022 Q0.1582,0.0249,0.1659,0.0251 T0.1767,0.0265 Q0.1807,0.0278,0.2,0.0292 T0.2265,0.0328 Q0.2305,0.034,0.2329,0.034 T0.239,0.0326 T0.2458,0.0311 T0.253,0.0319 Q0.2578,0.0332,0.2614,0.0342 T0.2667,0.0363 T0.2723,0.0384 T0.2827,0.0406 Q0.3084,0.0448,0.3141,0.0485 Q0.3149,0.0494,0.3225,0.0494 T0.3361,0.0504 T0.3422,0.0554 T0.3357,0.0606 T0.3205,0.0618 T0.31,0.0626 Q0.3092,0.0626,0.3056,0.0659 T0.2932,0.0701 Q0.2884,0.0709,0.2843,0.0709 Q0.2811,0.0709,0.2759,0.0701 T0.2667,0.0693 Q0.2586,0.0693,0.2422,0.0718 T0.2193,0.0763 Q0.2137,0.0784,0.1831,0.079 T0.1502,0.0805 Q0.1494,0.0809,0.147,0.0809 Q0.1454,0.0809,0.1386,0.0803 T0.1277,0.0796 Q0.1173,0.0796,0.1092,0.0821 Q0.1036,0.0834,0.1036,0.0854 Q0.1036,0.0879,0.1129,0.0908 T0.1269,0.0937 Q0.1293,0.0937,0.1558,0.0991 T0.1863,0.1045 Q0.2016,0.1045,0.2193,0.1087 Q0.2225,0.1091,0.2249,0.1091 Q0.2289,0.1091,0.2357,0.1076 T0.2482,0.1054 Q0.2506,0.1049,0.2546,0.1049 Q0.2699,0.1049,0.2924,0.1085 T0.3213,0.1165 Q0.3253,0.119,0.3301,0.119 Q0.3325,0.119,0.3446,0.1161 T0.3687,0.1124 Q0.3831,0.1112,0.3863,0.1112 T0.3908,0.1118 T0.3936,0.1145 T0.3984,0.1186 Q0.4048,0.1228,0.408,0.1228 Q0.4096,0.1228,0.4161,0.1213 T0.4305,0.1195 L0.4337,0.1195 Q0.4426,0.1195,0.4474,0.1211 T0.4566,0.1246 T0.4667,0.1273 Q0.4876,0.1298,0.5116,0.1298 Q0.5365,0.1298,0.5631,0.1389 Q0.5703,0.1414,0.5751,0.1414 Q0.5783,0.1414,0.5819,0.1398 T0.5888,0.1381 Q0.5928,0.1381,0.5984,0.1398 T0.6129,0.1414 Q0.6321,0.1414,0.6498,0.1477 Q0.661,0.1514,0.6667,0.1514 Q0.6707,0.1514,0.6771,0.1501 Q0.6787,0.1497,0.6795,0.1497 Q0.6835,0.1497,0.6863,0.1526 T0.6892,0.1593 Q0.6892,0.1626,0.6876,0.1647 Q0.6851,0.1672,0.6799,0.1686 T0.6695,0.1705 T0.6566,0.1715 T0.6458,0.1725 Q0.6434,0.173,0.6402,0.173 Q0.6378,0.173,0.6317,0.1727 T0.6225,0.1725 Q0.6096,0.1725,0.5984,0.1759 Q0.5823,0.1804,0.5454,0.1804 Q0.5205,0.1804,0.5116,0.1788 L0.5084,0.1788 Q0.5036,0.1788,0.4956,0.1819 T0.4843,0.185 Q0.4434,0.1887,0.4353,0.1929 Q0.4321,0.1945,0.4289,0.1945 T0.4197,0.1922 T0.4112,0.19 T0.4072,0.1912 Q0.4032,0.1949,0.388,0.1949 Q0.3663,0.1949,0.3542,0.19 Q0.351,0.1883,0.3462,0.1883 Q0.3406,0.1883,0.3321,0.1906 T0.3153,0.1954 T0.302,0.1983 Q0.2972,0.1991,0.2859,0.1997 T0.2643,0.201 T0.245,0.2024 T0.2337,0.2041 Q0.2321,0.2045,0.2181,0.2045 T0.1851,0.207 T0.1542,0.2157 Q0.143,0.2211,0.143,0.226 Q0.143,0.2314,0.153,0.2348 T0.1719,0.2381 Q0.1791,0.2381,0.2474,0.2476 Q0.2506,0.248,0.2558,0.2489 T0.2651,0.2505 T0.2739,0.252 T0.2819,0.2526 Q0.2835,0.2526,0.2851,0.2522 L0.2859,0.2522 Q0.2948,0.2522,0.3104,0.2574 T0.3309,0.2671 Q0.3333,0.2696,0.3365,0.2696 Q0.339,0.2696,0.3442,0.2684 T0.3526,0.2671 T0.3582,0.2679 Q0.3687,0.2717,0.3968,0.2742 Q0.4,0.2746,0.4036,0.2771 T0.4096,0.2796 Q0.4112,0.2796,0.4137,0.2787 Q0.4169,0.2779,0.4257,0.2779 Q0.4594,0.2779,0.4779,0.2849 Q0.4876,0.2891,0.4972,0.2891 Q0.4996,0.2891,0.506,0.2887 T0.5165,0.2883 Q0.5253,0.2883,0.5305,0.2889 T0.541,0.292 T0.5494,0.299 L0.5494,0.3015 Q0.5494,0.3036,0.5466,0.3051 T0.5386,0.3073 T0.5285,0.3086 T0.5181,0.3094 T0.5108,0.3102 Q0.5068,0.3107,0.494,0.3121 L0.4683,0.315 T0.4506,0.3181 Q0.449,0.3181,0.4426,0.32 T0.4313,0.3229 T0.4197,0.3256 T0.4064,0.3281 T0.3952,0.3289 Q0.3888,0.3289,0.3855,0.3273 Q0.3783,0.3243,0.3703,0.3243 Q0.3598,0.3243,0.351,0.3297 Q0.3478,0.3314,0.3422,0.3314 Q0.3398,0.3314,0.3337,0.3306 T0.3245,0.3297 T0.3189,0.3306 Q0.3149,0.3314,0.2956,0.3328 T0.2715,0.3368 Q0.2691,0.338,0.2635,0.3391 T0.2522,0.3407 T0.2406,0.3424 T0.2313,0.3447 Q0.2305,0.3447,0.2213,0.348 T0.206,0.354 T0.1924,0.3621 T0.1815,0.3733 Q0.1767,0.3816,0.1767,0.3849 Q0.1767,0.387,0.1783,0.3893 T0.1847,0.3961 T0.1936,0.4063 T0.2004,0.4181 T0.2052,0.4264 T0.2145,0.4309 T0.2386,0.4359 Q0.2458,0.4372,0.2554,0.4372 Q0.2578,0.4372,0.2627,0.437 T0.2699,0.4367 Q0.2795,0.4367,0.2851,0.4384 Q0.294,0.4409,0.3092,0.4409 Q0.3116,0.4409,0.3157,0.4407 T0.3213,0.4405 Q0.3301,0.4405,0.3466,0.444 T0.3687,0.4509 Q0.3703,0.4517,0.3703,0.4525 Q0.3703,0.4575,0.3373,0.4596 Q0.3133,0.4608,0.2988,0.4658 Q0.294,0.4674,0.2803,0.4674 Q0.2763,0.4674,0.2687,0.4672 T0.257,0.467 Q0.2402,0.467,0.2313,0.4687 Q0.2209,0.4708,0.2209,0.4737 Q0.2209,0.4741,0.2213,0.4749 T0.2229,0.4764 L0.2249,0.4774 T0.2265,0.4778 L0.2273,0.4782 Q0.2498,0.4803,0.2618,0.4803 Q0.2667,0.4803,0.2691,0.4799 Q0.2699,0.4795,0.2715,0.4795 Q0.2779,0.4795,0.3133,0.4873 Q0.3181,0.4886,0.3233,0.4892 T0.3386,0.4905 T0.3542,0.4915 T0.3655,0.4919 Q0.3703,0.4919,0.3779,0.4915 T0.3888,0.4911 T0.3952,0.4915 Q0.3976,0.4919,0.4008,0.4927 L0.4072,0.4944 T0.4149,0.4961 T0.4249,0.4975 T0.4369,0.4985 Q0.449,0.499,0.4554,0.5 T0.4651,0.5031 T0.4699,0.5085 Q0.4707,0.5093,0.4707,0.5097 Q0.4707,0.5114,0.4687,0.5124 T0.4631,0.5139 T0.4562,0.5145 T0.4478,0.5147 L0.4402,0.5147 Q0.4353,0.5151,0.4265,0.5174 T0.4096,0.5197 L0.4024,0.5197 Q0.4,0.5193,0.3968,0.5193 Q0.3896,0.5193,0.3831,0.5209 T0.3703,0.5253 T0.3598,0.5288 Q0.3558,0.5301,0.3321,0.5342 T0.302,0.5409 Q0.2876,0.5454,0.2337,0.55 Q0.2112,0.5521,0.2112,0.5558 Q0.2112,0.5574,0.2161,0.5612 Q0.2201,0.5637,0.2281,0.5637 Q0.2305,0.5637,0.2361,0.5633 T0.2442,0.5628 Q0.2474,0.5628,0.2498,0.5633 Q0.2635,0.5653,0.3133,0.5653 Q0.3261,0.5653,0.3301,0.5649 L0.3325,0.5649 Q0.3349,0.5649,0.3402,0.5657 T0.3494,0.5666 Q0.3542,0.5666,0.3598,0.5657 Q0.3663,0.5645,0.3695,0.5645 Q0.3735,0.5645,0.3775,0.5666 T0.3847,0.5695 Q0.3888,0.5707,0.3912,0.5709 T0.3968,0.5711 T0.4048,0.572 Q0.4096,0.5724,0.4165,0.5726 T0.4281,0.5734 T0.4378,0.5757 Q0.4418,0.5774,0.4522,0.5774 Q0.4562,0.5774,0.4655,0.5769 T0.4795,0.5765 Q0.4932,0.5765,0.4972,0.579 Q0.5028,0.5819,0.51,0.5819 Q0.5141,0.5819,0.5221,0.5809 T0.5341,0.5798 L0.5373,0.5798 Q0.5422,0.5798,0.557,0.5821 T0.5855,0.5844 L0.5936,0.5844 L0.5992,0.5844 T0.604,0.5848 T0.6076,0.5852 T0.6108,0.5859 T0.6137,0.5865 T0.6161,0.5873 T0.6181,0.5881 T0.6209,0.5896 L0.6233,0.591 Q0.6265,0.5927,0.6353,0.5927 L0.6402,0.5927 L0.645,0.5927 Q0.6723,0.5927,0.6948,0.6039 Q0.6988,0.606,0.7036,0.606 Q0.7076,0.606,0.7153,0.6045 T0.7261,0.6031 Q0.7309,0.6031,0.7454,0.6027 T0.7647,0.6022 Q0.7783,0.6022,0.808,0.608 L0.812,0.608 Q0.8169,0.608,0.8293,0.6062 T0.8474,0.6039 L0.8506,0.6039 Q0.857,0.6039,0.8655,0.6049 T0.8835,0.6074 T0.8972,0.6093 Q0.9036,0.6101,0.9213,0.6101 L0.9398,0.6101 L0.959,0.6101 L0.9767,0.6101 Q0.9976,0.6109,0.9976,0.6176 Q0.9976,0.6188,0.9968,0.6226 Q0.9952,0.6284,0.9671,0.6284 L0.959,0.6284 L0.9526,0.6284 Q0.9486,0.6284,0.9382,0.6302 T0.9205,0.6321 L0.9173,0.6321 Q0.9133,0.6317,0.9092,0.6317 Q0.9012,0.6317,0.8715,0.6346 Q0.8233,0.6387,0.7944,0.6421 Q0.7888,0.6429,0.7831,0.6429 Q0.7783,0.6429,0.7731,0.6425 T0.7663,0.6421 L0.7622,0.6421 Q0.7614,0.6425,0.7586,0.6425 T0.7474,0.6421 T0.7349,0.6416 Q0.7261,0.6416,0.7221,0.6429 Q0.7173,0.645,0.7032,0.646 T0.6711,0.6479 T0.6482,0.6491 Q0.645,0.6495,0.6418,0.6495 Q0.6353,0.6495,0.6273,0.6474 T0.6161,0.6454 Q0.612,0.6454,0.6088,0.6477 T0.6,0.6499 Q0.5912,0.6499,0.5494,0.6578 Q0.5422,0.6591,0.5373,0.6591 Q0.5341,0.6591,0.5281,0.6584 T0.5173,0.6578 L0.5124,0.6578 Q0.5004,0.6582,0.4747,0.6632 Q0.4675,0.6649,0.4398,0.6667 T0.3851,0.6703 T0.3566,0.6719 Q0.3534,0.6723,0.3406,0.6734 T0.3141,0.6757 T0.2948,0.6773 Q0.2908,0.6781,0.2819,0.6829 T0.2618,0.6877 Q0.2482,0.6877,0.2273,0.6958 T0.1992,0.7117 Q0.1976,0.7138,0.1976,0.7155 Q0.1976,0.7188,0.2032,0.7213 T0.2181,0.7258 T0.2289,0.7292 Q0.2337,0.7308,0.2378,0.7316 L0.2458,0.7333 T0.2522,0.7345 Q0.2747,0.7399,0.2835,0.7399 Q0.29,0.7399,0.3048,0.7408 T0.3253,0.7416 Q0.3277,0.7416,0.3297,0.7414 T0.3341,0.7412 L0.3598,0.7445 Q0.3639,0.7449,0.3663,0.7449 Q0.3695,0.7449,0.3751,0.7443 T0.3855,0.7437 L0.3904,0.7437 Q0.3984,0.7441,0.4141,0.7472 T0.4353,0.7507 Q0.4434,0.752,0.4434,0.7553 Q0.4434,0.7578,0.439,0.7605 T0.4297,0.764 Q0.4257,0.7648,0.4201,0.7655 T0.4076,0.7665 T0.3984,0.7673 Q0.3968,0.7673,0.3912,0.7677 T0.3815,0.7688 T0.3735,0.771 T0.3655,0.7727 Q0.3606,0.7727,0.3534,0.7708 T0.3458,0.769 L0.3446,0.769 Q0.3414,0.769,0.3297,0.7713 T0.3149,0.7735 Q0.3092,0.7735,0.296,0.7771 T0.2771,0.7831 Q0.2683,0.7872,0.2554,0.7885 Q0.253,0.7889,0.2325,0.7897 T0.2032,0.7914 Q0.2008,0.7918,0.198,0.792 T0.194,0.7924 T0.1912,0.7928 T0.1888,0.793 T0.1867,0.7934 T0.1851,0.7943 T0.1843,0.7953 T0.1835,0.7968 T0.1823,0.7984 T0.1815,0.8001 Q0.1815,0.8022,0.1847,0.8032 T0.1932,0.8053 T0.2008,0.8071 Q0.2072,0.8092,0.2265,0.8092 Q0.2361,0.8092,0.2586,0.8088 L0.2635,0.8088 Q0.2731,0.8088,0.2795,0.8098 T0.2928,0.8134 T0.302,0.8167 Q0.306,0.8179,0.3277,0.8183 T0.3542,0.8221 Q0.359,0.8246,0.3622,0.8246 Q0.3639,0.8246,0.3675,0.8237 T0.3751,0.8229 Q0.3968,0.8229,0.4048,0.8275 Q0.4088,0.8291,0.4161,0.8291 Q0.4225,0.8291,0.4353,0.8275 Q0.4369,0.827,0.4394,0.827 Q0.4474,0.827,0.4675,0.8345 Q0.4707,0.8358,0.4739,0.8358 Q0.4755,0.8358,0.4799,0.8353 T0.4876,0.8349 Q0.494,0.8349,0.5012,0.8362 L0.5076,0.837 T0.5133,0.8376 T0.5181,0.8382 T0.5221,0.8391 L0.5253,0.8399 T0.5277,0.8409 L0.5293,0.8422 T0.5301,0.8438 L0.5301,0.8461 Q0.5301,0.8499,0.5249,0.8511 T0.5112,0.8526 T0.5004,0.8532 Q0.4964,0.854,0.4851,0.8546 T0.4667,0.8565 Q0.4578,0.8577,0.441,0.8577 L0.4345,0.8577 L0.4289,0.8577 L0.4209,0.8577 Q0.4153,0.8582,0.4088,0.8586 T0.3968,0.8592 T0.3855,0.8594 T0.3755,0.8596 T0.3675,0.86 T0.3622,0.8602 Q0.3317,0.8635,0.3237,0.8635 Q0.2996,0.8635,0.2996,0.8681 Q0.2996,0.8698,0.3032,0.8733 T0.3068,0.8797 L0.3068,0.8822 Q0.3052,0.8859,0.2859,0.8859 Q0.2787,0.8859,0.2739,0.8851 L0.2699,0.8851 Q0.2659,0.8851,0.259,0.888 T0.2498,0.8913 Q0.2394,0.8946,0.2056,0.8971 Q0.2032,0.8971,0.1928,0.9002 T0.1703,0.9034 Q0.1526,0.9034,0.1353,0.9067 T0.1173,0.915 L0.1173,0.9158 Q0.1173,0.9166,0.1177,0.9175 T0.1189,0.9191 T0.1205,0.9204 T0.1229,0.9212 T0.1257,0.922 T0.1285,0.9226 T0.1309,0.9231 T0.1337,0.9235 T0.1365,0.9237 L0.1446,0.9237 T0.1554,0.9243 T0.1614,0.9268 T0.1659,0.9293 T0.1743,0.9303 T0.1823,0.9316 Q0.1863,0.932,0.1912,0.9332 T0.2028,0.9361 T0.2153,0.939 Q0.2233,0.9411,0.2297,0.9448 T0.2386,0.9494 Q0.2426,0.9506,0.2506,0.9506 Q0.253,0.9506,0.2594,0.9504 T0.2699,0.9502 Q0.2763,0.9502,0.2819,0.9511 Q0.2851,0.9511,0.2871,0.9529 T0.2892,0.9573 Q0.2892,0.9627,0.2811,0.9635 Q0.2434,0.9652,0.2337,0.9672 Q0.2257,0.9693,0.2241,0.9693 Q0.2217,0.9693,0.2096,0.9689 L0.2056,0.9689 Q0.1976,0.9689,0.1924,0.9699 T0.1787,0.9741 L0.1647,0.9793 Q0.1518,0.983,0.1285,0.983 L0.1261,0.983 L0.1237,0.983 Q0.1124,0.983,0.1076,0.9838 Q0.102,0.9851,0.0839,0.9896 T0.0602,0.9954 T0.0406,0.9971 T0.0153,0.9979 L0.0032,0.9979 Z', type: CustomGlyphVectorType.FILL } }, // '\u{E0C8}' is unused '\u{E0CA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.9992,0.998 Q0.9992,1,0.9968,1 Q0.9521,1,0.9392,0.998 Q0.9335,0.997,0.9152,0.9921 T0.8913,0.9862 Q0.8873,0.9852,0.8783,0.9852 L0.8747,0.9852 L0.871,0.9852 Q0.8483,0.9852,0.8345,0.9812 Q0.8289,0.9792,0.8228,0.9773 T0.8135,0.9743 T0.8054,0.9723 T0.7948,0.9713 L0.7908,0.9713 L0.777,0.9713 T0.7656,0.9693 Q0.7616,0.9683,0.7498,0.9674 T0.7283,0.9664 L0.7186,0.9654 Q0.708,0.9644,0.708,0.9575 Q0.708,0.9545,0.7109,0.952 T0.7178,0.9486 L0.7315,0.9486 L0.7417,0.9486 L0.7502,0.9486 Q0.7575,0.9486,0.7599,0.9476 Q0.7616,0.9466,0.7685,0.9426 T0.7843,0.9377 Q0.7899,0.9357,0.8009,0.9332 T0.8175,0.9298 Q0.82,0.9288,0.8228,0.9283 T0.8277,0.9278 T0.8313,0.9273 T0.8341,0.9263 T0.8362,0.9258 Q0.8402,0.9219,0.854,0.9219 L0.8569,0.9219 L0.8597,0.9219 L0.8637,0.9219 Q0.88,0.9199,0.88,0.9159 L0.88,0.9149 Q0.88,0.913,0.8719,0.911 T0.852,0.9075 T0.8297,0.906 Q0.8224,0.906,0.8167,0.905 T0.8074,0.9031 T0.8001,0.9006 T0.794,0.8991 Q0.7599,0.8971,0.7494,0.8942 Q0.7461,0.8932,0.7397,0.8902 T0.7299,0.8872 L0.7267,0.8872 Q0.721,0.8882,0.7121,0.8882 Q0.6926,0.8882,0.691,0.8833 L0.691,0.8803 Q0.691,0.8773,0.6922,0.8749 T0.6955,0.8704 L0.6975,0.8684 Q0.6926,0.8655,0.6764,0.8655 Q0.6683,0.8655,0.6375,0.8625 Q0.6358,0.8625,0.629,0.862 T0.6148,0.8615 T0.5973,0.861 T0.5791,0.8605 L0.5718,0.8605 L0.5661,0.8605 L0.5596,0.8605 Q0.5426,0.8605,0.5337,0.8586 Q0.5264,0.8576,0.515,0.8571 T0.4996,0.8556 Q0.498,0.8556,0.4947,0.8551 T0.4882,0.8546 T0.4809,0.8541 T0.474,0.8531 T0.4692,0.8506 T0.4672,0.8467 Q0.4672,0.8437,0.468,0.8422 T0.4704,0.8398 T0.4736,0.8383 T0.4781,0.8368 T0.4838,0.8358 T0.4911,0.8353 T0.4988,0.8338 Q0.5069,0.8328,0.5142,0.8328 Q0.5174,0.8328,0.5215,0.8333 T0.5264,0.8338 Q0.5296,0.8338,0.532,0.8328 Q0.5523,0.8249,0.5604,0.8249 L0.5653,0.8249 Q0.5775,0.8269,0.5848,0.8269 Q0.5912,0.8269,0.5937,0.8259 Q0.6026,0.821,0.6253,0.821 Q0.6294,0.821,0.6334,0.822 T0.6383,0.8229 Q0.6399,0.8229,0.644,0.82 Q0.6504,0.817,0.6723,0.816 T0.6975,0.814 Q0.6999,0.814,0.7048,0.8121 T0.7129,0.8091 T0.7226,0.8071 T0.7372,0.8061 L0.7421,0.8061 Q0.7624,0.8071,0.7721,0.8071 Q0.7924,0.8071,0.7981,0.8051 Q0.8021,0.8032,0.807,0.8027 T0.8135,0.8017 T0.8151,0.8002 L0.8151,0.7992 Q0.8143,0.7962,0.8127,0.7957 T0.8045,0.7943 Q0.7997,0.7943,0.7964,0.7933 Q0.7883,0.7933,0.7676,0.7923 T0.7437,0.7903 Q0.7307,0.7893,0.7218,0.7844 Q0.7194,0.7834,0.7141,0.7819 L0.7036,0.7789 T0.693,0.7765 T0.6853,0.7755 Q0.6805,0.7755,0.6691,0.773 T0.6553,0.7705 Q0.6545,0.7705,0.6545,0.7715 Q0.6537,0.7715,0.65,0.7725 T0.6423,0.774 T0.635,0.7745 Q0.6294,0.7745,0.6257,0.773 T0.618,0.771 T0.6083,0.77 T0.601,0.7695 Q0.5985,0.7695,0.5921,0.769 T0.5795,0.7676 T0.5702,0.7666 Q0.5653,0.7656,0.5596,0.7616 T0.5539,0.7547 Q0.5539,0.7498,0.5645,0.7488 Q0.5693,0.7478,0.5852,0.7448 T0.6099,0.7418 L0.6148,0.7418 Q0.6196,0.7418,0.6253,0.7423 T0.6338,0.7428 L0.6399,0.7428 Q0.6431,0.7418,0.6488,0.7409 T0.6586,0.7394 T0.6659,0.7389 L0.6707,0.7389 Q0.6723,0.7399,0.6748,0.7399 Q0.6796,0.7399,0.6946,0.7389 T0.7161,0.7379 Q0.7251,0.7379,0.7478,0.7319 Q0.7494,0.7319,0.7539,0.7315 T0.762,0.73 T0.7697,0.727 T0.7818,0.723 T0.7948,0.7196 T0.7997,0.7151 Q0.7997,0.7141,0.7989,0.7132 Q0.7924,0.7062,0.7717,0.6978 T0.738,0.6894 Q0.7267,0.6894,0.717,0.6845 T0.7048,0.6795 Q0.6975,0.6785,0.6711,0.6766 T0.6431,0.6746 Q0.6415,0.6736,0.6144,0.6721 T0.5596,0.6686 T0.5247,0.6657 Q0.4996,0.6607,0.4874,0.6597 L0.4834,0.6597 Q0.4785,0.6597,0.4724,0.6607 T0.4631,0.6617 Q0.4574,0.6617,0.4501,0.6597 Q0.4088,0.6518,0.4006,0.6518 Q0.3933,0.6518,0.3901,0.6499 T0.3844,0.6479 T0.3739,0.6499 T0.3585,0.6518 L0.352,0.6518 Q0.3471,0.6508,0.335,0.6503 T0.3122,0.6494 T0.2912,0.6479 T0.2766,0.6449 Q0.2733,0.6439,0.2668,0.6439 Q0.2628,0.6439,0.2543,0.6444 T0.2425,0.6449 T0.2368,0.6439 L0.2344,0.6439 Q0.2336,0.6439,0.2283,0.6444 T0.2182,0.6449 Q0.2117,0.6449,0.2052,0.6439 Q0.1768,0.6409,0.1281,0.637 Q0.0989,0.634,0.0916,0.634 L0.0835,0.634 L0.0795,0.634 Q0.0722,0.634,0.0616,0.632 T0.0479,0.6301 Q0.0462,0.6301,0.0406,0.6306 T0.0308,0.6311 Q0.0032,0.6311,0.0008,0.6231 Q0,0.6202,0,0.6182 Q0,0.6083,0.0227,0.6083 Q0.03,0.6073,0.0422,0.6073 Q0.0487,0.6073,0.0608,0.6078 T0.0787,0.6083 Q0.0973,0.6083,0.103,0.6073 Q0.1062,0.6063,0.1131,0.6053 T0.1253,0.6039 T0.1375,0.6024 T0.1492,0.6014 L0.1533,0.6014 Q0.1598,0.6024,0.1723,0.6044 T0.189,0.6063 Q0.1906,0.6063,0.1922,0.6053 Q0.2214,0.6004,0.236,0.6004 Q0.2409,0.6004,0.2551,0.6009 T0.2741,0.6014 Q0.2782,0.6014,0.2859,0.6024 T0.2968,0.6034 Q0.3009,0.6034,0.3041,0.6024 Q0.3277,0.5905,0.356,0.5905 L0.3613,0.5905 L0.3666,0.5905 Q0.3731,0.5905,0.3755,0.5895 Q0.382,0.5856,0.3844,0.5846 T0.3938,0.5831 T0.4144,0.5826 T0.4428,0.5801 T0.4631,0.5776 L0.4663,0.5776 Q0.4704,0.5776,0.4785,0.5786 T0.4899,0.5796 Q0.4964,0.5796,0.5012,0.5767 Q0.5069,0.5737,0.5223,0.5737 Q0.5272,0.5737,0.5369,0.5742 T0.5499,0.5747 Q0.558,0.5747,0.5612,0.5737 Q0.5661,0.5717,0.5714,0.5712 T0.5835,0.5702 T0.5945,0.5692 T0.6018,0.5687 L0.6062,0.5687 T0.6095,0.5682 T0.6148,0.5678 Q0.6172,0.5668,0.6196,0.5653 T0.6245,0.5633 T0.631,0.5628 T0.6407,0.5638 T0.6504,0.5648 T0.6594,0.5638 T0.6675,0.5628 L0.6707,0.5628 L0.6861,0.5628 Q0.7364,0.5628,0.7494,0.5608 L0.7567,0.5608 L0.7652,0.5608 L0.7729,0.5608 Q0.7794,0.5608,0.7818,0.5589 Q0.7851,0.5569,0.7859,0.5559 Q0.7835,0.5539,0.7656,0.5519 Q0.7121,0.547,0.6975,0.543 Q0.6934,0.541,0.6853,0.5396 L0.6691,0.5366 T0.6525,0.5336 T0.6391,0.5312 Q0.6367,0.5302,0.6245,0.5257 T0.6034,0.5213 L0.5977,0.5213 Q0.5945,0.5223,0.5904,0.5223 Q0.5839,0.5223,0.5787,0.5208 T0.5685,0.5183 T0.5596,0.5173 Q0.558,0.5173,0.5552,0.5168 T0.5495,0.5163 L0.5434,0.5163 T0.5373,0.5158 T0.532,0.5148 T0.5284,0.5129 T0.5272,0.5104 L0.5272,0.5074 Q0.5304,0.5015,0.5377,0.499 T0.5629,0.4965 Q0.5734,0.4955,0.5811,0.4946 T0.5953,0.4916 T0.605,0.4896 Q0.6083,0.4886,0.6123,0.4886 Q0.6148,0.4886,0.6225,0.4891 T0.635,0.4896 L0.6456,0.4896 Q0.6837,0.4857,0.6861,0.4857 Q0.7226,0.4768,0.7283,0.4768 Q0.7307,0.4768,0.732,0.4773 T0.7372,0.4777 Q0.7502,0.4777,0.7721,0.4758 Q0.7753,0.4748,0.777,0.4738 Q0.7745,0.4718,0.768,0.4708 Q0.7599,0.4688,0.7445,0.4688 Q0.7405,0.4688,0.7324,0.4693 T0.721,0.4698 Q0.7056,0.4698,0.6999,0.4679 Q0.6861,0.4629,0.6626,0.4619 Q0.6586,0.4609,0.6533,0.4604 T0.6419,0.459 T0.6314,0.456 T0.6269,0.452 Q0.6269,0.451,0.6302,0.4491 Q0.6358,0.4451,0.6525,0.4416 T0.6788,0.4382 L0.6845,0.4382 L0.6902,0.4382 Q0.7064,0.4382,0.7145,0.4362 Q0.7202,0.4342,0.7307,0.4342 L0.7356,0.4342 L0.7409,0.4342 L0.7453,0.4342 Q0.7543,0.4342,0.7607,0.4332 Q0.7778,0.4313,0.7847,0.4288 T0.7932,0.4248 T0.7972,0.4169 T0.8045,0.4045 L0.8094,0.3986 T0.8135,0.3942 L0.8167,0.3912 T0.8191,0.3887 T0.8204,0.3867 T0.8208,0.3848 Q0.8208,0.3818,0.8167,0.3739 Q0.8127,0.3689,0.8054,0.3635 T0.7924,0.3556 T0.7778,0.3497 T0.768,0.3462 Q0.764,0.3442,0.7478,0.3427 T0.7267,0.3383 Q0.7234,0.3363,0.7044,0.3348 T0.6805,0.3323 L0.6764,0.3323 T0.6679,0.3328 T0.6586,0.3333 Q0.6521,0.3333,0.648,0.3314 Q0.6391,0.3264,0.6294,0.3264 Q0.6229,0.3264,0.6156,0.3294 Q0.6115,0.3304,0.6042,0.3304 Q0.5977,0.3304,0.5888,0.3289 T0.5738,0.3264 T0.5584,0.3225 L0.5491,0.3195 Q0.5418,0.3185,0.5162,0.3155 T0.4895,0.3121 T0.4826,0.3116 T0.4716,0.3111 T0.4607,0.3096 T0.4513,0.3066 T0.4477,0.3017 Q0.4477,0.2997,0.4485,0.2977 Q0.4526,0.2898,0.4611,0.2878 T0.4842,0.2859 Q0.4882,0.2859,0.4943,0.2864 T0.5028,0.2868 Q0.5118,0.2868,0.5215,0.2829 Q0.5401,0.276,0.5734,0.276 Q0.5831,0.276,0.5872,0.277 L0.5904,0.277 T0.5949,0.2745 T0.6034,0.272 Q0.631,0.269,0.6407,0.2661 Q0.6448,0.2641,0.6488,0.2641 T0.6573,0.2656 T0.6634,0.2671 Q0.6659,0.2671,0.6675,0.2651 Q0.6723,0.2601,0.6886,0.2552 T0.7137,0.2502 L0.7153,0.2502 L0.7178,0.2502 Q0.7218,0.2502,0.7279,0.2493 T0.7417,0.2468 T0.7526,0.2453 Q0.8208,0.2354,0.8281,0.2354 Q0.8337,0.2354,0.8394,0.2344 T0.8496,0.2315 T0.854,0.2265 Q0.854,0.2216,0.8443,0.2176 Q0.8354,0.2127,0.8216,0.2102 T0.7972,0.2072 T0.777,0.2067 T0.7648,0.2057 Q0.7632,0.2047,0.7498,0.2038 T0.7202,0.2018 T0.6983,0.1998 Q0.6918,0.1998,0.676,0.1949 T0.6537,0.1899 Q0.6496,0.1899,0.6472,0.1919 Q0.6342,0.1968,0.6123,0.1968 Q0.5961,0.1968,0.5912,0.1929 Q0.5904,0.1919,0.5892,0.1919 T0.5823,0.1944 T0.5722,0.1968 T0.5637,0.1949 Q0.5556,0.1909,0.5158,0.1869 Q0.5109,0.1869,0.5032,0.1835 T0.4915,0.18 Q0.4907,0.18,0.4891,0.181 Q0.4793,0.182,0.455,0.182 Q0.4177,0.182,0.4015,0.1771 Q0.3901,0.1741,0.3779,0.1741 Q0.3747,0.1741,0.369,0.1746 T0.3609,0.1751 Q0.3569,0.1751,0.3536,0.1741 Q0.3512,0.1741,0.3459,0.1736 T0.3358,0.1726 T0.3256,0.1716 T0.3167,0.1696 T0.3106,0.1662 Q0.3082,0.1632,0.3082,0.1592 Q0.3082,0.1543,0.3114,0.1508 T0.3204,0.1474 L0.3236,0.1474 Q0.3301,0.1494,0.3333,0.1494 Q0.339,0.1494,0.3496,0.1454 Q0.3674,0.1385,0.3869,0.1385 Q0.395,0.1385,0.4011,0.137 T0.4116,0.1355 T0.4197,0.1375 T0.425,0.1395 Q0.429,0.1395,0.4355,0.1365 Q0.4631,0.1266,0.4882,0.1266 Q0.5126,0.1266,0.5337,0.1246 Q0.5369,0.1246,0.5458,0.1207 T0.5661,0.1167 L0.5693,0.1167 Q0.5791,0.1177,0.5852,0.1187 T0.5921,0.1197 Q0.5945,0.1197,0.6002,0.1167 Q0.6026,0.1147,0.605,0.1123 T0.6091,0.1093 T0.6156,0.1088 T0.6318,0.1098 Q0.6456,0.1108,0.6569,0.1137 T0.6707,0.1167 Q0.674,0.1167,0.6772,0.1147 Q0.6821,0.1108,0.6946,0.1083 T0.721,0.1044 T0.7453,0.1029 L0.7526,0.1029 Q0.7583,0.1039,0.7652,0.1053 T0.7753,0.1068 Q0.7778,0.1068,0.7802,0.1058 Q0.7981,0.1019,0.8143,0.1019 Q0.8175,0.1019,0.8439,0.0964 T0.8735,0.091 Q0.8759,0.091,0.8836,0.089 T0.8938,0.0851 Q0.8929,0.0841,0.8905,0.0841 Q0.8832,0.0821,0.8735,0.0821 Q0.8694,0.0821,0.8629,0.0826 T0.854,0.0831 Q0.8508,0.0831,0.8491,0.0821 Q0.8475,0.0821,0.8171,0.0811 T0.7802,0.0781 T0.7571,0.0737 T0.7332,0.0712 Q0.7299,0.0712,0.7247,0.0717 T0.7161,0.0722 L0.7064,0.0722 Q0.7032,0.0712,0.7003,0.0707 T0.6959,0.0692 T0.693,0.0678 T0.6914,0.0663 T0.6902,0.0648 T0.689,0.0643 L0.6869,0.0643 L0.6845,0.0643 L0.6821,0.0643 Q0.6553,0.0643,0.6553,0.0554 T0.6796,0.0465 L0.6813,0.0465 L0.6845,0.0465 Q0.6918,0.0425,0.717,0.0376 Q0.7226,0.0366,0.7267,0.0356 T0.7324,0.0336 T0.7376,0.0317 T0.747,0.0297 Q0.7518,0.0287,0.7551,0.0287 T0.762,0.0297 T0.768,0.0307 L0.7729,0.0307 Q0.7802,0.0277,0.7997,0.0267 T0.8224,0.0237 Q0.8273,0.0227,0.8341,0.0223 T0.8475,0.0193 T0.8654,0.0143 T0.8873,0.0079 Q0.9002,0.003,0.9331,0.004 T0.97,0.004 Q0.9838,0,0.9976,0.001 Q1,0.001,1,0.003 Z M0.9935,0.996 L0.9951,0.0049 Q0.9822,0.0049,0.9716,0.0079 Q0.9659,0.0099,0.9327,0.0089 T0.8897,0.0119 Q0.8775,0.0158,0.8666,0.0183 T0.85,0.0237 Q0.8418,0.0267,0.8341,0.0272 T0.8248,0.0277 Q0.82,0.0297,0.8001,0.0312 T0.7745,0.0346 Q0.7697,0.0356,0.7664,0.0356 Q0.764,0.0356,0.7603,0.0341 T0.7534,0.0326 Q0.751,0.0326,0.7478,0.0336 Q0.7429,0.0346,0.7397,0.0356 T0.7344,0.0376 T0.7283,0.0401 T0.7178,0.0425 Q0.6926,0.0465,0.6878,0.0495 Q0.6845,0.0514,0.6764,0.0514 L0.6723,0.0514 L0.6691,0.0514 Q0.6602,0.0514,0.6602,0.0554 Q0.6602,0.0574,0.6659,0.0579 T0.6796,0.0589 T0.691,0.0603 T0.6967,0.0643 T0.7072,0.0673 Q0.7121,0.0682,0.7153,0.0682 T0.7238,0.0678 T0.7332,0.0673 Q0.7413,0.0673,0.7579,0.0692 T0.7818,0.0742 Q0.7867,0.0752,0.8171,0.0762 T0.8508,0.0781 L0.852,0.0781 T0.8601,0.0776 T0.8719,0.0772 Q0.8832,0.0772,0.8921,0.0791 Q0.8994,0.0811,0.8994,0.0841 T0.8933,0.0895 T0.8812,0.094 T0.8735,0.0959 T0.8451,0.1014 T0.8143,0.1068 Q0.7989,0.1068,0.7818,0.1098 Q0.7778,0.1108,0.7745,0.1108 T0.768,0.1103 T0.7603,0.1088 T0.7518,0.1068 L0.7461,0.1068 Q0.7315,0.1068,0.7088,0.1103 T0.6805,0.1177 Q0.6756,0.1207,0.6699,0.1207 Q0.6667,0.1207,0.6545,0.1177 T0.631,0.1147 Q0.6269,0.1137,0.6225,0.1133 T0.616,0.1128 L0.6123,0.1128 T0.6103,0.1133 L0.6083,0.1157 T0.6034,0.1197 Q0.5961,0.1246,0.5921,0.1246 Q0.5904,0.1246,0.5835,0.1231 T0.5693,0.1217 L0.5661,0.1217 Q0.558,0.1217,0.5535,0.1231 T0.5446,0.1266 T0.5345,0.1296 Q0.5126,0.1316,0.4882,0.1316 T0.438,0.1405 Q0.4298,0.1434,0.425,0.1434 Q0.4209,0.1434,0.4173,0.1419 T0.4112,0.1405 T0.4027,0.1419 T0.3869,0.1434 Q0.3682,0.1434,0.3512,0.1494 Q0.3398,0.1533,0.3333,0.1533 Q0.3285,0.1533,0.3228,0.1513 L0.3212,0.1513 Q0.3179,0.1513,0.3155,0.1538 T0.3131,0.1592 Q0.3131,0.1612,0.3147,0.1632 Q0.3171,0.1652,0.3232,0.1667 T0.3406,0.1691 T0.3544,0.1701 L0.3601,0.1701 L0.3678,0.1701 L0.3771,0.1701 Q0.3909,0.1701,0.4031,0.1731 Q0.4177,0.178,0.4558,0.178 Q0.4793,0.178,0.4882,0.1761 L0.4915,0.1761 Q0.498,0.1761,0.5061,0.179 T0.5158,0.183 Q0.5166,0.183,0.5231,0.1835 T0.5341,0.1845 T0.545,0.1855 T0.5572,0.1874 T0.5661,0.1909 Q0.5677,0.1919,0.5702,0.1919 T0.5791,0.1894 T0.588,0.1869 Q0.5921,0.1869,0.5945,0.1899 Q0.5977,0.1919,0.6123,0.1919 Q0.6334,0.1919,0.6448,0.1879 Q0.6488,0.186,0.6537,0.186 Q0.6626,0.186,0.6784,0.1909 T0.6983,0.1958 Q0.704,0.1968,0.7206,0.1973 T0.7506,0.1993 T0.7689,0.2018 L0.7705,0.2018 L0.7741,0.2018 L0.7786,0.2018 Q0.8248,0.2018,0.8475,0.2136 Q0.8597,0.2196,0.8597,0.2255 T0.8483,0.2359 T0.8281,0.2404 Q0.8208,0.2404,0.7534,0.2493 Q0.7502,0.2502,0.7425,0.2512 T0.7287,0.2532 T0.7178,0.2542 L0.7153,0.2542 L0.7137,0.2542 Q0.7064,0.2542,0.6906,0.2591 T0.6715,0.2681 T0.6626,0.272 Q0.6594,0.272,0.6541,0.2705 T0.6464,0.269 T0.6431,0.27 Q0.6318,0.273,0.6034,0.276 Q0.6018,0.276,0.5998,0.2774 T0.5957,0.2799 T0.5904,0.2809 L0.5856,0.2809 Q0.5823,0.2799,0.575,0.2799 Q0.541,0.2799,0.5239,0.2868 Q0.5134,0.2908,0.5028,0.2908 Q0.4996,0.2908,0.4935,0.2903 T0.4842,0.2898 Q0.4704,0.2898,0.4635,0.2918 T0.4534,0.2997 L0.4534,0.3007 Q0.4534,0.3037,0.459,0.3046 T0.4753,0.3066 T0.4899,0.3076 Q0.4907,0.3076,0.5166,0.3106 T0.5507,0.3155 Q0.5921,0.3264,0.605,0.3264 Q0.6107,0.3264,0.6131,0.3254 Q0.6212,0.3225,0.6294,0.3225 Q0.6407,0.3225,0.6513,0.3274 Q0.6529,0.3294,0.6569,0.3294 Q0.6594,0.3294,0.665,0.3284 T0.6748,0.3274 T0.6821,0.3284 Q0.6861,0.3294,0.6967,0.3299 T0.7165,0.3314 T0.7299,0.3343 Q0.7324,0.3363,0.7486,0.3383 T0.7697,0.3422 Q0.7713,0.3432,0.7774,0.3452 T0.7887,0.3492 T0.8005,0.3546 T0.8127,0.3625 T0.8208,0.3719 Q0.8256,0.3798,0.8256,0.3848 Q0.8256,0.3877,0.8236,0.3902 T0.8171,0.3976 T0.809,0.4075 T0.8021,0.4189 T0.7968,0.4273 T0.7867,0.4322 T0.7616,0.4382 Q0.7543,0.4392,0.7437,0.4392 L0.7364,0.4392 L0.7299,0.4392 Q0.7202,0.4392,0.7161,0.4402 Q0.7072,0.4431,0.691,0.4431 L0.6845,0.4431 L0.6788,0.4431 Q0.6707,0.4431,0.6545,0.4461 T0.6334,0.452 Q0.6399,0.456,0.6626,0.457 Q0.6878,0.458,0.7024,0.4639 Q0.7064,0.4649,0.7186,0.4649 L0.7295,0.4649 L0.7413,0.4649 Q0.7591,0.4649,0.7697,0.4669 Q0.7818,0.4688,0.7818,0.4728 Q0.7818,0.4748,0.7798,0.4763 T0.7753,0.4787 L0.7737,0.4797 L0.7729,0.4797 Q0.7494,0.4827,0.7388,0.4827 Q0.7332,0.4827,0.7307,0.4817 L0.7291,0.4817 Q0.7226,0.4817,0.6878,0.4896 Q0.6829,0.4906,0.6772,0.4911 T0.6618,0.4921 T0.646,0.4931 T0.6342,0.4936 T0.6212,0.4931 T0.6115,0.4926 Q0.6083,0.4926,0.6058,0.4931 T0.5969,0.4955 T0.5819,0.499 T0.5629,0.5005 Q0.5572,0.5005,0.5527,0.501 T0.545,0.502 T0.5397,0.503 T0.5361,0.5045 T0.5337,0.5064 T0.5328,0.5094 L0.532,0.5094 Q0.532,0.5104,0.5337,0.5109 T0.5381,0.5114 T0.5438,0.5119 T0.5515,0.5124 L0.5604,0.5124 Q0.5661,0.5134,0.5746,0.5153 T0.5912,0.5173 L0.5977,0.5173 L0.6026,0.5173 Q0.6139,0.5173,0.6273,0.5218 T0.6407,0.5272 Q0.6448,0.5282,0.6683,0.5321 T0.6991,0.5381 Q0.7137,0.543,0.7664,0.548 Q0.7908,0.55,0.7908,0.5559 Q0.7908,0.5589,0.7859,0.5628 Q0.781,0.5658,0.7713,0.5658 Q0.7689,0.5658,0.7628,0.5653 T0.7543,0.5648 T0.7502,0.5658 Q0.7364,0.5678,0.6878,0.5678 Q0.674,0.5678,0.6699,0.5668 L0.6683,0.5668 Q0.6659,0.5668,0.6606,0.5678 T0.6513,0.5687 Q0.6464,0.5687,0.6391,0.5678 Q0.6334,0.5668,0.6302,0.5668 T0.6241,0.5687 T0.6164,0.5717 Q0.6123,0.5727,0.6099,0.5727 L0.6062,0.5727 T0.6022,0.5732 T0.5953,0.5737 Q0.5904,0.5747,0.5839,0.5747 T0.5726,0.5752 T0.5629,0.5776 T0.5458,0.5796 Q0.5418,0.5796,0.5324,0.5791 T0.5191,0.5786 Q0.5077,0.5786,0.5045,0.5806 Q0.498,0.5836,0.4899,0.5836 Q0.4858,0.5836,0.4777,0.5826 T0.4655,0.5816 L0.4631,0.5816 Q0.4582,0.5816,0.4436,0.5841 T0.4144,0.5865 L0.4051,0.5865 L0.3986,0.5865 T0.3933,0.587 T0.3897,0.5875 T0.3869,0.5885 T0.3844,0.5895 T0.3812,0.591 T0.3779,0.5925 Q0.3739,0.5955,0.3633,0.5955 L0.3585,0.5955 L0.3536,0.5955 Q0.3277,0.5955,0.3066,0.6053 Q0.3017,0.6083,0.296,0.6083 Q0.2912,0.6083,0.2839,0.6068 T0.2741,0.6053 Q0.2693,0.6053,0.2547,0.6048 T0.236,0.6044 Q0.2222,0.6044,0.193,0.6103 L0.1882,0.6103 Q0.1825,0.6103,0.1703,0.6083 T0.1525,0.6063 L0.15,0.6063 Q0.1436,0.6063,0.135,0.6073 T0.1168,0.6098 T0.1038,0.6113 Q0.0973,0.6123,0.0787,0.6123 L0.06,0.6123 L0.0414,0.6123 L0.0235,0.6123 Q0.0049,0.6133,0.0049,0.6172 L0.0057,0.6222 Q0.0073,0.6261,0.043,0.6261 L0.0454,0.6261 L0.0479,0.6261 Q0.0527,0.6261,0.0633,0.6281 T0.0803,0.6301 L0.0835,0.6301 L0.0916,0.6301 Q0.0998,0.6301,0.129,0.632 Q0.1768,0.636,0.206,0.64 Q0.2117,0.6409,0.2165,0.6409 Q0.2206,0.6409,0.2263,0.6405 T0.2336,0.64 L0.2384,0.64 L0.2409,0.64 T0.251,0.6395 T0.2644,0.639 Q0.2741,0.639,0.279,0.6409 Q0.283,0.6429,0.2972,0.6439 T0.3293,0.6459 T0.3528,0.6469 L0.3577,0.6469 Q0.3642,0.6469,0.3723,0.6449 T0.3844,0.6429 Q0.3901,0.6429,0.3933,0.6454 T0.4006,0.6479 Q0.4088,0.6479,0.4517,0.6558 Q0.4582,0.6568,0.4623,0.6568 Q0.4655,0.6568,0.4716,0.6563 T0.4826,0.6558 L0.4882,0.6558 Q0.5004,0.6558,0.5255,0.6607 Q0.5328,0.6627,0.5604,0.6647 T0.6152,0.6682 T0.644,0.6696 T0.6719,0.6721 T0.7056,0.6756 Q0.7113,0.6756,0.7198,0.6805 T0.738,0.6855 Q0.7453,0.6855,0.7551,0.6879 T0.7741,0.6939 T0.7912,0.7018 T0.8029,0.7102 Q0.8054,0.7132,0.8054,0.7151 Q0.8054,0.7191,0.8009,0.7216 T0.7908,0.7255 T0.779,0.7285 T0.7721,0.731 Q0.7672,0.7329,0.7632,0.7339 T0.7551,0.7354 T0.7486,0.7369 Q0.7478,0.7369,0.7445,0.7379 T0.7388,0.7394 T0.7324,0.7404 L0.7242,0.7413 T0.7161,0.7418 Q0.7105,0.7418,0.6955,0.7428 T0.6748,0.7438 L0.6699,0.7438 L0.6667,0.7438 Q0.6659,0.7438,0.6407,0.7468 Q0.6367,0.7478,0.6334,0.7478 T0.6245,0.7468 T0.6148,0.7458 L0.6099,0.7458 Q0.6018,0.7468,0.5864,0.7498 T0.5653,0.7532 T0.5596,0.7557 Q0.5596,0.7596,0.571,0.7616 Q0.5742,0.7626,0.5803,0.7631 T0.5929,0.7641 T0.6018,0.7656 L0.6087,0.7656 T0.6192,0.7666 T0.6277,0.7695 Q0.631,0.7705,0.6342,0.7705 Q0.6383,0.7705,0.6452,0.7685 T0.6537,0.7666 L0.6553,0.7666 Q0.6602,0.7666,0.6711,0.769 T0.6853,0.7715 Q0.691,0.7715,0.7048,0.775 T0.7242,0.7814 Q0.7324,0.7854,0.7445,0.7864 Q0.7478,0.7864,0.768,0.7873 T0.7972,0.7893 Q0.7989,0.7893,0.8017,0.7898 T0.8054,0.7903 L0.8082,0.7903 T0.811,0.7908 T0.8131,0.7913 T0.8151,0.7918 L0.8167,0.7928 T0.8179,0.7943 T0.8191,0.7957 T0.8204,0.7977 T0.8208,0.8002 Q0.8208,0.8032,0.8171,0.8051 T0.8074,0.8081 T0.7997,0.8091 Q0.7932,0.8121,0.7737,0.8121 Q0.7664,0.8121,0.7413,0.8111 L0.7364,0.8111 Q0.7275,0.8111,0.7214,0.8121 T0.7084,0.8155 T0.6991,0.819 Q0.6934,0.82,0.6723,0.8205 T0.6472,0.8239 Q0.6423,0.8269,0.6375,0.8269 Q0.6358,0.8269,0.6338,0.8264 T0.6294,0.8254 T0.6253,0.8249 Q0.6042,0.8249,0.5961,0.8289 Q0.5921,0.8318,0.5839,0.8318 Q0.5783,0.8318,0.5645,0.8299 L0.5604,0.8299 Q0.5531,0.8299,0.5337,0.8368 Q0.5296,0.8378,0.5255,0.8378 Q0.5239,0.8378,0.5195,0.8373 T0.5118,0.8368 Q0.5061,0.8368,0.4996,0.8388 Q0.4947,0.8388,0.4882,0.8398 Q0.4777,0.8417,0.4753,0.8427 T0.4728,0.8467 Q0.4728,0.8487,0.4769,0.8492 T0.4886,0.8501 T0.5004,0.8516 Q0.5036,0.8516,0.515,0.8521 T0.5345,0.8546 Q0.5426,0.8556,0.5596,0.8556 L0.5649,0.8556 L0.5702,0.8556 L0.5791,0.8556 Q0.5921,0.8566,0.6135,0.8571 T0.6383,0.8586 Q0.6683,0.8615,0.6764,0.8615 L0.6776,0.8615 L0.6788,0.8615 Q0.7032,0.8615,0.7032,0.8684 Q0.7032,0.8714,0.6995,0.8749 T0.6959,0.8803 L0.6959,0.8813 Q0.6991,0.8843,0.7153,0.8843 Q0.7218,0.8843,0.7259,0.8833 L0.7299,0.8833 Q0.7324,0.8833,0.7348,0.8838 T0.7393,0.8848 T0.7433,0.8863 L0.7474,0.8882 T0.751,0.8892 Q0.7616,0.8922,0.7948,0.8952 Q0.7981,0.8952,0.8082,0.8981 T0.8297,0.9011 T0.8528,0.9026 T0.8747,0.907 T0.8856,0.9149 L0.8856,0.9159 Q0.8856,0.9179,0.8848,0.9189 T0.8828,0.9209 T0.88,0.9228 T0.8767,0.9243 T0.8731,0.9248 T0.8698,0.9253 T0.8666,0.9258 L0.8646,0.9258 Q0.8621,0.9258,0.8577,0.9263 T0.85,0.9268 T0.8439,0.9273 T0.8402,0.9278 Q0.8386,0.9298,0.8358,0.9308 T0.8305,0.9322 T0.8244,0.9332 T0.8183,0.9337 Q0.8135,0.9347,0.8025,0.9372 T0.7859,0.9416 Q0.7778,0.9436,0.7717,0.9471 T0.7624,0.9515 Q0.7575,0.9535,0.7486,0.9535 Q0.7461,0.9535,0.7397,0.953 T0.7291,0.9525 Q0.7234,0.9525,0.7186,0.953 T0.7137,0.957 T0.7194,0.9614 Q0.7567,0.9634,0.7664,0.9654 Q0.7745,0.9674,0.7762,0.9674 L0.7908,0.9674 Q0.7924,0.9664,0.7948,0.9664 Q0.8029,0.9664,0.8082,0.9679 T0.8224,0.9723 T0.8362,0.9773 Q0.8483,0.9812,0.871,0.9812 L0.8751,0.9812 Q0.8873,0.9812,0.8929,0.9822 T0.9165,0.9876 T0.9408,0.9931 Q0.9513,0.996,0.9935,0.996 Z M0.9968,0.998 L0.9976,0.003 Q0.9838,0.002,0.9708,0.0059 Q0.9659,0.0069,0.9327,0.0059 T0.8881,0.0099 Q0.8767,0.0138,0.8658,0.0163 T0.8491,0.0218 Q0.8418,0.0247,0.8341,0.0247 T0.8232,0.0257 Q0.8191,0.0277,0.7997,0.0292 T0.7737,0.0326 Q0.7697,0.0336,0.7672,0.0336 T0.7612,0.0321 T0.7543,0.0307 T0.747,0.0317 Q0.7397,0.0326,0.7336,0.0356 T0.717,0.0406 Q0.6918,0.0445,0.6861,0.0475 Q0.6845,0.0485,0.6772,0.0485 T0.6638,0.05 T0.6577,0.0549 T0.6642,0.0598 T0.6796,0.0613 T0.6902,0.0623 Q0.691,0.0623,0.6946,0.0658 T0.7064,0.0702 L0.7161,0.0702 Q0.7194,0.0702,0.7242,0.0697 T0.7332,0.0692 Q0.7413,0.0692,0.7579,0.0717 T0.781,0.0762 Q0.7867,0.0781,0.8171,0.0786 T0.85,0.0801 L0.8532,0.0801 Q0.8548,0.0801,0.8613,0.0796 T0.8727,0.0791 Q0.8832,0.0791,0.8913,0.0811 Q0.897,0.0831,0.897,0.0851 Q0.897,0.088,0.8873,0.0905 T0.8735,0.093 Q0.871,0.093,0.8447,0.0984 T0.8143,0.1039 Q0.7981,0.1039,0.781,0.1078 Q0.7778,0.1088,0.7745,0.1088 T0.7644,0.1073 T0.7518,0.1048 L0.7453,0.1048 Q0.7299,0.1048,0.7076,0.1083 T0.6788,0.1157 Q0.6748,0.1187,0.6699,0.1187 Q0.6675,0.1187,0.6557,0.1157 T0.6318,0.1118 Q0.6164,0.1108,0.6135,0.1108 T0.6095,0.1113 T0.6067,0.1142 T0.6018,0.1187 Q0.5953,0.1227,0.5921,0.1227 Q0.5904,0.1227,0.5839,0.1212 T0.5693,0.1187 L0.5661,0.1187 Q0.558,0.1187,0.5527,0.1207 T0.5434,0.1246 T0.5337,0.1266 Q0.5126,0.1296,0.4882,0.1296 T0.4371,0.1385 Q0.4298,0.1414,0.425,0.1414 Q0.4225,0.1414,0.4189,0.1395 T0.4112,0.1375 Q0.4079,0.1375,0.4019,0.1395 T0.3869,0.1414 Q0.3682,0.1414,0.3504,0.1474 Q0.3398,0.1513,0.3333,0.1513 Q0.3293,0.1513,0.3228,0.1494 L0.3204,0.1494 Q0.3179,0.1494,0.3155,0.1508 T0.3118,0.1543 T0.3106,0.1592 T0.3127,0.1642 T0.32,0.1677 T0.3305,0.1701 T0.3435,0.1716 T0.3544,0.1721 Q0.3569,0.1731,0.3601,0.1731 Q0.3625,0.1731,0.3686,0.1726 T0.3779,0.1721 Q0.3901,0.1721,0.4023,0.1751 Q0.4177,0.18,0.455,0.18 Q0.4793,0.18,0.4882,0.178 L0.4915,0.178 Q0.4964,0.178,0.5045,0.1815 T0.5158,0.185 Q0.5564,0.1889,0.5645,0.1929 Q0.5677,0.1939,0.571,0.1939 Q0.575,0.1939,0.5807,0.1919 T0.5888,0.1899 T0.5929,0.1909 Q0.5969,0.1949,0.6123,0.1949 Q0.6334,0.1949,0.6456,0.1899 Q0.6488,0.1879,0.6537,0.1879 Q0.661,0.1879,0.6772,0.1929 T0.6983,0.1978 Q0.704,0.1988,0.7206,0.1998 T0.7502,0.2018 T0.7664,0.2038 Q0.7672,0.2038,0.7818,0.2043 T0.8151,0.2067 T0.8459,0.2156 Q0.8573,0.2206,0.8573,0.2255 Q0.8573,0.2295,0.8516,0.2324 T0.8394,0.2369 T0.8281,0.2384 Q0.8208,0.2384,0.7526,0.2473 Q0.7494,0.2483,0.7421,0.2493 T0.7287,0.2512 T0.7178,0.2522 L0.7153,0.2522 L0.7137,0.2522 Q0.7056,0.2522,0.6898,0.2572 T0.6691,0.2671 Q0.6667,0.269,0.6634,0.269 Q0.661,0.269,0.6557,0.2681 T0.6472,0.2671 Q0.6448,0.2671,0.6415,0.2681 Q0.631,0.271,0.6034,0.274 Q0.5994,0.274,0.5961,0.2765 T0.5904,0.2789 L0.5864,0.2789 Q0.5831,0.2779,0.5742,0.2779 Q0.541,0.2779,0.5223,0.2849 Q0.5126,0.2888,0.5028,0.2888 Q0.5004,0.2888,0.4943,0.2883 T0.4842,0.2878 Q0.4704,0.2878,0.4627,0.2898 T0.4509,0.2987 Q0.4501,0.2997,0.4501,0.3007 Q0.4501,0.3046,0.4574,0.3066 T0.4765,0.3091 T0.4899,0.3096 Q0.4931,0.3106,0.5061,0.3121 T0.5316,0.315 T0.5499,0.3175 Q0.5507,0.3175,0.5572,0.3195 T0.5685,0.3225 T0.5803,0.3254 T0.5933,0.3279 T0.605,0.3284 T0.6148,0.3274 Q0.6221,0.3244,0.6294,0.3244 Q0.6399,0.3244,0.6496,0.3294 Q0.6529,0.3314,0.6577,0.3314 Q0.6602,0.3314,0.6663,0.3304 T0.6756,0.3294 T0.6813,0.3304 Q0.6837,0.3304,0.6873,0.3309 L0.6946,0.3318 T0.7024,0.3323 T0.7105,0.3328 T0.7178,0.3338 T0.7238,0.3348 T0.7283,0.3363 Q0.7324,0.3383,0.7482,0.3403 T0.7689,0.3442 Q0.7697,0.3442,0.779,0.3477 T0.794,0.3536 T0.8074,0.3615 T0.8183,0.3729 Q0.8232,0.3808,0.8232,0.3848 Q0.8232,0.3867,0.8216,0.3892 T0.8151,0.3961 T0.8062,0.406 T0.7997,0.4179 T0.7952,0.4263 T0.7855,0.4308 T0.7616,0.4362 Q0.7543,0.4372,0.7445,0.4372 L0.7372,0.4372 L0.7307,0.4372 Q0.7202,0.4372,0.7153,0.4382 Q0.7064,0.4402,0.691,0.4402 L0.6845,0.4402 L0.6788,0.4402 Q0.6699,0.4402,0.6533,0.4436 T0.6318,0.451 Q0.6302,0.451,0.6302,0.452 Q0.6302,0.457,0.6626,0.459 Q0.6869,0.4609,0.7015,0.4659 Q0.7064,0.4669,0.7202,0.4669 L0.7311,0.4669 L0.7429,0.4669 Q0.7599,0.4669,0.7689,0.4688 Q0.7794,0.4708,0.7794,0.4738 L0.7786,0.4748 T0.777,0.4763 T0.7749,0.4773 L0.7737,0.4777 L0.7729,0.4777 Q0.7502,0.4797,0.738,0.4797 L0.7315,0.4797 L0.7291,0.4797 Q0.7226,0.4797,0.6869,0.4876 Q0.6821,0.4886,0.6768,0.4891 T0.6614,0.4901 T0.6456,0.4916 L0.6342,0.4916 Q0.6294,0.4916,0.6217,0.4911 T0.6115,0.4906 Q0.6083,0.4906,0.6054,0.4911 T0.5961,0.4936 T0.5815,0.4965 T0.5629,0.4985 Q0.5507,0.4985,0.5442,0.4995 T0.5345,0.5025 T0.5304,0.5084 L0.5296,0.5094 Q0.5296,0.5114,0.5316,0.5124 T0.5373,0.5138 T0.5442,0.5143 L0.5523,0.5143 L0.5604,0.5143 Q0.5637,0.5153,0.5677,0.5158 T0.5742,0.5173 T0.5811,0.5188 T0.5904,0.5193 L0.5977,0.5193 L0.6026,0.5193 Q0.6107,0.5193,0.6168,0.5208 T0.6294,0.5252 T0.6399,0.5292 Q0.6448,0.5302,0.6679,0.5341 T0.6983,0.541 Q0.7129,0.545,0.7664,0.55 Q0.7883,0.5519,0.7883,0.5559 Q0.7883,0.5579,0.7843,0.5608 T0.7721,0.5638 Q0.7697,0.5638,0.764,0.5633 T0.7559,0.5628 L0.7502,0.5628 Q0.7364,0.5648,0.6869,0.5648 L0.6699,0.5648 L0.6675,0.5648 Q0.665,0.5648,0.6602,0.5658 T0.6513,0.5668 Q0.6464,0.5668,0.6399,0.5658 Q0.6342,0.5648,0.6302,0.5648 T0.6225,0.5668 T0.6156,0.5697 Q0.6107,0.5707,0.6087,0.5707 T0.6034,0.5712 T0.5953,0.5717 Q0.5904,0.5727,0.5835,0.5727 T0.5718,0.5732 T0.562,0.5757 Q0.558,0.5767,0.5483,0.5767 L0.5349,0.5767 L0.5207,0.5767 Q0.5069,0.5767,0.5028,0.5786 Q0.4972,0.5816,0.4899,0.5816 Q0.4858,0.5816,0.4781,0.5806 T0.4663,0.5796 L0.4631,0.5796 Q0.4582,0.5796,0.4432,0.5821 T0.4144,0.5846 L0.4071,0.5846 L0.4011,0.5846 T0.3958,0.5851 T0.3921,0.5856 T0.3889,0.5861 T0.3865,0.5865 T0.384,0.5875 L0.382,0.5885 L0.3796,0.5895 L0.3771,0.5905 Q0.3731,0.5925,0.365,0.5925 L0.3601,0.5925 L0.3552,0.5925 Q0.3277,0.5925,0.3049,0.6044 Q0.3017,0.6053,0.296,0.6053 Q0.292,0.6053,0.2847,0.6044 T0.2741,0.6034 Q0.2693,0.6034,0.2547,0.6029 T0.236,0.6024 Q0.2222,0.6024,0.1922,0.6073 Q0.1906,0.6083,0.1882,0.6083 Q0.1833,0.6083,0.1711,0.6063 T0.1533,0.6044 Q0.1517,0.6034,0.1492,0.6034 Q0.1436,0.6034,0.135,0.6048 T0.1168,0.6078 T0.103,0.6093 Q0.0973,0.6103,0.0787,0.6103 L0.0604,0.6103 L0.0414,0.6103 L0.0235,0.6103 Q0.0024,0.6113,0.0024,0.6172 Q0.0024,0.6192,0.0032,0.6231 Q0.0049,0.6281,0.0333,0.6281 L0.0414,0.6281 L0.0479,0.6281 Q0.0519,0.6281,0.0624,0.6301 T0.0803,0.632 L0.0835,0.632 L0.0916,0.632 Q0.0989,0.632,0.1281,0.634 L0.206,0.6419 Q0.2117,0.6429,0.2174,0.6429 Q0.2214,0.6429,0.2271,0.6424 T0.2344,0.6419 L0.2376,0.6419 Q0.2393,0.6429,0.2417,0.6429 T0.2526,0.6419 T0.2652,0.6409 Q0.2741,0.6409,0.2782,0.6429 T0.2968,0.6459 T0.3293,0.6479 T0.352,0.6489 Q0.3552,0.6499,0.3585,0.6499 Q0.365,0.6499,0.3731,0.6474 T0.3844,0.6449 Q0.3885,0.6449,0.3917,0.6474 T0.4006,0.6499 Q0.4088,0.6499,0.4509,0.6578 Q0.4574,0.6588,0.4631,0.6588 Q0.4655,0.6588,0.4716,0.6583 T0.4834,0.6578 L0.4874,0.6578 Q0.5004,0.6588,0.5255,0.6637 Q0.532,0.6647,0.56,0.6667 T0.6148,0.6701 T0.644,0.6716 Q0.6464,0.6726,0.6594,0.6736 T0.6861,0.6756 T0.7048,0.6775 Q0.7097,0.6775,0.7186,0.6825 T0.738,0.6874 Q0.7518,0.6874,0.7729,0.6958 T0.8005,0.7122 Q0.8029,0.7141,0.8029,0.7151 Q0.8029,0.7191,0.7968,0.7216 T0.7818,0.726 T0.7705,0.729 L0.764,0.731 T0.7583,0.7324 T0.753,0.7334 T0.7486,0.7349 Q0.7251,0.7399,0.7161,0.7399 Q0.7097,0.7399,0.6951,0.7409 T0.6748,0.7418 L0.6703,0.7418 L0.6667,0.7418 Q0.6659,0.7418,0.6399,0.7448 L0.6334,0.7448 Q0.631,0.7448,0.6249,0.7443 T0.6148,0.7438 L0.6099,0.7438 Q0.601,0.7448,0.5856,0.7473 T0.5645,0.7507 Q0.5572,0.7517,0.5572,0.7547 T0.5616,0.7606 T0.5702,0.7646 Q0.5734,0.7646,0.5779,0.7651 T0.5864,0.7661 T0.5949,0.7671 T0.6018,0.7676 L0.6071,0.7676 T0.6135,0.7681 T0.62,0.769 T0.6269,0.771 T0.6342,0.7725 Q0.6391,0.7725,0.6464,0.7705 T0.6541,0.7685 L0.6553,0.7685 Q0.6586,0.7685,0.6699,0.771 T0.6853,0.7735 Q0.6902,0.7735,0.704,0.777 T0.7226,0.7834 Q0.7315,0.7873,0.7445,0.7883 Q0.747,0.7883,0.7676,0.7893 T0.7972,0.7913 Q0.7989,0.7913,0.8017,0.7918 T0.8058,0.7923 T0.809,0.7928 T0.8114,0.7933 T0.8131,0.7938 T0.8147,0.7943 T0.8159,0.7953 L0.8171,0.7967 T0.8175,0.7982 Q0.8183,0.7992,0.8183,0.8002 Q0.8183,0.8022,0.8155,0.8032 T0.807,0.8051 T0.7989,0.8071 Q0.7932,0.8091,0.7737,0.8091 L0.7413,0.8091 L0.7372,0.8091 Q0.7275,0.8091,0.721,0.8101 T0.7072,0.8136 T0.6983,0.817 Q0.6942,0.818,0.6825,0.818 T0.6602,0.819 T0.6456,0.822 Q0.6415,0.8249,0.6375,0.8249 Q0.6367,0.8249,0.6326,0.8239 T0.6253,0.8229 Q0.6034,0.8229,0.5953,0.8279 Q0.5912,0.8289,0.5848,0.8289 Q0.5775,0.8289,0.5645,0.8279 Q0.5629,0.8269,0.5604,0.8269 Q0.5523,0.8269,0.5328,0.8348 Q0.5296,0.8358,0.5264,0.8358 Q0.5247,0.8358,0.5203,0.8353 T0.5126,0.8348 Q0.5061,0.8348,0.4988,0.8358 Q0.4947,0.8368,0.4882,0.8378 Q0.4826,0.8388,0.4797,0.8388 T0.474,0.8398 T0.4704,0.8422 T0.4696,0.8462 T0.472,0.8501 T0.4781,0.8521 T0.4854,0.8526 L0.4935,0.8526 T0.4996,0.8536 Q0.5036,0.8536,0.515,0.8546 T0.5337,0.8566 Q0.5426,0.8576,0.5596,0.8576 L0.5657,0.8576 L0.571,0.8576 L0.5791,0.8576 Q0.5921,0.8586,0.6135,0.8591 T0.6383,0.8605 Q0.6683,0.8635,0.6764,0.8635 Q0.6999,0.8635,0.6999,0.8684 Q0.6999,0.8694,0.6967,0.8734 T0.6934,0.8803 L0.6934,0.8823 Q0.6951,0.8863,0.7137,0.8863 Q0.7218,0.8863,0.7267,0.8853 L0.7299,0.8853 Q0.734,0.8853,0.7409,0.8882 T0.7502,0.8912 Q0.7607,0.8942,0.794,0.8971 Q0.7972,0.8971,0.8078,0.9001 T0.8297,0.9031 Q0.8475,0.9031,0.8646,0.9065 T0.8824,0.9149 L0.8824,0.9159 Q0.8824,0.9169,0.882,0.9179 T0.8808,0.9194 L0.8792,0.9204 T0.8767,0.9214 T0.8743,0.9224 T0.8715,0.9228 L0.8686,0.9228 T0.8662,0.9233 T0.8637,0.9238 L0.8556,0.9238 T0.8447,0.9243 T0.8386,0.9268 Q0.837,0.9278,0.835,0.9288 T0.8305,0.9303 T0.8244,0.9308 T0.8175,0.9318 Q0.8127,0.9327,0.8017,0.9352 T0.7851,0.9397 Q0.777,0.9416,0.7705,0.9451 T0.7616,0.9496 Q0.7575,0.9505,0.7494,0.9505 L0.7405,0.9505 L0.7307,0.9505 Q0.7234,0.9505,0.7178,0.9515 Q0.7145,0.9515,0.7125,0.9535 T0.7105,0.9575 Q0.7105,0.9624,0.7194,0.9634 Q0.7567,0.9654,0.7664,0.9674 Q0.7745,0.9693,0.7762,0.9693 L0.7908,0.9693 L0.7948,0.9693 Q0.8005,0.9693,0.8058,0.9698 T0.8143,0.9718 T0.824,0.9753 T0.8354,0.9792 Q0.8483,0.9832,0.871,0.9832 L0.8739,0.9832 L0.8767,0.9832 Q0.8873,0.9832,0.8921,0.9842 Q0.8978,0.9852,0.9157,0.9896 T0.94,0.996 Q0.9424,0.996,0.9469,0.9965 T0.9566,0.997 T0.9676,0.9975 T0.9781,0.998 L0.9874,0.998 L0.9943,0.998 L0.9968,0.998 Z', type: CustomGlyphVectorType.FILL } }, // '\u{E0CB}' is unused '\u{E0CC}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0.5071 L0.8623,0.7102 L0.5865,0.7102 L0.4489,0.5071 L0.5865,0.3045 L0.8623,0.3045 Z M0.4135,0.7974 L0.2758,1 L0,1 L0,0.5944 L0.2758,0.5944 Z M0.4135,0.2026 L0.2758,0.4056 L0,0.4056 L0,0 L0.2758,0 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0CD}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.9726,0.5071 L0.8451,0.3184 L0.59,0.3184 L0.4624,0.5071 L0.59,0.6955 L0.8451,0.6955 Z M1,0.5071 L0.8588,0.716 L0.5768,0.716 L0.4355,0.5071 L0.5768,0.2982 L0.8588,0.2982 Z M0.401,0.7911 L0.2735,0.6028 L0.0236,0.6028 L0.0236,0.9799 L0.2735,0.9799 Z M0.4284,0.7911 L0.2872,1 L0,1 L0,0.5826 L0.2872,0.5826 Z M0.401,0.2089 L0.2735,0.0201 L0.0236,0.0201 L0.0236,0.3972 L0.2735,0.3972 Z M0.4284,0.2089 L0.2872,0.4174 L0,0.4174 L0,0 L0.2872,0 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0CE}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0.4996 Q1,0.5365,0.9924,0.568 T0.9718,0.6177 T0.9433,0.6363 L0.9425,0.6367 Q0.9355,0.6367,0.9285,0.6326 L0.9308,0.6326 Q0.9129,0.6195,0.9013,0.587 T0.8871,0.5164 T0.8943,0.4373 T0.9254,0.3687 L0.9254,0.3691 Q0.9339,0.3628,0.9425,0.3628 Q0.9584,0.3628,0.9716,0.3811 T0.9924,0.431 T1,0.4996 Z M0.9234,0.6321 L0.8776,0.7169 Q0.8791,0.732,0.8791,0.7441 Q0.8791,0.781,0.8716,0.8123 T0.8508,0.862 T0.8224,0.8809 L0.8216,0.8809 Q0.8154,0.8809,0.8092,0.8775 L0.8088,0.8775 Q0.7905,0.8633,0.7792,0.8297 T0.766,0.7571 T0.775,0.6768 T0.8088,0.6086 L0.7431,0.6065 Q0.719,0.6313,0.7072,0.6705 T0.6965,0.7487 T0.7097,0.823 T0.7423,0.8763 L0.7913,0.8775 L0.7314,0.9895 L0.4648,0.5038 L0.7299,0.0176 L0.7882,0.1229 L0.7404,0.1212 Q0.7244,0.138,0.7136,0.1617 T0.6982,0.2108 T0.6943,0.2632 T0.7003,0.3144 T0.7159,0.3586 T0.7396,0.3909 L0.8072,0.3926 Q0.7894,0.3796,0.7777,0.3479 T0.7635,0.2789 T0.7697,0.2011 T0.7987,0.1326 Q0.8092,0.1225,0.8204,0.1225 Q0.8321,0.1225,0.8426,0.1332 T0.8609,0.1623 T0.8731,0.2062 T0.8776,0.2592 Q0.8776,0.268,0.8768,0.2815 L0.9219,0.3628 L0.8644,0.3612 Q0.8403,0.3855,0.8284,0.4247 T0.8177,0.5029 T0.8309,0.5772 T0.8632,0.6305 Z M0.6759,0.6384 Q0.6576,0.625,0.6457,0.5914 T0.6319,0.5185 T0.6407,0.4375 T0.6751,0.3691 L0.609,0.367 Q0.5849,0.3918,0.5731,0.431 T0.5626,0.509 T0.5758,0.5833 T0.6082,0.6367 Z M0.6459,0.0025 L0.391,0.4551 L0.0035,0.4551 L0.246,0 Z M0.6553,1 L0.2421,1 L0,0.5449 L0.3875,0.5449 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0CF}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.4996,0 Q0.5562,0,0.5961,0.0166 T0.6364,0.0566 L0.6368,0.0575 Q0.6368,0.0646,0.6324,0.0713 L0.6324,0.0692 Q0.6196,0.0868,0.5872,0.0986 T0.5166,0.1128 T0.4373,0.1057 T0.3689,0.0747 L0.3694,0.0747 Q0.3627,0.0663,0.3627,0.0575 Q0.3627,0.0336,0.4028,0.0168 T0.4996,0 Z M0.6324,0.0768 L0.717,0.1225 Q0.7321,0.1212,0.744,0.1212 Q0.8007,0.1212,0.8406,0.1378 T0.8809,0.1779 L0.8813,0.1783 Q0.8813,0.1846,0.8778,0.1909 Q0.8636,0.2093,0.8299,0.2208 T0.7573,0.2343 T0.6769,0.2253 T0.6089,0.1913 L0.6067,0.2567 Q0.6315,0.281,0.6707,0.2928 T0.7489,0.3035 T0.8231,0.2903 T0.8764,0.258 L0.8778,0.2085 L0.9898,0.2685 L0.504,0.5352 L0.0177,0.2701 L0.1227,0.2118 L0.1213,0.2596 Q0.1457,0.2836,0.1851,0.2955 T0.2635,0.3062 T0.3377,0.293 T0.3911,0.2605 L0.3928,0.193 Q0.38,0.2106,0.3483,0.2221 T0.2792,0.2364 T0.2013,0.2303 T0.1324,0.2013 Q0.1222,0.1909,0.1222,0.1795 Q0.1222,0.156,0.1623,0.1393 T0.2591,0.1225 Q0.2679,0.1225,0.2817,0.1233 L0.3627,0.078 L0.3609,0.1359 Q0.3857,0.1598,0.4249,0.1718 T0.5031,0.1825 T0.5773,0.1693 T0.6306,0.1367 Z M0.6386,0.3242 Q0.6253,0.3427,0.5917,0.3544 T0.5186,0.3683 T0.4376,0.3595 T0.3689,0.3251 L0.3671,0.3909 Q0.3915,0.4148,0.4309,0.4268 T0.5093,0.4375 T0.5835,0.4243 T0.6368,0.3918 Z M0.0027,0.354 L0.4553,0.6091 L0.4553,0.9962 L0,0.7542 Z M1,0.3448 L1,0.7576 L0.5447,1 L0.5447,0.6128 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0D0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0.7699 Q0,0.7172,0.0277,0.6731 T0.103,0.6032 T0.2066,0.5774 Q0.2487,0.5774,0.2865,0.5922 T0.3526,0.6339 L0.3935,0.6715 Q0.4218,0.6984,0.438,0.7336 T0.4541,0.8075 Q0.4541,0.8602,0.4264,0.9046 T0.3511,0.9745 T0.2475,1 Q0.2054,1,0.1676,0.9852 T0.1016,0.9435 L0.0606,0.9059 Q0.0317,0.879,0.0159,0.8438 T0,0.7699 Z M0.5459,0.7699 Q0.5459,0.7172,0.5736,0.6731 T0.6489,0.6032 T0.7525,0.5774 Q0.7946,0.5774,0.8324,0.5922 T0.8984,0.6339 L0.9394,0.6715 Q0.9579,0.6892,0.9714,0.7105 T0.9925,0.7567 T1,0.8075 Q1,0.8602,0.9723,0.9046 T0.897,0.9745 T0.7934,1 Q0.7513,1,0.7135,0.9852 T0.6474,0.9435 L0.6065,0.9059 Q0.5776,0.879,0.5617,0.8438 T0.5459,0.7699 Z M0,0.1925 Q0,0.1398,0.0277,0.0957 T0.103,0.0258 T0.2066,0 Q0.2487,0,0.2865,0.0148 T0.3526,0.0565 L0.3935,0.0941 Q0.4218,0.121,0.438,0.1562 T0.4541,0.2301 Q0.4541,0.2828,0.4264,0.3269 T0.3511,0.3968 T0.2475,0.4226 Q0.2054,0.4226,0.1676,0.4078 T0.1016,0.3661 L0.0606,0.3285 Q0.0317,0.3016,0.0159,0.2664 T0,0.1925 Z M0.5459,0.1925 Q0.5459,0.1398,0.5736,0.0957 T0.6489,0.0258 T0.7525,0 Q0.7946,0,0.8324,0.0148 T0.8984,0.0565 L0.9394,0.0941 Q0.9677,0.121,0.9838,0.1562 T1,0.2301 Q1,0.2828,0.9723,0.3269 T0.897,0.3968 T0.7934,0.4226 Q0.7513,0.4226,0.7135,0.4078 T0.6474,0.3661 L0.6065,0.3285 Q0.5874,0.3108,0.5741,0.2895 T0.5534,0.2433 T0.5459,0.1925 Z M0.3301,0.6403 Q0.3306,0.6403,0.3312,0.6409 L0.3272,0.6371 L0.3272,0.6371 Z M0.8759,0.6403 Q0.8765,0.6403,0.8771,0.6409 Q0.8748,0.6387,0.8725,0.6371 L0.8731,0.6371 Z M0.3301,0.0629 Q0.3306,0.0629,0.3312,0.0634 L0.3272,0.0597 L0.3272,0.0602 Z M0.8759,0.0629 Q0.8765,0.0629,0.8771,0.0634 Q0.8748,0.0613,0.8725,0.0597 Q0.8731,0.0597,0.8731,0.0602 Z M0.779,0.4113 L0.7501,0.3849 Q0.7046,0.3844,0.6636,0.3661 Q0.7132,0.407,0.779,0.4113 Z M0.7957,0.4118 Q0.8477,0.4113,0.8918,0.3874 T0.9619,0.3223 T0.9885,0.2323 L0.9585,0.2048 Q0.9538,0.2769,0.8987,0.3282 T0.7657,0.3844 Z M0.9879,0.2167 Q0.9827,0.1554,0.9388,0.1097 Q0.9585,0.1478,0.959,0.1898 Z M0.2331,0.4113 L0.2043,0.3849 Q0.1587,0.3844,0.1177,0.3661 Q0.1673,0.407,0.2331,0.4113 Z M0.2499,0.4118 Q0.2885,0.4118,0.324,0.3973 T0.3852,0.3589 T0.4264,0.3019 T0.4426,0.2323 L0.4126,0.2048 Q0.408,0.2769,0.3529,0.3282 T0.2198,0.3844 Z M0.442,0.2167 Q0.4368,0.1554,0.393,0.1097 Q0.4126,0.1478,0.4132,0.1903 Z M0.779,0.9887 L0.7501,0.9624 Q0.7046,0.9618,0.6636,0.9435 Q0.7132,0.9844,0.779,0.9887 Z M0.7957,0.9892 Q0.8477,0.9887,0.8918,0.9648 T0.9619,0.8997 T0.9885,0.8097 L0.9585,0.7823 Q0.9538,0.8543,0.8987,0.9056 T0.7657,0.9618 Z M0.9879,0.7941 Q0.9827,0.7328,0.9388,0.6871 Q0.9585,0.7253,0.959,0.7672 Z M0.2331,0.9887 L0.2043,0.9624 Q0.1587,0.9618,0.1177,0.9435 Q0.1673,0.9844,0.2331,0.9887 Z M0.2499,0.9892 Q0.3018,0.9887,0.3459,0.9648 T0.416,0.8997 T0.4426,0.8102 L0.4126,0.7823 Q0.408,0.8543,0.3529,0.9056 T0.2198,0.9618 Z M0.442,0.7941 Q0.4368,0.7328,0.393,0.6871 Q0.4126,0.7253,0.4132,0.7672 Z M0.3191,0.6452 Q0.2943,0.622,0.2643,0.6091 T0.2066,0.5962 Q0.1795,0.5962,0.1497,0.6091 T0.0949,0.6441 T0.0537,0.6995 T0.0375,0.7699 Q0.0375,0.8419,0.0941,0.8941 Q0.1189,0.9172,0.1489,0.9304 T0.2066,0.9435 Q0.2337,0.9435,0.2634,0.9304 T0.3182,0.8952 T0.3595,0.8398 T0.3756,0.7699 Q0.3756,0.6978,0.3191,0.6452 Z M0.026,0.7731 L0.026,0.7699 Q0.026,0.6935,0.0854,0.6376 Q0.0658,0.6538,0.0514,0.6739 T0.0289,0.718 T0.0202,0.7677 Z M0.0271,0.7892 L0.0208,0.7833 Q0.0254,0.8419,0.0681,0.886 Q0.0664,0.8828,0.0646,0.879 Q0.0329,0.8382,0.0271,0.7892 Z M0.3866,0.7823 Q0.382,0.8511,0.3278,0.9016 Q0.3843,0.8565,0.3918,0.7871 Z M0.3872,0.7677 L0.393,0.7726 L0.393,0.7699 Q0.393,0.7349,0.3791,0.7038 Q0.3739,0.6973,0.3681,0.6914 Q0.3866,0.7274,0.3872,0.7677 Z M0.865,0.6452 Q0.8402,0.622,0.8102,0.6091 T0.7525,0.5962 Q0.7305,0.5962,0.7072,0.6043 T0.6619,0.628 T0.6223,0.6642 T0.5941,0.7124 T0.5834,0.7699 Q0.5834,0.8419,0.6399,0.8941 Q0.6561,0.9097,0.6757,0.9207 T0.7149,0.9376 T0.7525,0.9435 Q0.7796,0.9435,0.8093,0.9304 T0.8641,0.8952 T0.9054,0.8398 T0.9215,0.7699 Q0.9215,0.6978,0.865,0.6452 Z M0.5718,0.7731 L0.5718,0.7699 Q0.5718,0.6935,0.6313,0.6376 Q0.6013,0.6618,0.584,0.6954 T0.5661,0.7677 Z M0.573,0.7892 L0.5666,0.7833 Q0.5713,0.8419,0.614,0.886 Q0.6122,0.8828,0.6105,0.879 Q0.5788,0.8382,0.573,0.7892 Z M0.9325,0.7823 Q0.9279,0.8511,0.8736,0.9016 Q0.9308,0.8565,0.9377,0.7871 Z M0.9331,0.7672 L0.9388,0.7726 L0.9388,0.7699 Q0.9388,0.7349,0.925,0.7038 Q0.9198,0.6973,0.914,0.6914 Q0.9325,0.7274,0.9331,0.7672 Z M0.3191,0.0677 Q0.2943,0.0446,0.2643,0.0317 T0.2066,0.0188 Q0.1795,0.0188,0.1497,0.0317 T0.0949,0.0667 T0.0537,0.122 T0.0375,0.1925 Q0.0375,0.2645,0.0941,0.3167 Q0.1189,0.3398,0.1489,0.353 T0.2066,0.3661 Q0.2337,0.3661,0.2634,0.353 T0.3182,0.3177 T0.3595,0.2624 T0.3756,0.1925 Q0.3756,0.1204,0.3191,0.0677 Z M0.026,0.1957 L0.026,0.1925 Q0.026,0.1161,0.0854,0.0602 Q0.0554,0.0844,0.0381,0.118 T0.0202,0.1903 Z M0.0271,0.2118 L0.0208,0.2059 Q0.0254,0.2645,0.0681,0.3086 Q0.0664,0.3054,0.0646,0.3016 Q0.0329,0.2608,0.0271,0.2118 Z M0.3866,0.2048 Q0.382,0.2737,0.3278,0.3242 Q0.3849,0.279,0.3918,0.2097 Z M0.3872,0.1903 L0.393,0.1952 L0.393,0.1925 Q0.393,0.1581,0.3791,0.1263 Q0.3739,0.1199,0.3681,0.114 Q0.3866,0.15,0.3872,0.1903 Z M0.865,0.0677 Q0.8402,0.0446,0.8102,0.0317 T0.7525,0.0188 Q0.7253,0.0188,0.6956,0.0317 T0.6408,0.0667 T0.5995,0.122 T0.5834,0.1925 Q0.5834,0.2645,0.6399,0.3167 Q0.6647,0.3398,0.6947,0.353 T0.7525,0.3661 Q0.7796,0.3661,0.8093,0.353 T0.8641,0.3177 T0.9054,0.2624 T0.9215,0.1925 Q0.9215,0.1204,0.865,0.0677 Z M0.5718,0.1957 L0.5718,0.1925 Q0.5718,0.1161,0.6313,0.0602 Q0.6013,0.0844,0.584,0.118 T0.5661,0.1903 Z M0.573,0.2118 L0.5666,0.2059 Q0.5713,0.2645,0.614,0.3086 Q0.6122,0.3054,0.6105,0.3016 Q0.5788,0.2608,0.573,0.2118 Z M0.9325,0.2048 Q0.9279,0.2737,0.8736,0.3242 Q0.9308,0.279,0.9377,0.2097 Z M0.9331,0.1898 L0.9388,0.1952 L0.9388,0.1925 Q0.9388,0.1575,0.925,0.1263 Q0.9198,0.1199,0.914,0.114 Q0.9325,0.15,0.9331,0.1898 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0D1}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.7639,0.1974 L0.9593,0.1974 L0.9593,0.1443 L0.7639,0.1443 L0.7639,0.1974 Z M0.7586,0.109 L0.9727,0.109 Q0.9839,0.109,0.992,0.1153 T1,0.1291 L1,0.382 Q1,0.3906,0.9912,0.3964 T0.9727,0.4021 L0.7586,0.4021 Q0.7468,0.4021,0.739,0.3956 T0.7313,0.382 L0.7313,0.1291 Q0.7313,0.1246,0.7339,0.1207 T0.7406,0.1143 T0.7495,0.1104 T0.7586,0.109 Z M0.7639,0.6575 L0.9593,0.6575 L0.9593,0.6044 L0.7639,0.6044 L0.7639,0.6575 Z M0.7313,0.8347 L0.7313,0.5843 Q0.7313,0.5757,0.7401,0.5699 T0.7586,0.5641 L0.9706,0.5641 Q0.9818,0.5641,0.9898,0.5705 T0.9979,0.5843 L0.9979,0.8347 Q0.9979,0.8433,0.989,0.8491 T0.9706,0.8549 L0.7586,0.8549 Q0.7468,0.8549,0.739,0.8483 T0.7313,0.8347 Z M0,1 L0,0 L0.7607,0 L0.7607,1 L0,1 Z', type: CustomGlyphVectorType.FILL } }, '\u{E0D2}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.9764,0.0029 L0.3179,0.4552 L0,0.4552 L0,0 Z M1,1 L0,1 L0,0.5448 L0.3091,0.5448 Z', type: CustomGlyphVectorType.FILL } }, // '\u{E0C3}' is unused '\u{E0D4}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.0236,0.0029 L0.6824,0.4552 L1,0.4552 L1,0 Z M0,1 L1,1 L1,0.5448 L0.6912,0.5448 Z', type: CustomGlyphVectorType.FILL } }, // #endregion // #region Progress Indicators (EE00-EE0B) // initially added in Fira Code and later Nerd Fonts // https://github.com/ryanoasis/nerd-fonts/pull/1733 // Progress bars (EE00-EE05) '\u{EE00}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.05 L.1,.05 L.1,.95 L1,.95 L1,1 L0,1 Z' }, // PROGRESS BAR EMPTY START '\u{EE01}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.05 L0,.05 Z M0,.95 L1,.95 L1,1 L0,1 Z' }, // PROGRESS BAR EMPTY MIDDLE '\u{EE02}': { type: CustomGlyphDefinitionType.PATH, data: 'M1,0 L0,0 L0,.05 L.9,.05 L.9,.95 L0,.95 L0,1 L1,1 Z' }, // PROGRESS BAR EMPTY END '\u{EE03}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.05 L.1,.05 L.1,.95 L1,.95 L1,1 L0,1 Z M.25,.15 L1,.15 L1,.85 L.25,.85 Z' }, // PROGRESS BAR FILLED START '\u{EE04}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,.05 L0,.05 Z M0,.95 L1,.95 L1,1 L0,1 Z M0,.15 L1,.15 L1,.85 L0,.85 Z' }, // PROGRESS BAR FILLED MIDDLE '\u{EE05}': { type: CustomGlyphDefinitionType.PATH, data: 'M1,0 L0,0 L0,.05 L.9,.05 L.9,.95 L0,.95 L0,1 L1,1 Z M0,.15 L.75,.15 L.75,.85 L0,.85 Z' }, // PROGRESS BAR FILLED END // Progress spinners (EE06-EE0B) - 6-frame spinner animation using stroked arcs '\u{EE06}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.6574,0.303 Q0.623,0.291,0.5852,0.285 T0.5082,0.279 T0.4311,0.285 T0.359,0.303 L0.3082,0.248 Q0.3525,0.232,0.4041,0.2235 T0.5082,0.215 T0.6123,0.2235 T0.7082,0.248 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SPINNER FRAME 1 (12 o'clock) '\u{EE07}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.8557,0.582 L0.7656,0.551 Q0.7852,0.53,0.7951,0.507 T0.8049,0.46 Q0.8049,0.424,0.782,0.3905 T0.718,0.332 T0.6221,0.293 T0.5082,0.279 L0.5082,0.215 Q0.5607,0.215,0.6123,0.2235 T0.709,0.248 T0.7918,0.287 T0.8557,0.3375 T0.8959,0.3965 T0.9098,0.46 T0.8959,0.5235 T0.8557,0.582 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SPINNER FRAME 2 (2 o'clock) '\u{EE08}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.5082,0.705 Q0.482,0.705,0.4557,0.703 T0.4049,0.697 L0.4311,0.635 Q0.4508,0.638,0.4697,0.6395 T0.5082,0.641 Q0.5672,0.641,0.6221,0.627 T0.718,0.588 T0.782,0.5295 T0.8049,0.46 Q0.8049,0.436,0.7951,0.413 T0.7656,0.369 L0.8557,0.338 Q0.882,0.365,0.8959,0.3965 T0.9098,0.46 T0.8959,0.5235 T0.8557,0.5825 T0.7918,0.633 T0.709,0.672 T0.6123,0.6965 T0.5082,0.705 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SPINNER FRAME 3 (4 o'clock) '\u{EE09}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.5082,0.705 Q0.4557,0.705,0.4041,0.6965 T0.3074,0.672 T0.2246,0.633 T0.1607,0.5825 T0.1205,0.5235 T0.1066,0.46 L0.2115,0.46 Q0.2115,0.496,0.2344,0.5295 T0.2984,0.588 T0.3943,0.627 T0.5082,0.641 T0.6221,0.627 T0.718,0.588 T0.782,0.5295 T0.8049,0.46 L0.9098,0.46 Q0.9098,0.492,0.8959,0.5235 T0.8557,0.5825 T0.7918,0.633 T0.709,0.672 T0.6123,0.6965 T0.5082,0.705 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SPINNER FRAME 4 (6 o'clock) '\u{EE0A}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.5082,0.705 Q0.4557,0.705,0.4041,0.6965 T0.3074,0.672 T0.2246,0.633 T0.1607,0.5825 T0.1205,0.5235 T0.1066,0.46 T0.1205,0.3965 T0.1607,0.338 L0.2508,0.369 Q0.2311,0.39,0.2213,0.413 T0.2115,0.46 Q0.2115,0.496,0.2344,0.5295 T0.2984,0.588 T0.3943,0.627 T0.5082,0.641 Q0.5279,0.641,0.5467,0.6395 T0.5852,0.635 L0.6115,0.697 Q0.5869,0.701,0.5607,0.703 T0.5082,0.705 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SPINNER FRAME 5 (8 o'clock) '\u{EE0B}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0.1607,0.582 Q0.1344,0.555,0.1205,0.5235 T0.1066,0.46 T0.1205,0.3965 T0.1607,0.3375 T0.2246,0.287 T0.3074,0.248 T0.4041,0.2235 T0.5082,0.215 L0.5082,0.279 Q0.4492,0.279,0.3943,0.293 T0.2984,0.332 T0.2344,0.3905 T0.2115,0.46 Q0.2115,0.484,0.2213,0.507 T0.2508,0.551 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SPINNER FRAME 6 (10 o'clock) // #endregion // #region Git Branch Symbols (F5D0-F60D) // Initially added in Kitty (https://github.com/kovidgoyal/kitty/pull/7681) // then in Wezterm (https://github.com/wezterm/wezterm/issues/6328). // Straight lines (F5D0-F5D9) '\u{F5D0}': GitBranchSymbolsParts.LINE_H, // Same as 2500 '\u{F5D1}': GitBranchSymbolsParts.LINE_V, // Same as 2502 '\u{F5D2}': GitBranchSymbolsParts.FADE_RIGHT, '\u{F5D3}': GitBranchSymbolsParts.FADE_LEFT, '\u{F5D4}': GitBranchSymbolsParts.FADE_DOWN, '\u{F5D5}': GitBranchSymbolsParts.FADE_UP, // Curved lines (F5D6-F5D9) '\u{F5D6}': GitBranchSymbolsParts.CURVE_DOWN_RIGHT, // Same as 256D) '\u{F5D7}': GitBranchSymbolsParts.CURVE_DOWN_LEFT, // Same as 256E) '\u{F5D8}': GitBranchSymbolsParts.CURVE_UP_RIGHT, // Same as 2570) '\u{F5D9}': GitBranchSymbolsParts.CURVE_UP_LEFT, // Same as 256F) // Branching lines (F5DA-F5ED) '\u{F5DA}': [GitBranchSymbolsParts.LINE_V, GitBranchSymbolsParts.CURVE_UP_RIGHT], '\u{F5DB}': [GitBranchSymbolsParts.LINE_V, GitBranchSymbolsParts.CURVE_DOWN_RIGHT], '\u{F5DC}': [GitBranchSymbolsParts.CURVE_DOWN_RIGHT, GitBranchSymbolsParts.CURVE_UP_RIGHT], '\u{F5DD}': [GitBranchSymbolsParts.LINE_V, GitBranchSymbolsParts.CURVE_UP_LEFT], '\u{F5DE}': [GitBranchSymbolsParts.LINE_V, GitBranchSymbolsParts.CURVE_DOWN_LEFT], '\u{F5DF}': [GitBranchSymbolsParts.CURVE_DOWN_LEFT, GitBranchSymbolsParts.CURVE_UP_LEFT], '\u{F5E0}': [GitBranchSymbolsParts.LINE_H, GitBranchSymbolsParts.CURVE_DOWN_LEFT], '\u{F5E1}': [GitBranchSymbolsParts.LINE_H, GitBranchSymbolsParts.CURVE_DOWN_RIGHT], '\u{F5E2}': [GitBranchSymbolsParts.CURVE_DOWN_LEFT, GitBranchSymbolsParts.CURVE_DOWN_RIGHT], '\u{F5E3}': [GitBranchSymbolsParts.LINE_H, GitBranchSymbolsParts.CURVE_UP_LEFT], '\u{F5E4}': [GitBranchSymbolsParts.LINE_H, GitBranchSymbolsParts.CURVE_UP_RIGHT], '\u{F5E5}': [GitBranchSymbolsParts.CURVE_UP_LEFT, GitBranchSymbolsParts.CURVE_UP_RIGHT], '\u{F5E6}': [GitBranchSymbolsParts.LINE_V, GitBranchSymbolsParts.CURVE_UP_LEFT, GitBranchSymbolsParts.CURVE_UP_RIGHT], '\u{F5E7}': [GitBranchSymbolsParts.LINE_V, GitBranchSymbolsParts.CURVE_DOWN_LEFT, GitBranchSymbolsParts.CURVE_DOWN_RIGHT], '\u{F5E8}': [GitBranchSymbolsParts.LINE_H, GitBranchSymbolsParts.CURVE_DOWN_LEFT, GitBranchSymbolsParts.CURVE_UP_LEFT], '\u{F5E9}': [GitBranchSymbolsParts.LINE_H, GitBranchSymbolsParts.CURVE_DOWN_RIGHT, GitBranchSymbolsParts.CURVE_UP_RIGHT], '\u{F5EA}': [GitBranchSymbolsParts.LINE_V, GitBranchSymbolsParts.CURVE_DOWN_RIGHT, GitBranchSymbolsParts.CURVE_UP_LEFT], '\u{F5EB}': [GitBranchSymbolsParts.LINE_V, GitBranchSymbolsParts.CURVE_DOWN_LEFT, GitBranchSymbolsParts.CURVE_UP_RIGHT], '\u{F5EC}': [GitBranchSymbolsParts.LINE_H, GitBranchSymbolsParts.CURVE_DOWN_RIGHT, GitBranchSymbolsParts.CURVE_UP_LEFT], '\u{F5ED}': [GitBranchSymbolsParts.LINE_H, GitBranchSymbolsParts.CURVE_DOWN_LEFT, GitBranchSymbolsParts.CURVE_UP_RIGHT], // Nodes (F5EE-F5FB) '\u{F5EE}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE], '\u{F5EF}': GitBranchSymbolsParts.NODE_STROKE, '\u{F5F0}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5F1}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5F2}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_LEFT], '\u{F5F3}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_LEFT], '\u{F5F4}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_LEFT, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5F5}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_LEFT, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5F6}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN], '\u{F5F7}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN], '\u{F5F8}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP], '\u{F5F9}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP], '\u{F5FA}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_UP], '\u{F5FB}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_UP], // Extended Nodes (F5FC-F60D) // These were added a little later https://github.com/kovidgoyal/kitty/pull/7805 '\u{F5FC}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5FD}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F5FE}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_LEFT], '\u{F5FF}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_LEFT], '\u{F600}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F601}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F602}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP, GitBranchSymbolsParts.NODE_LINE_LEFT], '\u{F603}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP, GitBranchSymbolsParts.NODE_LINE_LEFT], '\u{F604}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F605}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F606}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_LEFT], '\u{F607}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_LEFT], '\u{F608}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_LEFT, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F609}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_LEFT, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F60A}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP, GitBranchSymbolsParts.NODE_LINE_LEFT, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F60B}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP, GitBranchSymbolsParts.NODE_LINE_LEFT, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F60C}': [GitBranchSymbolsParts.NODE_FILL, GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_LEFT, GitBranchSymbolsParts.NODE_LINE_RIGHT], '\u{F60D}': [GitBranchSymbolsParts.NODE_STROKE, GitBranchSymbolsParts.NODE_LINE_UP, GitBranchSymbolsParts.NODE_LINE_DOWN, GitBranchSymbolsParts.NODE_LINE_LEFT, GitBranchSymbolsParts.NODE_LINE_RIGHT], // #endregion // #region Symbols for Legacy Computing (1FB00-1FB3B) // https://www.unicode.org/charts/PDF/U1FB00.pdf // Block mosaic terminal graphic characters (1FB00-1FB3B) // The term "sextant" refers to block mosaics divided into six parts. '\u{1FB00}': sextant(0b000001), // BLOCK SEXTANT-1 '\u{1FB01}': sextant(0b000010), // BLOCK SEXTANT-2 '\u{1FB02}': sextant(0b000011), // BLOCK SEXTANT-12 (upper one third block) '\u{1FB03}': sextant(0b000100), // BLOCK SEXTANT-3 '\u{1FB04}': sextant(0b000101), // BLOCK SEXTANT-13 '\u{1FB05}': sextant(0b000110), // BLOCK SEXTANT-23 '\u{1FB06}': sextant(0b000111), // BLOCK SEXTANT-123 '\u{1FB07}': sextant(0b001000), // BLOCK SEXTANT-4 '\u{1FB08}': sextant(0b001001), // BLOCK SEXTANT-14 '\u{1FB09}': sextant(0b001010), // BLOCK SEXTANT-24 '\u{1FB0A}': sextant(0b001011), // BLOCK SEXTANT-124 '\u{1FB0B}': sextant(0b001100), // BLOCK SEXTANT-34 (middle one third block) '\u{1FB0C}': sextant(0b001101), // BLOCK SEXTANT-134 '\u{1FB0D}': sextant(0b001110), // BLOCK SEXTANT-234 '\u{1FB0E}': sextant(0b001111), // BLOCK SEXTANT-1234 (upper two thirds block) '\u{1FB0F}': sextant(0b010000), // BLOCK SEXTANT-5 '\u{1FB10}': sextant(0b010001), // BLOCK SEXTANT-15 '\u{1FB11}': sextant(0b010010), // BLOCK SEXTANT-25 '\u{1FB12}': sextant(0b010011), // BLOCK SEXTANT-125 '\u{1FB13}': sextant(0b010100), // BLOCK SEXTANT-35 '\u{1FB14}': sextant(0b010110), // BLOCK SEXTANT-235 '\u{1FB15}': sextant(0b010111), // BLOCK SEXTANT-1235 '\u{1FB16}': sextant(0b011000), // BLOCK SEXTANT-45 '\u{1FB17}': sextant(0b011001), // BLOCK SEXTANT-145 '\u{1FB18}': sextant(0b011010), // BLOCK SEXTANT-245 '\u{1FB19}': sextant(0b011011), // BLOCK SEXTANT-1245 '\u{1FB1A}': sextant(0b011100), // BLOCK SEXTANT-345 '\u{1FB1B}': sextant(0b011101), // BLOCK SEXTANT-1345 '\u{1FB1C}': sextant(0b011110), // BLOCK SEXTANT-2345 '\u{1FB1D}': sextant(0b011111), // BLOCK SEXTANT-12345 '\u{1FB1E}': sextant(0b100000), // BLOCK SEXTANT-6 '\u{1FB1F}': sextant(0b100001), // BLOCK SEXTANT-16 '\u{1FB20}': sextant(0b100010), // BLOCK SEXTANT-26 '\u{1FB21}': sextant(0b100011), // BLOCK SEXTANT-126 '\u{1FB22}': sextant(0b100100), // BLOCK SEXTANT-36 '\u{1FB23}': sextant(0b100101), // BLOCK SEXTANT-136 '\u{1FB24}': sextant(0b100110), // BLOCK SEXTANT-236 '\u{1FB25}': sextant(0b100111), // BLOCK SEXTANT-1236 '\u{1FB26}': sextant(0b101000), // BLOCK SEXTANT-46 '\u{1FB27}': sextant(0b101001), // BLOCK SEXTANT-146 '\u{1FB28}': sextant(0b101011), // BLOCK SEXTANT-1246 '\u{1FB29}': sextant(0b101100), // BLOCK SEXTANT-346 '\u{1FB2A}': sextant(0b101101), // BLOCK SEXTANT-1346 '\u{1FB2B}': sextant(0b101110), // BLOCK SEXTANT-2346 '\u{1FB2C}': sextant(0b101111), // BLOCK SEXTANT-12346 '\u{1FB2D}': sextant(0b110000), // BLOCK SEXTANT-56 (lower one third block) '\u{1FB2E}': sextant(0b110001), // BLOCK SEXTANT-156 '\u{1FB2F}': sextant(0b110010), // BLOCK SEXTANT-256 '\u{1FB30}': sextant(0b110011), // BLOCK SEXTANT-1256 (upper and lower one third block) '\u{1FB31}': sextant(0b110100), // BLOCK SEXTANT-356 '\u{1FB32}': sextant(0b110101), // BLOCK SEXTANT-1356 '\u{1FB33}': sextant(0b110110), // BLOCK SEXTANT-2356 '\u{1FB34}': sextant(0b110111), // BLOCK SEXTANT-12356 '\u{1FB35}': sextant(0b111000), // BLOCK SEXTANT-456 '\u{1FB36}': sextant(0b111001), // BLOCK SEXTANT-1456 '\u{1FB37}': sextant(0b111010), // BLOCK SEXTANT-2456 '\u{1FB38}': sextant(0b111011), // BLOCK SEXTANT-12456 '\u{1FB39}': sextant(0b111100), // BLOCK SEXTANT-3456 (lower two thirds block) '\u{1FB3A}': sextant(0b111101), // BLOCK SEXTANT-13456 '\u{1FB3B}': sextant(0b111110), // BLOCK SEXTANT-23456 // Smooth mosaic terminal graphic characters (1FB3C-1FB6F) '\u{1FB3C}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.6667 L0,1 L0.5,1 Z' }, // LOWER LEFT BLOCK DIAGONAL LOWER MIDDLE LEFT TO LOWER CENTRE '\u{1FB3D}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.6667 L0,1 L1,1 Z' }, // LOWER LEFT BLOCK DIAGONAL LOWER MIDDLE LEFT TO LOWER RIGHT '\u{1FB3E}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.3333 L0,1 L0.5,1 Z' }, // LOWER LEFT BLOCK DIAGONAL UPPER MIDDLE LEFT TO LOWER CENTRE '\u{1FB3F}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.3333 L0,1 L1,1 Z' }, // LOWER LEFT BLOCK DIAGONAL UPPER MIDDLE LEFT TO LOWER RIGHT '\u{1FB40}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L0,1 L0.5,1 Z' }, // LOWER LEFT BLOCK DIAGONAL UPPER LEFT TO LOWER CENTRE '\u{1FB41}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.3333 L0.5,0 L1,0 L1,1 L0,1 Z' }, // LOWER RIGHT BLOCK DIAGONAL UPPER MIDDLE LEFT TO UPPER CENTRE '\u{1FB42}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.3333 L1,0 L1,1 L0,1 Z' }, // LOWER RIGHT BLOCK DIAGONAL UPPER MIDDLE LEFT TO UPPER RIGHT '\u{1FB43}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.6667 L0.5,0 L1,0 L1,1 L0,1 Z' }, // LOWER RIGHT BLOCK DIAGONAL LOWER MIDDLE LEFT TO UPPER CENTRE '\u{1FB44}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.6667 L1,0 L1,1 L0,1 Z' }, // LOWER RIGHT BLOCK DIAGONAL LOWER MIDDLE LEFT TO UPPER RIGHT '\u{1FB45}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,1 L0.5,0 L1,0 L1,1 Z' }, // LOWER RIGHT BLOCK DIAGONAL LOWER LEFT TO UPPER CENTRE '\u{1FB46}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.6667 L1,0.3333 L1,1 L0,1 Z' }, // LOWER RIGHT BLOCK DIAGONAL LOWER MIDDLE LEFT TO UPPER MIDDLE RIGHT '\u{1FB47}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.5,1 L1,0.6667 L1,1 Z' }, // LOWER RIGHT BLOCK DIAGONAL LOWER CENTRE TO LOWER MIDDLE RIGHT '\u{1FB48}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,1 L1,0.6667 L1,1 Z' }, // LOWER RIGHT BLOCK DIAGONAL LOWER LEFT TO LOWER MIDDLE RIGHT '\u{1FB49}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.5,1 L1,0.3333 L1,1 Z' }, // LOWER RIGHT BLOCK DIAGONAL LOWER CENTRE TO UPPER MIDDLE RIGHT '\u{1FB4A}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,1 L1,0.3333 L1,1 Z' }, // LOWER RIGHT BLOCK DIAGONAL LOWER LEFT TO UPPER MIDDLE RIGHT '\u{1FB4B}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.5,1 L1,0 L1,1 Z' }, // LOWER RIGHT BLOCK DIAGONAL LOWER CENTRE TO UPPER RIGHT '\u{1FB4C}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.5,0 L0,0 L0,1 L1,1 L1,0.3333 Z' }, // LOWER LEFT BLOCK DIAGONAL UPPER CENTRE TO UPPER MIDDLE RIGHT '\u{1FB4D}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L0,1 L1,1 L1,0.3333 Z' }, // LOWER LEFT BLOCK DIAGONAL UPPER LEFT TO UPPER MIDDLE RIGHT '\u{1FB4E}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.5,0 L0,0 L0,1 L1,1 L1,0.6667 Z' }, // LOWER LEFT BLOCK DIAGONAL UPPER CENTRE TO LOWER MIDDLE RIGHT '\u{1FB4F}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L0,1 L1,1 L1,0.6667 Z' }, // LOWER LEFT BLOCK DIAGONAL UPPER LEFT TO LOWER MIDDLE RIGHT '\u{1FB50}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.5,0 L0,0 L0,1 L1,1 Z' }, // LOWER LEFT BLOCK DIAGONAL UPPER CENTRE TO LOWER RIGHT '\u{1FB51}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.3333 L0,1 L1,1 L1,0.6667 Z' }, // LOWER LEFT BLOCK DIAGONAL UPPER MIDDLE LEFT TO LOWER MIDDLE RIGHT '\u{1FB52}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.6667 L0.5,1 L1,1 L1,0 L0,0 Z' }, // UPPER RIGHT BLOCK DIAGONAL LOWER MIDDLE LEFT TO LOWER CENTRE '\u{1FB53}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.6667 L1,1 L1,0 L0,0 Z' }, // UPPER RIGHT BLOCK DIAGONAL LOWER MIDDLE LEFT TO LOWER RIGHT '\u{1FB54}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.3333 L0.5,1 L1,1 L1,0 L0,0 Z' }, // UPPER RIGHT BLOCK DIAGONAL UPPER MIDDLE LEFT TO LOWER CENTRE '\u{1FB55}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.3333 L1,1 L1,0 L0,0 Z' }, // UPPER RIGHT BLOCK DIAGONAL UPPER MIDDLE LEFT TO LOWER RIGHT '\u{1FB56}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L0.5,1 L1,1 L1,0 Z' }, // UPPER RIGHT BLOCK DIAGONAL UPPER LEFT TO LOWER CENTRE '\u{1FB57}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.3333 L0,0 L0.5,0 Z' }, // UPPER LEFT BLOCK DIAGONAL UPPER MIDDLE LEFT TO UPPER CENTRE '\u{1FB58}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.3333 L0,0 L1,0 Z' }, // UPPER LEFT BLOCK DIAGONAL UPPER MIDDLE LEFT TO UPPER RIGHT '\u{1FB59}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.6667 L0,0 L0.5,0 Z' }, // UPPER LEFT BLOCK DIAGONAL LOWER MIDDLE LEFT TO UPPER CENTRE '\u{1FB5A}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.6667 L0,0 L1,0 Z' }, // UPPER LEFT BLOCK DIAGONAL LOWER MIDDLE LEFT TO UPPER RIGHT '\u{1FB5B}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,1 L0,0 L0.5,0 Z' }, // UPPER LEFT BLOCK DIAGONAL LOWER LEFT TO UPPER CENTRE '\u{1FB5C}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.6667 L0,0 L1,0 L1,0.3333 Z' }, // UPPER LEFT BLOCK DIAGONAL LOWER MIDDLE LEFT TO UPPER MIDDLE RIGHT '\u{1FB5D}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.5,1 L0,1 L0,0 L1,0 L1,0.6667 Z' }, // UPPER LEFT BLOCK DIAGONAL LOWER CENTRE TO LOWER MIDDLE RIGHT '\u{1FB5E}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,1 L0,0 L1,0 L1,0.6667 Z' }, // UPPER LEFT BLOCK DIAGONAL LOWER LEFT TO LOWER MIDDLE RIGHT '\u{1FB5F}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.5,1 L0,1 L0,0 L1,0 L1,0.3333 Z' }, // UPPER LEFT BLOCK DIAGONAL LOWER CENTRE TO UPPER MIDDLE RIGHT '\u{1FB60}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,1 L0,0 L1,0 L1,0.3333 Z' }, // UPPER LEFT BLOCK DIAGONAL LOWER LEFT TO UPPER MIDDLE RIGHT '\u{1FB61}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.5,1 L0,1 L0,0 L1,0 Z' }, // UPPER LEFT BLOCK DIAGONAL LOWER CENTRE TO UPPER RIGHT '\u{1FB62}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.5,0 L1,0 L1,0.3333 Z' }, // UPPER RIGHT BLOCK DIAGONAL UPPER CENTRE TO UPPER MIDDLE RIGHT '\u{1FB63}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,0.3333 Z' }, // UPPER RIGHT BLOCK DIAGONAL UPPER LEFT TO UPPER MIDDLE RIGHT '\u{1FB64}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.5,0 L1,0 L1,0.6667 Z' }, // UPPER RIGHT BLOCK DIAGONAL UPPER CENTRE TO LOWER MIDDLE RIGHT '\u{1FB65}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,0.6667 Z' }, // UPPER RIGHT BLOCK DIAGONAL UPPER LEFT TO LOWER MIDDLE RIGHT '\u{1FB66}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.5,0 L1,0 L1,1 Z' }, // UPPER RIGHT BLOCK DIAGONAL UPPER CENTRE TO LOWER RIGHT '\u{1FB67}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.3333 L1,0.6667 L1,0 L0,0 Z' }, // UPPER RIGHT BLOCK DIAGONAL UPPER MIDDLE LEFT TO LOWER MIDDLE RIGHT '\u{1FB68}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,1 L0,1 L0.5,0.5 Z' }, // UPPER AND RIGHT AND LOWER TRIANGULAR THREE QUARTERS BLOCK (missing left) '\u{1FB69}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L0.5,0.5 L1,0 L1,1 L0,1 Z' }, // LEFT AND LOWER AND RIGHT TRIANGULAR THREE QUARTERS BLOCK (missing upper) '\u{1FB6A}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L0.5,0.5 L1,1 L0,1 Z' }, // UPPER AND LEFT AND LOWER TRIANGULAR THREE QUARTERS BLOCK (missing right) '\u{1FB6B}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,1 L0.5,0.5 L0,1 Z' }, // LEFT AND UPPER AND RIGHT TRIANGULAR THREE QUARTERS BLOCK (missing lower) '\u{1FB6C}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L0.5,0.5 L0,1 Z' }, // LEFT TRIANGULAR ONE QUARTER BLOCK '\u{1FB6D}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L0.5,0.5 Z' }, // UPPER TRIANGULAR ONE QUARTER BLOCK '\u{1FB6E}': { type: CustomGlyphDefinitionType.PATH, data: 'M1,0 L1,1 L0.5,0.5 Z' }, // RIGHT TRIANGULAR ONE QUARTER BLOCK '\u{1FB6F}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,1 L1,1 L0.5,0.5 Z' }, // LOWER TRIANGULAR ONE QUARTER BLOCK // Block elements (1FB70-1FB80) '\u{1FB70}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 1, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-2 '\u{1FB71}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 2, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-3 '\u{1FB72}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 3, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-4 '\u{1FB73}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-5 '\u{1FB74}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 5, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-6 '\u{1FB75}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 6, y: 0, w: 1, h: 8 }] }, // VERTICAL ONE EIGHTH BLOCK-7 '\u{1FB76}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 1, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-2 '\u{1FB77}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 2, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-3 '\u{1FB78}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 3, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-4 '\u{1FB79}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 4, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-5 '\u{1FB7A}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 5, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-6 '\u{1FB7B}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 6, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-7 '\u{1FB7C}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 1, h: 8 }, { x: 0, y: 7, w: 8, h: 1 }] }, // LEFT AND LOWER ONE EIGHTH BLOCK '\u{1FB7D}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 1, h: 8 }, { x: 0, y: 0, w: 8, h: 1 }] }, // LEFT AND UPPER ONE EIGHTH BLOCK '\u{1FB7E}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 7, y: 0, w: 1, h: 8 }, { x: 0, y: 0, w: 8, h: 1 }] }, // RIGHT AND UPPER ONE EIGHTH BLOCK '\u{1FB7F}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 7, y: 0, w: 1, h: 8 }, { x: 0, y: 7, w: 8, h: 1 }] }, // RIGHT AND LOWER ONE EIGHTH BLOCK '\u{1FB80}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 1 }, { x: 0, y: 7, w: 8, h: 1 }] }, // UPPER AND LOWER ONE EIGHTH BLOCK // Window title bar (1FB81-1FB81) '\u{1FB81}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 1 }, { x: 0, y: 2, w: 8, h: 1 }, { x: 0, y: 4, w: 8, h: 1 }, { x: 0, y: 7, w: 8, h: 1 }] }, // HORIZONTAL ONE EIGHTH BLOCK-1358 // Block elements (1FB82-1FB8B) '\u{1FB82}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 2 }] }, // UPPER ONE QUARTER BLOCK '\u{1FB83}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 3 }] }, // UPPER THREE EIGHTHS BLOCK '\u{1FB84}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 5 }] }, // UPPER FIVE EIGHTHS BLOCK '\u{1FB85}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 6 }] }, // UPPER THREE QUARTERS BLOCK '\u{1FB86}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 7 }] }, // UPPER SEVEN EIGHTHS BLOCK '\u{1FB87}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 6, y: 0, w: 2, h: 8 }] }, // RIGHT ONE QUARTER BLOCK '\u{1FB88}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 5, y: 0, w: 3, h: 8 }] }, // RIGHT THREE EIGHTHS B0OCK '\u{1FB89}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 3, y: 0, w: 5, h: 8 }] }, // RIGHT FIVE EIGHTHS BL0CK '\u{1FB8A}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 2, y: 0, w: 6, h: 8 }] }, // RIGHT THREE QUARTERS 0LOCK '\u{1FB8B}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 1, y: 0, w: 7, h: 8 }] }, // RIGHT SEVEN EIGHTHS B0OCK // Rectangular shade characters (1FB8C-1FB94) '\u{1FB8C}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // LEFT HALF MEDIUM SHADE [1, 0], [0, 1] ], clipPath: 'M0,0 L0.5,0 L0.5,1 L0,1 Z' }, '\u{1FB8D}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // RIGHT HALF MEDIUM SHADE [1, 0], [0, 1] ], clipPath: 'M0.5,0 L1,0 L1,1 L0.5,1 Z' }, '\u{1FB8E}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // UPPER HALF MEDIUM SHADE [1, 0], [0, 1] ], clipPath: 'M0,0 L1,0 L1,0.5 L0,0.5 Z' }, '\u{1FB8F}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // LOWER HALF MEDIUM SHADE [1, 0], [0, 1] ], clipPath: 'M0,0.5 L1,0.5 L1,1 L0,1 Z' }, '\u{1FB90}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // INVERSE MEDIUM SHADE [0, 1], [1, 0] ] }, '\u{1FB91}': [ // UPPER HALF BLOCK AND LOWER HALF INVERSE MEDIUM SHADE { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ [0, 1], [1, 0] ], clipPath: 'M0,0.5 L1,0.5 L1,1 L0,1 Z' }, { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 0, w: 8, h: 4 }] } ], '\u{1FB92}': [ // UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ [0, 1], [1, 0] ], clipPath: 'M0,0 L1,0 L1,0.5 L0,0.5 Z' }, { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 4, w: 8, h: 4 }] } ], // 1FB93 is '\u{1FB94}': [ // LEFT HALF INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ [0, 1], [1, 0] ], clipPath: 'M0,0 L0.5,0 L0.5,1 L0,1 Z' }, { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 0, w: 4, h: 8 }] } ], // Fill characters (1FB95-1FB97) '\u{1FB95}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [ // CHECKER BOARD FILL { x: 0, y: 0, w: 2, h: 2 }, { x: 4, y: 0, w: 2, h: 2 }, { x: 2, y: 2, w: 2, h: 2 }, { x: 6, y: 2, w: 2, h: 2 }, { x: 0, y: 4, w: 2, h: 2 }, { x: 4, y: 4, w: 2, h: 2 }, { x: 2, y: 6, w: 2, h: 2 }, { x: 6, y: 6, w: 2, h: 2 } ] }, '\u{1FB96}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [ // INVERSE CHECKER BOARD FILL { x: 2, y: 0, w: 2, h: 2 }, { x: 6, y: 0, w: 2, h: 2 }, { x: 0, y: 2, w: 2, h: 2 }, { x: 4, y: 2, w: 2, h: 2 }, { x: 2, y: 4, w: 2, h: 2 }, { x: 6, y: 4, w: 2, h: 2 }, { x: 0, y: 6, w: 2, h: 2 }, { x: 4, y: 6, w: 2, h: 2 } ] }, '\u{1FB97}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [ // HEAVY HORIZONTAL FILL (upper middle and lower one quarter block) { x: 0, y: 2, w: 8, h: 2 }, { x: 0, y: 6, w: 8, h: 2 } ] }, // Diagonal fill characters (1FB98-1FB99) '\u{1FB98}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M-0.25,-0.25 L1.25,1.25 M-0.25,0 L1,1.25 M-0.25,0.25 L0.75,1.25 M-0.25,0.5 L0.5,1.25 M0,-0.25 L1.25,1 M0.25,-0.25 L1.25,0.75 M0.5,-0.25 L1.25,0.5 M-0.25,0.75 L0.25,1.25 M0.75,-0.25 L1.25,0.25', strokeWidth: 1 }, // UPPER LEFT TO LOWER RIGHT FILL '\u{1FB99}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M-0.25,0.5 L0.5,-0.25 M-0.25,0.75 L0.75,-0.25 M-0.25,1 L1,-0.25 M-0.25,1.25 L1.25,-0.25 M0,1.25 L1.25,0 M0.25,1.25 L1.25,0.25 M0.5,1.25 L1.25,0.5 M-0.25,0.25 L0.25,-0.25 M0.75,1.25 L1.25,0.75', strokeWidth: 1 }, // UPPER RIGHT TO LOWER LEFT FILL // Smooth mosaic terminal graphic characters (1FB9A-1FB9B) '\u{1FB9A}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L.5,.5 L0,1 L1,1 L.5,.5 L1,0', type: CustomGlyphVectorType.FILL } }, // UPPER AND LOWER TRIANGULAR HALF BLOCK '\u{1FB9B}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L.5,.5 L1,0 L1,1 L.5,.5 L0,1', type: CustomGlyphVectorType.FILL } }, // LEFT AND RIGHT TRIANGULAR HALF BLOCK // Triangular shade characters (1FB9C-1FB9F) '\u{1FB9C}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // UPPER LEFT TRIANGULAR MEDIUM SHADE [1, 0], [0, 1] ], clipPath: 'M0,0 L1,0 L0,1 Z' }, '\u{1FB9D}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // UPPER RIGHT TRIANGULAR MEDIUM SHADE [1, 0], [0, 1] ], clipPath: 'M0,0 L1,0 L1,1 Z' }, '\u{1FB9E}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // LOWER RIGHT TRIANGULAR MEDIUM SHADE [1, 0], [0, 1] ], clipPath: 'M1,0 L1,1 L0,1 Z' }, '\u{1FB9F}': { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: [ // LOWER LEFT TRIANGULAR MEDIUM SHADE [1, 0], [0, 1] ], clipPath: 'M0,0 L1,1 L0,1 Z' }, // Character cell diagonals (1FBA0-1FBAE) '\u{1FBA0}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,0 L0,.5', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT '\u{1FBA1}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,0 L1,.5', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT '\u{1FBA2}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,.5 L.5,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO LOWER CENTRE '\u{1FBA3}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M1,.5 L.5,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE RIGHT TO LOWER CENTRE '\u{1FBA4}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,0 L0,.5 L.5,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT TO LOWER CENTRE '\u{1FBA5}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,0 L1,.5 L.5,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT TO LOWER CENTRE '\u{1FBA6}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,.5 L.5,1 L1,.5', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO LOWER CENTRE TO MIDDLE RIGHT '\u{1FBA7}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,.5 L.5,0 L1,.5', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO UPPER CENTRE TO MIDDLE RIGHT '\u{1FBA8}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,0 L0,.5 M1,.5 L.5,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT AND MIDDLE RIGHT TO LOWER CENTRE '\u{1FBA9}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,0 L1,.5 M0,.5 L.5,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT AND MIDDLE LEFT TO LOWER CENTRE '\u{1FBAA}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,0 L1,.5 L.5,1 L0,.5', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE RIGHT TO LOWER CENTRE TO MIDDLE LEFT '\u{1FBAB}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,0 L0,.5 L.5,1 L1,.5', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO MIDDLE LEFT TO LOWER CENTRE TO MIDDLE RIGHT '\u{1FBAC}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,.5 L.5,0 L1,.5 L.5,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO UPPER CENTRE TO MIDDLE RIGHT TO LOWER CENTRE '\u{1FBAD}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M1,.5 L.5,0 L0,.5 L.5,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE RIGHT TO UPPER CENTRE TO MIDDLE LEFT TO LOWER CENTRE '\u{1FBAE}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,0 L1,.5 L.5,1 L0,.5 Z', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL DIAMOND // Light solid line with stroke (1FBAF-1FBAF) '\u{1FBAF}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: `${Shapes.LEFT_TO_RIGHT} M.5,.35 L.5,.65`, strokeWidth: 1 }, // BOX DRAWINGS LIGHT HORIZONTAL WITH VERTICAL STROKE // Terminal graphic characters (1FBB0-1FBB3) '\u{1FBB0}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.1,0.2 L0.1,.8 L.4,.6 L.9,0.6 Z', scaleType: CustomGlyphScaleType.CHAR }, // ARROWHEAD-SHAPED POINTER '\u{1FBB1}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.1,.525 L.35,.675 L.9,.35', type: CustomGlyphVectorType.STROKE }, scaleType: CustomGlyphScaleType.CHAR }, // INVERSE CHECK MARK '\u{1FBB2}': { type: CustomGlyphDefinitionType.PATH, data: 'M.29,.27 L0.13,.56 L.22,.59 L.35,0.35 L.67,.35 L.57,.57 L.71,.76 L.22,.76 L.42,1 L.53,.98 L.43,.86 L.9,.86 L.71,.6 L1,.6 L1,.52 L.83,.52 L.92,.36 L1,.36 L1,.27Z M.99,.13 A.12,.12,0,1,1,.75,.13 A.12,.12,0,1,1,.99,.13' }, // LEFT HALF RUNNING MAN '\u{1FBB3}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,.27 L.3,.27 L.55,.12 L.63,.18 L.33,.36 L0,.36 M0,.52 L.33,.52 L.59,.89 L.73,.89 L.73,.98 L.53,.98 L.28,.6 L0,.6' }, // RIGHT HALF RUNNING MAN // Arrows (1FBB4-1FBB8) // TODO: Improve all arrows, use hybrid approach '\u{1FBB4}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.15,.55 L.5,.4 L.5,.45 L.65,.45 L.65,.35 L.85,.35 L.85,.625 L.5,.625 L.5,.7 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // INVERSE DOWNWARDS ARROW WITH TIP LEFTWARDS '\u{1FBB5}': [ // LEFTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L1,0 L1,.0625 L0,.0625 Z M0,.9375 L1,.9375 L1,1 L0,1 Z', type: CustomGlyphVectorType.FILL } }, { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.15,.5 L.5,.35 L.5,.425 L.85,.425 L.85,.575 L.5,.575 L.5,.65 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR } ], '\u{1FBB6}': [ // RIGHTWARDS ARROW AND UPPER AND LOWER ONE EIGHTH BLOCK { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L1,0 L1,.0625 L0,.0625 Z M0,.9375 L1,.9375 L1,1 L0,1 Z', type: CustomGlyphVectorType.FILL } }, { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.85,.5 L.5,.35 L.5,.425 L.15,.425 L.15,.575 L.5,.575 L.5,.65 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR } ], '\u{1FBB7}': [ // DOWNWARDS ARROW AND RIGHT ONE EIGHTH BLOCK { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.875,0 L1,0 L1,1 L.875,1 Z', type: CustomGlyphVectorType.FILL } }, { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.5,.675 L.2,.5 L.35,.5 L.35,.325 L.65,.325 L.65,.5 L.8,.5 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR } ], '\u{1FBB8}': [ // UPWARDS ARROW AND RIGHT ONE EIGHTH BLOCK { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.875,0 L1,0 L1,1 L.875,1 Z', type: CustomGlyphVectorType.FILL } }, { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.5,.325 L.2,.5 L.35,.5 L.35,.675 L.65,.675 L.65,.5 L.8,.5 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR } ], // Terminal graphic characters (1FBB9-1FBBC) '\u{1FBB9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,.89 L.11,.89 L.11,.37 L.36,.12 L.74,.12 L.96,.34 L1,.34 L1,.405 L.92,.405 L.69,.185 L.41,.185 L.21,.42 L.21,.825 L1,.825 Z', type: CustomGlyphVectorType.FILL } }, // LEFT HALF FOLDER '\u{1FBBA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,.89 L0,.825 L.78,.825 L.78,.53 L.7,.415 L0,.415 L0,.35 L.75,.35 L.88,.48 L.88,.89 Z', type: CustomGlyphVectorType.FILL } }, // RIGHT HALF FOLDER '\u{1FBBB}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.31,.275 L.44,.275 L.44,.47 L.05,.47 L.05,.405 L.31,.405 Z M.56,.275 L.69,.275 L.69,.405 L.95,.405 L.95,.47 L.56,.47 Z M.05,.53 L.44,.53 L.44,.725 L.31,.725 L.31,.595 L.05,.595 Z M.56,.53 L.95,.53 L.95,.595 L.69,.595 L.69,.725 L.56,.725 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // VOIDED GREEK CROSS '\u{1FBBC}': [ // RIGHT OPEN SQUARED DOT { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L1,0 L1,1 L0,1 L0,.935 L.885,.935 L.885,.065 L0,.065 Z' }, { type: CustomGlyphDefinitionType.PATH, data: 'M.67,.5 A.17,.085,0,1,1,.33,.5 A.17,.085,0,1,1,.67,.5', scaleType: CustomGlyphScaleType.CHAR } ], // Negative terminal graphic characters (1FBBD-1FBBF) '\u{1FBBD}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M0,0 L.5,.5 L1,0 L1,1 L.5,.5 L0,1 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL CROSS '\u{1FBBE}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M1,.5 L.5,1', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL MIDDLE RIGHT TO LOWER CENTRE '\u{1FBBF}': { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: { d: 'M.5,0 L1,.5 L.5,1 L0,.5 Z', type: CustomGlyphVectorType.STROKE } }, // NEGATIVE DIAGONAL DIAMOND // Terminal graphic characters (1FBC0-1FBCA) '\u{1FBC0}': { type: CustomGlyphDefinitionType.PATH, data: 'M.16,.445 A.02,.01,0,0,1,.39,.33 L.5,.375 L.61,.33 A.02,.01,0,0,1,.84,.445 L.75,.5 L.84,.555 A.02,.01,0,0,1,.61,.67 L.5,.625 L.39,.67 A.02,.01,0,0,1,.16,.555 L.25,.5 Z M.24,.41 L.39,.5 L.24,.59 L.32,.63 L.5,.555 L.68,.63 L.76,.59 L.61,.5 L.76,.41 L.68,.37 L.5,.445 L.32,.37 Z', scaleType: CustomGlyphScaleType.CHAR }, // WHITE HEAVY SALTIRE WITH ROUNDED CORNERS // 1FBC1-1FBC4 is dervied from the Iosevka font (SIL OFL v1.1) https://github.com/be5invis/Iosevka '\u{1FBC1}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.142308,0.924 Q0.130770,0.924,0.120193,0.922 T0.101924,0.916 T0.089424,0.9065 T0.084616,0.895 V0.025 Q0.084616,0.019,0.089424,0.0135 T0.101924,0.004 T0.120193,-0.002 T0.142308,-0.004 Q0.150000,-0.004,0.157693,-0.003 T0.173078,0.000 L0.430770,0.085 Q0.451924,0.059,0.494232,0.041 T0.587501,0.013 T0.692309,-0.0005 T0.800001,-0.004 H1 V0.055 H0.800001 Q0.771155,0.055,0.742309,0.056 T0.685578,0.060 T0.630770,0.0685 T0.580770,0.0825 T0.543270,0.1045 T0.528847,0.133 V0.647 H0.530770 Q0.596155,0.645,0.659616,0.638 T0.782693,0.6165 T0.893270,0.5805 T1,0.531 V0.623 Q0.934616,0.644,0.880770,0.6595 T0.769232,0.685 T0.650963,0.700 T0.528847,0.707 V0.787 Q0.528847,0.802,0.543270,0.8155 T0.580770,0.8375 T0.630770,0.8515 T0.685578,0.860 T0.742309,0.864 T0.800001,0.865 H1 V0.924 H0.800001 Q0.746155,0.924,0.692309,0.9205 T0.587501,0.907 T0.494232,0.879 T0.430770,0.835 L0.173078,0.920 Q0.165386,0.922,0.157693,0.923 T0.142308,0.924 Z M0.2,0.841 L0.415385,0.770 V0.150 L0.2,0.079 V0.841 Z M1,0.698 Q0.973077,0.694,0.969231,0.6885 T0.965385,0.677 Q0.965385,0.672,0.969231,0.6665 T1,0.657 V0.698 Z' }, // LEFT THIRD WHITE RIGHT POINTING INDEX '\u{1FBC2}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.623 V0.531 Q0.044231,0.512,0.0625,0.4905 T0.092308,0.4465 T0.108654,0.4005 T0.113462,0.354 V0.351 Q0.113462,0.345,0.117308,0.3395 T0.129808,0.3305 T0.148846,0.3245 T0.171154,0.323 H0.501923 Q0.532692,0.323,0.563462,0.3185 T0.617308,0.303 T0.651923,0.2765 T0.663462,0.2435 V0.241 Q0.663462,0.219,0.657692,0.197 T0.639423,0.1545 T0.604808,0.115 T0.549615,0.082 T0.475577,0.0615 T0.392308,0.055 H0 V-0.004 H1 V0.055 H0.667308 Q0.694231,0.071,0.713462,0.09 T0.746154,0.129 T0.766346,0.1705 T0.775,0.213 H1 V0.316 Q0.971154,0.305,0.955769,0.2965 T0.920192,0.2825 T0.876923,0.2745 T0.830769,0.272 H0.773077 Q0.767308,0.297,0.743269,0.319 T0.680769,0.355 T0.595192,0.375 T0.501923,0.381 H0.227308 Q0.223462,0.415,0.211,0.4485 T0.172692,0.5135 T0.108269,0.573 T0,0.623 Z M0.611538,0.924 H0 V0.865 H0.611538 Q0.642308,0.865,0.673077,0.8605 T0.727885,0.845 T0.762308,0.8185 T0.773077,0.787 V0.785 Q0.773077,0.769,0.762308,0.7535 T0.727885,0.727 T0.673077,0.7115 T0.611538,0.707 H0.059615 Q0.048077,0.707,0.037115,0.7045 T0,0.694 V0.653 Q0.026923,0.648,0.037115,0.646 T0.059615,0.644 H0.722692 Q0.753462,0.644,0.784231,0.6395 T0.839038,0.624 T0.873846,0.5975 T0.884615,0.566 V0.564 Q0.884615,0.548,0.873846,0.532 T0.839038,0.5055 T0.784231,0.49 T0.722692,0.4855 H0.392308 Q0.380769,0.4855,0.370192,0.483 T0.351923,0.4765 T0.339423,0.467 T0.334615,0.4555 T0.339423,0.444 T0.351923,0.4345 T0.370192,0.428 T0.392308,0.4255 H0.832692 Q0.855769,0.4255,0.878846,0.4235 T0.922115,0.416 T0.957692,0.4015 T1,0.3815 V0.4685 Q0.975,0.4705,0.966346,0.4725 T0.95,0.4765 Q0.961538,0.4835,0.969231,0.4915 T1,0.5075 V0.6205 Q0.965385,0.6455,0.926923,0.665 T0.84,0.6935 Q0.866923,0.7115,0.877596,0.7345 T0.894231,0.7855 V0.787 Q0.894231,0.815,0.876923,0.8425 T0.820192,0.8895 T0.726923,0.916 T0.611538,0.924 Z' }, // MIDDLE THIRD WHITE RIGHT POINTING INDEX '\u{1FBC3}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0.473 V0.386 Q0.025490,0.378,0.028431,0.3695 T0.031373,0.352 V0.35 Q0.031373,0.342,0.028431,0.333 T0,0.316 V0.213 H0.652941 Q0.684314,0.213,0.715686,0.2085 T0.770588,0.193 T0.804902,0.1665 T0.815686,0.135 V0.133 Q0.815686,0.117,0.804902,0.101 T0.770588,0.0745 T0.715686,0.0595 T0.652941,0.055 H0 V-0.004 H0.652941 Q0.707843,-0.004,0.761765,0.004 T0.856863,0.031 T0.915686,0.0775 T0.933333,0.133 V0.135 Q0.933333,0.163,0.915686,0.1905 T0.856863,0.237 T0.761765,0.264 T0.652941,0.272 H0.109804 Q0.131373,0.29,0.140196,0.31 T0.149020,0.35 V0.352 Q0.149020,0.37,0.142157,0.388 T0.118627,0.4225 T0.076471,0.452 T0,0.473 Z M0,0.625 V0.513 Q0.029412,0.526,0.032353,0.54 T0.035294,0.568 V0.57 Q0.035294,0.584,0.032353,0.598 T0,0.625 Z' }, // RIGHT THIRD WHITE RIGHT POINTING INDEX '\u{1FBC4}': { type: CustomGlyphDefinitionType.PATH, data: 'M0.019231,1.085 V-0.165 H0.980769 V1.085 H0.019231 Z M0.446154,0.527 H0.553846 Q0.553846,0.511,0.5625,0.495 T0.591346,0.466 T0.631731,0.4405 T0.666346,0.4135 T0.688462,0.3825 T0.696154,0.35 Q0.696154,0.33,0.682692,0.311 T0.641346,0.2785 T0.575,0.259 T0.498077,0.253 T0.421154,0.259 T0.356731,0.279 T0.315385,0.3125 T0.301923,0.352 V0.357 H0.409615 V0.354 Q0.409615,0.345,0.414423,0.335 T0.431731,0.318 T0.4625,0.3075 T0.498077,0.304 Q0.517308,0.304,0.534615,0.307 T0.564423,0.3165 T0.582692,0.332 T0.588462,0.35 Q0.588462,0.366,0.572115,0.3805 T0.535577,0.4075 T0.496154,0.4335 T0.465385,0.4625 T0.45,0.4945 T0.446154,0.527 Z M0.5,0.667 Q0.519231,0.667,0.5375,0.664 T0.569231,0.654 T0.588462,0.637 T0.594231,0.617 T0.588462,0.5975 T0.569231,0.581 T0.5375,0.571 T0.5,0.568 T0.4625,0.571 T0.430769,0.581 T0.411538,0.5975 T0.405769,0.617 T0.411538,0.637 T0.430769,0.654 T0.4625,0.664 T0.5,0.667 Z', scaleType: CustomGlyphScaleType.CHAR }, // NEGATIVE SQUARED QUESTION MARK '\u{1FBC5}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.425 L.44,.51 L.44,.56 L.19,.685 L.26,.72 L.5,.605 L.74,.72 L.81,.685 L.56,.56 L.56,.51 L.56,.425 Z M.17,.46 L.17,.51 L.83,.51 L.83,.46 Z M.67,.35 C.67,.303,.594,.265,.5,.265 C.406,.265,.33,.303,.33,.35 C.33,.397,.406,.435,.5,.435 C.594,.435,.67,.397,.67,.35 Z M.56,.35 C.56,.3665,.533,.38,.5,.38 C.467,.38,.44,.3665,.44,.35 C.44,.3335,.467,.32,.5,.32 C.533,.32,.56,.3335,.56,.35 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // STICK FIGURE '\u{1FBC6}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.425 L.44,.46 L.23,.385 L.19,.43 L.44,.51 L.44,.56 L.29,.71 L.38,.735 L.5,.605 L.61,.735 L.7,.71 L.56,.56 L.56,.51 L.81,.43 L.77,.385 L.56,.46 L.56,.425 Z M.67,.35 C.67,.303,.594,.265,.5,.265 C.406,.265,.33,.303,.33,.35 C.33,.397,.406,.435,.5,.435 C.594,.435,.67,.397,.67,.35 Z M.56,.35 C.56,.3665,.533,.38,.5,.38 C.467,.38,.44,.3665,.44,.35 C.44,.3335,.467,.32,.5,.32 C.533,.32,.56,.3335,.56,.35 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // STICK FIGURE WITH ARMS RAISED '\u{1FBC7}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.425 L.44,.56 L.29,.71 L.38,.735 L.5,.605 L.74,.72 L.81,.685 L.56,.56 L.56,.425 Z M.18,.53 L.23,.575 L.81,.43 L.77,.385 Z M.67,.35 C.67,.303,.594,.265,.5,.265 C.406,.265,.33,.303,.33,.35 C.33,.397,.406,.435,.5,.435 C.594,.435,.67,.397,.67,.35 Z M.56,.35 C.56,.3665,.533,.38,.5,.38 C.467,.38,.44,.3665,.44,.35 C.44,.3335,.467,.32,.5,.32 C.533,.32,.56,.3335,.56,.35 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // STICK FIGURE LEANING LEFT '\u{1FBC8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.425 L.44,.56 L.19,.685 L.26,.72 L.5,.605 L.62,.735 L.71,.71 L.56,.56 L.56,.425 Z M.23,.385 L.18,.43 L.77,.575 L.81,.53 Z M.67,.35 C.67,.303,.594,.265,.5,.265 C.406,.265,.33,.303,.33,.35 C.33,.397,.406,.435,.5,.435 C.594,.435,.67,.397,.67,.35 Z M.56,.35 C.56,.3665,.533,.38,.5,.38 C.467,.38,.44,.3665,.44,.35 C.44,.3335,.467,.32,.5,.32 C.533,.32,.56,.3335,.56,.35 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // STICK FIGURE LEANING RIGHT '\u{1FBC9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.44,.425 L.45,.495 L.15,.645 L.34,.645 L.34,.7 L.44,.7 L.44,.645 L.56,.645 L.56,.7 L.66,.7 L.66,.645 L.84,.645 L.54,.495 L.56,.425 Z M.39,.6 L.5,.55 L.60,.6 Z M.17,.46 L.17,.51 L.83,.51 L.83,.46 Z M.67,.35 C.67,.303,.594,.265,.5,.265 C.406,.265,.33,.303,.33,.35 C.33,.397,.406,.435,.5,.435 C.594,.435,.67,.397,.67,.35 Z M.56,.35 C.56,.3665,.533,.38,.5,.38 C.467,.38,.44,.3665,.44,.35 C.44,.3335,.467,.32,.5,.32 C.533,.32,.56,.3335,.56,.35 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // STICK FIGURE WITH DRESS '\u{1FBCA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.26,.375 L.5,.255 L.74,.375 L.74,.665 L.5,.55 L.26,.665 Z M.37,.395 L.37,.54 L.5,.475 L.63,.54 L.63,.395 L.5,.33 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // WHITE UP-POINTING CHEVRON // Terminal graphic characters (1FBCB-1FBCD) '\u{1FBCB}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.09,.41 L.32,.295 L.5,.375 L.68,.295 L.91,.41 L.75,.5 L.91,.59 L.68,.705 L.5,.625 L.32,.705 L.09,.59 L.25,.5 Z M.24,.41 L.39,.5 L.24,.59 L.32,.63 L.5,.555 L.68,.63 L.76,.59 L.61,.5 L.76,.41 L.68,.37 L.5,.445 L.32,.37 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // WHITE CROSS MARK '\u{1FBCC}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.55,.305 L.88,.305 L.88,.355 L.65,.355 L.65,.47 L.88,.47 L.88,.52 L.55,.52 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // RAISED SMALL LEFT SQUARE BRACKET '\u{1FBCD}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.38,.39 L.5,.33 L.62,.39 L.62,.53 L.5,.47 L.38,.53 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // BLACK SMALL UP-POINTING CHEVRON // Block elements (1FBCE-1FBCF) '\u{1FBCE}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L0.6667,0 L0.6667,1 L0,1 Z' }, // LEFT TWO THIRDS BLOCK '\u{1FBCF}': { type: CustomGlyphDefinitionType.PATH, data: 'M0,0 L0.3333,0 L0.3333,1 L0,1 Z' }, // LEFT ONE THIRD BLOCK // Character cell diagonals (1FBD0-1FBDF) '\u{1FBD0}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M1,.5 L0,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE RIGHT TO LOWER LEFT '\u{1FBD1}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M1,0 L0,.5', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO MIDDLE LEFT '\u{1FBD2}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,0 L1,.5', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO MIDDLE RIGHT '\u{1FBD3}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,.5 L1,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL MIDDLE LEFT TO LOWER RIGHT '\u{1FBD4}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,0 L.5,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER CENTRE '\u{1FBD5}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,0 L1,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO LOWER RIGHT '\u{1FBD6}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M1,0 L.5,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER CENTRE '\u{1FBD7}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M.5,0 L0,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER CENTRE TO LOWER LEFT '\u{1FBD8}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,0 L.5,.5 L1,0', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO MIDDLE CENTRE TO UPPER RIGHT '\u{1FBD9}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M1,0 L.5,.5 L1,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO MIDDLE CENTRE TO LOWER RIGHT '\u{1FBDA}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,1 L.5,.5 L1,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL LOWER LEFT TO MIDDLE CENTRE TO LOWER RIGHT '\u{1FBDB}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,0 L.5,.5 L0,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO MIDDLE CENTRE TO LOWER LEFT '\u{1FBDC}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,0 L.5,1 L1,0', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER CENTRE TO UPPER RIGHT '\u{1FBDD}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M1,0 L0,.5 L1,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO MIDDLE LEFT TO LOWER RIGHT '\u{1FBDE}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,1 L.5,0 L1,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL LOWER LEFT TO UPPER CENTRE TO LOWER RIGHT '\u{1FBDF}': { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: 'M0,0 L1,.5 L0,1', strokeWidth: 1 }, // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO MIDDLE RIGHT TO LOWER LEFT // Geometric shapes (1FBE0-1FBEF) '\u{1FBE0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 C0,.276,.224,.5,.5,.5 C.776,.5,1,.276,1,0', type: CustomGlyphVectorType.STROKE } }, // TOP JUSTIFIED LOWER HALF WHITE CIRCLE '\u{1FBE1}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0 C.724,0,.5,.224,.5,.5 C.5,.776,.724,1,1,1', type: CustomGlyphVectorType.STROKE } }, // RIGHT JUSTIFIED LEFT HALF WHITE CIRCLE '\u{1FBE2}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,1 C0,.724,.224,.5,.5,.5 C.776,.5,1,.724,1,1', type: CustomGlyphVectorType.STROKE } }, // BOTTOM JUSTIFIED UPPER HALF WHITE CIRCLE '\u{1FBE3}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 C.276,0,.5,.224,.5,.5 C.5,.776,.276,1,0,1', type: CustomGlyphVectorType.STROKE } }, // LEFT JUSTIFIED RIGHT HALF WHITE CIRCLE '\u{1FBE4}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 2, y: 0, w: 4, h: 4 }] }, // UPPER CENTRE ONE QUARTER BLOCK '\u{1FBE5}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 2, y: 4, w: 4, h: 4 }] }, // LOWER CENTRE ONE QUARTER BLOCK '\u{1FBE6}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 0, y: 2, w: 4, h: 4 }] }, // MIDDLE LEFT ONE QUARTER BLOCK '\u{1FBE7}': { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: [{ x: 4, y: 2, w: 4, h: 4 }] }, // MIDDLE RIGHT ONE QUARTER BLOCK '\u{1FBE8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 C0,.276,.224,.5,.5,.5 C.776,.5,1,.276,1,0 Z', type: CustomGlyphVectorType.FILL } }, // TOP JUSTIFIED LOWER HALF BLACK CIRCLE '\u{1FBE9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0 C.724,0,.5,.224,.5,.5 C.5,.776,.724,1,1,1 Z', type: CustomGlyphVectorType.FILL } }, // RIGHT JUSTIFIED LEFT HALF BLACK CIRCLE '\u{1FBEA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,1 C0,.724,.224,.5,.5,.5 C.776,.5,1,.724,1,1 Z', type: CustomGlyphVectorType.FILL } }, // BOTTOM JUSTIFIED UPPER HALF BLACK CIRCLE '\u{1FBEB}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 C.276,0,.5,.224,.5,.5 C.5,.776,.276,1,0,1 Z', type: CustomGlyphVectorType.FILL } }, // LEFT JUSTIFIED RIGHT HALF BLACK CIRCLE '\u{1FBEC}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,0 L.5,0 C.5,.276,.724,.5,1,.5 Z', type: CustomGlyphVectorType.FILL } }, // TOP RIGHT JUSTIFIED LOWER LEFT QUARTER BLACK CIRCLE '\u{1FBED}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,1 L.5,1 C.5,.724,.276,.5,0,.5 Z', type: CustomGlyphVectorType.FILL } }, // BOTTOM LEFT JUSTIFIED UPPER RIGHT QUARTER BLACK CIRCLE '\u{1FBEE}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M1,1 L1,.5 C.724,.5,.5,.724,.5,1 Z', type: CustomGlyphVectorType.FILL } }, // BOTTOM RIGHT JUSTIFIED UPPER LEFT QUARTER BLACK CIRCLE '\u{1FBEF}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M0,0 L0,.5 C.276,.5,.5,.276,.5,0 Z', type: CustomGlyphVectorType.FILL } }, // TOP LEFT JUSTIFIED LOWER RIGHT QUARTER BLACK CIRCLE // Segmented digits (1FBF0-1FBF9) '\u{1FBF0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1111110), type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SEGMENTED DIGIT ZERO (abcdef) '\u{1FBF1}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b0110000), type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SEGMENTED DIGIT ONE (bc) '\u{1FBF2}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1101101), type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SEGMENTED DIGIT TWO (abdeg) '\u{1FBF3}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1111001), type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SEGMENTED DIGIT THREE (abcdg) '\u{1FBF4}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b0110011), type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SEGMENTED DIGIT FOUR (bcfg) '\u{1FBF5}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1011011), type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SEGMENTED DIGIT FIVE (acdfg) '\u{1FBF6}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1011111), type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SEGMENTED DIGIT SIX (acdefg) '\u{1FBF7}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1110010), type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SEGMENTED DIGIT SEVEN (abcf) '\u{1FBF8}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1111111), type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SEGMENTED DIGIT EIGHT (abcdefg) '\u{1FBF9}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: segmentedDigit(0b1111011), type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // SEGMENTED DIGIT NINE (abcdfg) // Terminal graphic character (1FBFA-1FBFA) '\u{1FBFA}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: 'M.5,.175 C.2,.175,.15,.305,.15,.435 L.05,.63 L.35,.63 C.35,.682,.42,.76,.5,.76 C.58,.76,.65,.682,.65,.63 L.95,.63 L.85,.435 C.85,.305,.8,.175,.5,.175 Z', type: CustomGlyphVectorType.FILL }, scaleType: CustomGlyphScaleType.CHAR }, // ALARM BELL SYMBOL // #endregion // #region Braille Patterns (2800-28FF) // https://www.unicode.org/charts/PDF/U2800.pdf // Braille patterns (2800-28FF) ...Object.fromEntries( Array.from({ length: 256 }, (_, i) => [ String.fromCodePoint(0x2800 + i), { type: CustomGlyphDefinitionType.BRAILLE, data: i } ]) ), // #endregion }; export const blockPatternCodepoints = new Set([ // Shade characters (2591-2593) 0x2591, 0x2592, 0x2593, // Rectangular shade characters (1FB8C-1FB94) 0x1FB8C, 0x1FB8D, 0x1FB8E, 0x1FB8F, 0x1FB90, 0x1FB91, 0x1FB92, 0x1FB94, // Triangular shade characters (1FB9C-1FB9F) 0x1FB9C, 0x1FB9D, 0x1FB9E, 0x1FB9F ]); /** * Generates a drawing function for sextant characters. Sextants are a 2x3 grid where each cell * can be on or off. * @param pattern A 6-bit pattern where bit 0 = top-left, bit 1 = top-right, bit 2 = middle-left, * bit 3 = middle-right, bit 4 = bottom-left, bit 5 = bottom-right */ function sextant(pattern: number): { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: CustomGlyphPathDrawFunctionDefinition } { return { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: () => { // Sextant grid: 2 columns, 3 rows // Row heights in 8ths: top=3, middle=2, bottom=3 // Column widths: left=4, right=4 const rects: string[] = []; const colW = 0.5; // Each column is half width const rowH = [3 / 8, 2 / 8, 3 / 8]; // Row heights as fractions const rowY = [0, 3 / 8, 5 / 8]; // Row Y positions for (let row = 0; row < 3; row++) { const leftBit = (pattern >> (row * 2)) & 1; const rightBit = (pattern >> (row * 2 + 1)) & 1; if (leftBit && rightBit) { // Full row rects.push(`M0,${rowY[row]} L1,${rowY[row]} L1,${rowY[row] + rowH[row]} L0,${rowY[row] + rowH[row]} Z`); } else if (leftBit) { rects.push(`M0,${rowY[row]} L${colW},${rowY[row]} L${colW},${rowY[row] + rowH[row]} L0,${rowY[row] + rowH[row]} Z`); } else if (rightBit) { rects.push(`M${colW},${rowY[row]} L1,${rowY[row]} L1,${rowY[row] + rowH[row]} L${colW},${rowY[row] + rowH[row]} Z`); } } return rects.join(' '); } }; } /** * Generates SVG path data for a 7-segment display digit. * * Segment mapping (bit positions): * * - bit 6: a (top) * - bit 5: b (upper right) * - bit 4: c (lower right) * - bit 3: d (bottom) * - bit 2: e (lower left) * - bit 1: f (upper left) * - bit 0: g (middle) * * ``` * ─a─ * │ │ * f b * ─g─ * e c * │ │ * ─d─ * ``` */ function segmentedDigit(pattern: number): string { const paths: string[] = []; // Each segment should have approximately the same stroke width, this is somewhat difficult to be // precise since coordinates are 0-1 of the whole cell (percentage-based). To handle this, the // fact that terminal cells are typically sized at ~2:1 (height:width) is leveraged. // for horizontal vs vertical to make segments appear the same thickness const segW = 0.15; // Width of vertical segments (fraction of cell width) const segH = 0.075; // Height of horizontal segments (fraction of cell height, ~half of segW for 2:1 cells) const padX = 0.05; // Horizontal padding from edge const padY = 0.175; // Vertical padding from edge (35% total = 65% height) const gap = 0.015; // Gap between segments const taperX = segW / 2; // Horizontal taper for vertical segments const taperY = segH / 2; // Vertical taper for horizontal segments const left = padX; const right = 1 - padX; const top = padY; const bottom = 1 - padY; const midY = 0.5; // Segment a (top horizontal) - hexagonal with pointed left/right ends if (pattern & 0b1000000) { const y1 = top; const y2 = top + segH / 2; const y3 = top + segH; const x1 = left + segW + gap; const x2 = right - segW - gap; paths.push(`M${x1},${y2} L${x1 + taperX},${y1} L${x2 - taperX},${y1} L${x2},${y2} L${x2 - taperX},${y3} L${x1 + taperX},${y3} Z`); } // Segment b (upper right vertical) - hexagonal with pointed top/bottom ends if (pattern & 0b0100000) { const x1 = right - segW; const x2 = right - segW / 2; const x3 = right; const y1 = top + segH + gap; const y2 = midY - gap; paths.push(`M${x2},${y1} L${x3},${y1 + taperY} L${x3},${y2 - taperY} L${x2},${y2} L${x1},${y2 - taperY} L${x1},${y1 + taperY} Z`); } // Segment c (lower right vertical) - hexagonal with pointed top/bottom ends if (pattern & 0b0010000) { const x1 = right - segW; const x2 = right - segW / 2; const x3 = right; const y1 = midY + gap; const y2 = bottom - segH - gap; paths.push(`M${x2},${y1} L${x3},${y1 + taperY} L${x3},${y2 - taperY} L${x2},${y2} L${x1},${y2 - taperY} L${x1},${y1 + taperY} Z`); } // Segment d (bottom horizontal) - hexagonal with pointed left/right ends if (pattern & 0b0001000) { const y1 = bottom - segH; const y2 = bottom - segH / 2; const y3 = bottom; const x1 = left + segW + gap; const x2 = right - segW - gap; paths.push(`M${x1},${y2} L${x1 + taperX},${y1} L${x2 - taperX},${y1} L${x2},${y2} L${x2 - taperX},${y3} L${x1 + taperX},${y3} Z`); } // Segment e (lower left vertical) - hexagonal with pointed top/bottom ends if (pattern & 0b0000100) { const x1 = left; const x2 = left + segW / 2; const x3 = left + segW; const y1 = midY + gap; const y2 = bottom - segH - gap; paths.push(`M${x2},${y1} L${x3},${y1 + taperY} L${x3},${y2 - taperY} L${x2},${y2} L${x1},${y2 - taperY} L${x1},${y1 + taperY} Z`); } // Segment f (upper left vertical) - hexagonal with pointed top/bottom ends if (pattern & 0b0000010) { const x1 = left; const x2 = left + segW / 2; const x3 = left + segW; const y1 = top + segH + gap; const y2 = midY - gap; paths.push(`M${x2},${y1} L${x3},${y1 + taperY} L${x3},${y2 - taperY} L${x2},${y2} L${x1},${y2 - taperY} L${x1},${y1 + taperY} Z`); } // Segment g (middle horizontal) - hexagonal with pointed left/right ends if (pattern & 0b0000001) { const y1 = midY - segH / 2; const y2 = midY; const y3 = midY + segH / 2; const x1 = left + segW + gap; const x2 = right - segW - gap; paths.push(`M${x1},${y2} L${x1 + taperX},${y1} L${x2 - taperX},${y1} L${x2},${y2} L${x2 - taperX},${y3} L${x1 + taperX},${y3} Z`); } return paths.join(' '); } ================================================ FILE: addons/addon-webgl/src/customGlyphs/CustomGlyphRasterizer.ts ================================================ /** * Copyright (c) 2021 The xterm.js authors. All rights reserved. * @license MIT */ import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; import { customGlyphDefinitions } from './CustomGlyphDefinitions'; import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types'; /** * Try drawing a custom block element or box drawing character, returning whether it was * successfully drawn. */ export function tryDrawCustomGlyph( ctx: CanvasRenderingContext2D, c: string, xOffset: number, yOffset: number, deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, fontSize: number, devicePixelRatio: number, backgroundColor?: string, variantOffset: number = 0 ): boolean { const unifiedCharDefinition = customGlyphDefinitions[c]; if (unifiedCharDefinition) { // Normalize to array for uniform handling const parts = Array.isArray(unifiedCharDefinition) ? unifiedCharDefinition : [unifiedCharDefinition]; for (const part of parts) { drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, backgroundColor, variantOffset); } return true; } return false; } function drawDefinitionPart( ctx: CanvasRenderingContext2D, part: CustomGlyphDefinitionPart, xOffset: number, yOffset: number, deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, fontSize: number, devicePixelRatio: number, backgroundColor?: string, variantOffset: number = 0 ): void { // Handle scaleType - adjust dimensions and offset when scaling to character area let drawWidth = deviceCellWidth; let drawHeight = deviceCellHeight; let drawXOffset = xOffset; let drawYOffset = yOffset; if (part.scaleType === CustomGlyphScaleType.CHAR) { drawWidth = deviceCharWidth; drawHeight = deviceCharHeight; // Center the character within the cell drawXOffset = xOffset + (deviceCellWidth - deviceCharWidth) / 2; drawYOffset = yOffset + (deviceCellHeight - deviceCharHeight) / 2; } // Handle clipPath generically for any definition type if (part.clipPath) { ctx.save(); applyClipPath(ctx, part.clipPath, drawXOffset, drawYOffset, drawWidth, drawHeight); } switch (part.type) { case CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR: drawBlockVectorChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight); break; case CustomGlyphDefinitionType.BLOCK_PATTERN: drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, variantOffset); break; case CustomGlyphDefinitionType.PATH_FUNCTION: drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth); break; case CustomGlyphDefinitionType.PATH: drawPathDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth); break; case CustomGlyphDefinitionType.PATH_NEGATIVE: drawPathNegativeDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, backgroundColor); break; case CustomGlyphDefinitionType.VECTOR_SHAPE: drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio); break; case CustomGlyphDefinitionType.BRAILLE: drawBrailleCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight); break; } if (part.clipPath) { ctx.restore(); } } function drawBlockVectorChar( ctx: CanvasRenderingContext2D, charDefinition: ICustomGlyphSolidOctantBlockVector[], xOffset: number, yOffset: number, deviceCellWidth: number, deviceCellHeight: number ): void { for (let i = 0; i < charDefinition.length; i++) { const box = charDefinition[i]; const xEighth = deviceCellWidth / 8; const yEighth = deviceCellHeight / 8; ctx.fillRect( xOffset + box.x * xEighth, yOffset + box.y * yEighth, box.w * xEighth, box.h * yEighth ); } } /** * Braille dot positions in octant coordinates (x, y for center of each dot area) * Columns: left=1-2, right=5-6 (leaving 0 and 7 as margins, 3-4 as gap) * Rows: 0-1, 2-3, 4-5, 6-7 for the 4 rows */ const brailleDotPositions = new Uint8Array([ 1, 0, // dot 1 - bit 0 1, 2, // dot 2 - bit 1 1, 4, // dot 3 - bit 2 5, 0, // dot 4 - bit 3 5, 2, // dot 5 - bit 4 5, 4, // dot 6 - bit 5 1, 6, // dot 7 - bit 6 5, 6, // dot 8 - bit 7 ]); /** * Draws a braille pattern */ function drawBrailleCharacter( ctx: CanvasRenderingContext2D, pattern: number, xOffset: number, yOffset: number, deviceCellWidth: number, deviceCellHeight: number ): void { const xEighth = deviceCellWidth / 8; const paddingY = deviceCellHeight * 0.1; const usableHeight = deviceCellHeight * 0.8; const yEighth = usableHeight / 8; const radius = Math.min(xEighth, yEighth); for (let bit = 0; bit < 8; bit++) { if (pattern & (1 << bit)) { const x = brailleDotPositions[bit * 2]; const y = brailleDotPositions[bit * 2 + 1]; const cx = xOffset + (x + 1) * xEighth; const cy = yOffset + paddingY + (y + 1) * yEighth; ctx.beginPath(); ctx.arc(cx, cy, radius, 0, Math.PI * 2); ctx.fill(); } } } function drawPathDefinitionCharacter( ctx: CanvasRenderingContext2D, charDefinition: CustomGlyphPathDrawFunctionDefinition | string, xOffset: number, yOffset: number, deviceCellWidth: number, deviceCellHeight: number, devicePixelRatio: number, strokeWidth?: number ): void { const instructions = typeof charDefinition === 'string' ? charDefinition : charDefinition(0, 0); ctx.beginPath(); let currentX = 0; let currentY = 0; let lastControlX = 0; let lastControlY = 0; let lastCommand = ''; for (const instruction of instructions.split(' ')) { const type = instruction[0]; const args: string[] = instruction.substring(1).split(','); if (type === 'Z') { ctx.closePath(); lastCommand = type; continue; } if (type === 'V') { const y = yOffset + parseFloat(args[0]) * deviceCellHeight; ctx.lineTo(currentX, y); currentY = y; lastControlX = currentX; lastControlY = currentY; lastCommand = type; continue; } if (type === 'H') { const x = xOffset + parseFloat(args[0]) * deviceCellWidth; ctx.lineTo(x, currentY); currentX = x; lastControlX = currentX; lastControlY = currentY; lastCommand = type; continue; } if (!args[0] || !args[1]) { continue; } if (type === 'A') { // SVG arc: A rx,ry,xAxisRotation,largeArcFlag,sweepFlag,x,y const rx = parseFloat(args[0]) * deviceCellWidth; const ry = parseFloat(args[1]) * deviceCellHeight; const xAxisRotation = parseFloat(args[2]) * Math.PI / 180; const largeArcFlag = parseInt(args[3]); const sweepFlag = parseInt(args[4]); const x = xOffset + parseFloat(args[5]) * deviceCellWidth; const y = yOffset + parseFloat(args[6]) * deviceCellHeight; drawSvgArc(ctx, currentX, currentY, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y); currentX = x; currentY = y; continue; } const translatedArgs = args.map((e, i) => { const val = parseFloat(e); return i % 2 === 0 ? xOffset + val * deviceCellWidth : yOffset + val * deviceCellHeight; }); if (type === 'M') { ctx.moveTo(translatedArgs[0], translatedArgs[1]); currentX = translatedArgs[0]; currentY = translatedArgs[1]; lastControlX = currentX; lastControlY = currentY; } else if (type === 'L') { ctx.lineTo(translatedArgs[0], translatedArgs[1]); currentX = translatedArgs[0]; currentY = translatedArgs[1]; lastControlX = currentX; lastControlY = currentY; } else if (type === 'Q') { ctx.quadraticCurveTo(translatedArgs[0], translatedArgs[1], translatedArgs[2], translatedArgs[3]); lastControlX = translatedArgs[0]; lastControlY = translatedArgs[1]; currentX = translatedArgs[2]; currentY = translatedArgs[3]; } else if (type === 'T') { // T uses reflection of last control point if previous command was Q or T let cpX: number; let cpY: number; if (lastCommand === 'Q' || lastCommand === 'T') { cpX = 2 * currentX - lastControlX; cpY = 2 * currentY - lastControlY; } else { cpX = currentX; cpY = currentY; } ctx.quadraticCurveTo(cpX, cpY, translatedArgs[0], translatedArgs[1]); lastControlX = cpX; lastControlY = cpY; currentX = translatedArgs[0]; currentY = translatedArgs[1]; } else if (type === 'C') { ctx.bezierCurveTo(translatedArgs[0], translatedArgs[1], translatedArgs[2], translatedArgs[3], translatedArgs[4], translatedArgs[5]); lastControlX = translatedArgs[2]; lastControlY = translatedArgs[3]; currentX = translatedArgs[4]; currentY = translatedArgs[5]; } lastCommand = type; } if (strokeWidth !== undefined) { ctx.strokeStyle = ctx.fillStyle; ctx.lineWidth = devicePixelRatio * strokeWidth; ctx.stroke(); } else { ctx.fill(); } } /** * Converts SVG arc parameters to canvas arc/ellipse calls. * Based on the SVG spec's endpoint to center parameterization conversion. */ function drawSvgArc( ctx: CanvasRenderingContext2D, x1: number, y1: number, rx: number, ry: number, phi: number, largeArcFlag: number, sweepFlag: number, x2: number, y2: number ): void { // Handle degenerate cases if (rx === 0 || ry === 0) { ctx.lineTo(x2, y2); return; } rx = Math.abs(rx); ry = Math.abs(ry); const cosPhi = Math.cos(phi); const sinPhi = Math.sin(phi); // Step 1: Compute (x1', y1') const dx = (x1 - x2) / 2; const dy = (y1 - y2) / 2; const x1p = cosPhi * dx + sinPhi * dy; const y1p = -sinPhi * dx + cosPhi * dy; // Step 2: Compute (cx', cy') let rxSq = rx * rx; let rySq = ry * ry; const x1pSq = x1p * x1p; const y1pSq = y1p * y1p; // Correct radii if necessary const lambda = x1pSq / rxSq + y1pSq / rySq; if (lambda > 1) { const lambdaSqrt = Math.sqrt(lambda); rx *= lambdaSqrt; ry *= lambdaSqrt; rxSq = rx * rx; rySq = ry * ry; } let sq = (rxSq * rySq - rxSq * y1pSq - rySq * x1pSq) / (rxSq * y1pSq + rySq * x1pSq); if (sq < 0) sq = 0; const coef = (largeArcFlag === sweepFlag ? -1 : 1) * Math.sqrt(sq); const cxp = coef * (rx * y1p / ry); const cyp = coef * -(ry * x1p / rx); // Step 3: Compute (cx, cy) from (cx', cy') const cx = cosPhi * cxp - sinPhi * cyp + (x1 + x2) / 2; const cy = sinPhi * cxp + cosPhi * cyp + (y1 + y2) / 2; // Step 4: Compute angles const ux = (x1p - cxp) / rx; const uy = (y1p - cyp) / ry; const vx = (-x1p - cxp) / rx; const vy = (-y1p - cyp) / ry; const startAngle = Math.atan2(uy, ux); let dTheta = Math.atan2(vy, vx) - startAngle; if (sweepFlag === 0 && dTheta > 0) { dTheta -= 2 * Math.PI; } else if (sweepFlag === 1 && dTheta < 0) { dTheta += 2 * Math.PI; } const endAngle = startAngle + dTheta; ctx.ellipse(cx, cy, rx, ry, phi, startAngle, endAngle, sweepFlag === 0); } /** * Draws a "negative" path where the background color is used to draw the shape on top of a * foreground-filled cell. This creates the appearance of a cutout without using actual * transparency, which allows SPAA (subpixel anti-aliasing) to work correctly. * * @param ctx The canvas rendering context (fillStyle should be set to foreground color) * @param charDefinition The vector shape definition for the negative shape * @param xOffset The x offset to draw at * @param yOffset The y offset to draw at * @param deviceCellWidth The width of the cell in device pixels * @param deviceCellHeight The height of the cell in device pixels * @param devicePixelRatio The device pixel ratio * @param backgroundColor The background color to use for the "cutout" portion */ function drawPathNegativeDefinitionCharacter( ctx: CanvasRenderingContext2D, charDefinition: ICustomGlyphVectorShape, xOffset: number, yOffset: number, deviceCellWidth: number, deviceCellHeight: number, devicePixelRatio: number, backgroundColor?: string ): void { ctx.save(); // First, fill the entire cell with foreground color ctx.fillRect(xOffset, yOffset, deviceCellWidth, deviceCellHeight); // Then draw the "negative" shape with the background color if (backgroundColor) { ctx.fillStyle = backgroundColor; ctx.strokeStyle = backgroundColor; } ctx.lineWidth = devicePixelRatio; ctx.lineCap = 'square'; ctx.beginPath(); for (const instruction of charDefinition.d.split(' ')) { const type = instruction[0]; const args: string[] = instruction.substring(1).split(','); if (!args[0] || !args[1]) { if (type === 'Z') { ctx.closePath(); } continue; } const translatedArgs = args.map((e, i) => { const val = parseFloat(e); return i % 2 === 0 ? xOffset + val * deviceCellWidth : yOffset + val * deviceCellHeight; }); if (type === 'M') { ctx.moveTo(translatedArgs[0], translatedArgs[1]); } else if (type === 'L') { ctx.lineTo(translatedArgs[0], translatedArgs[1]); } } if (charDefinition.type === CustomGlyphVectorType.STROKE) { ctx.stroke(); } else { ctx.fill(); } ctx.restore(); } const cachedPatterns: Map> = new Map(); function drawPatternChar( ctx: CanvasRenderingContext2D, charDefinition: number[][], xOffset: number, yOffset: number, deviceCellWidth: number, deviceCellHeight: number, variantOffset: number = 0 ): void { let patternSet = cachedPatterns.get(charDefinition); if (!patternSet) { patternSet = new Map(); cachedPatterns.set(charDefinition, patternSet); } const fillStyle = ctx.fillStyle; if (typeof fillStyle !== 'string') { throw new Error(`Unexpected fillStyle type "${fillStyle}"`); } let pattern = patternSet.get(fillStyle); if (!pattern) { const width = charDefinition[0].length; const height = charDefinition.length; const tmpCanvas = ctx.canvas.ownerDocument.createElement('canvas'); tmpCanvas.width = width; tmpCanvas.height = height; const tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d')); const imageData = new ImageData(width, height); // Extract rgba from fillStyle let r: number; let g: number; let b: number; let a: number; if (fillStyle.startsWith('#')) { r = parseInt(fillStyle.slice(1, 3), 16); g = parseInt(fillStyle.slice(3, 5), 16); b = parseInt(fillStyle.slice(5, 7), 16); a = fillStyle.length > 7 && parseInt(fillStyle.slice(7, 9), 16) || 1; } else if (fillStyle.startsWith('rgba')) { ([r, g, b, a] = fillStyle.substring(5, fillStyle.length - 1).split(',').map(e => parseFloat(e))); } else { throw new Error(`Unexpected fillStyle color format "${fillStyle}" when drawing pattern glyph`); } for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { imageData.data[(y * width + x) * 4 ] = r; imageData.data[(y * width + x) * 4 + 1] = g; imageData.data[(y * width + x) * 4 + 2] = b; imageData.data[(y * width + x) * 4 + 3] = charDefinition[y][x] * (a * 255); } } tmpCtx.putImageData(imageData, 0, 0); pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null)); patternSet.set(fillStyle, pattern); } // Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd. // variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift. const dx = (variantOffset >> 1) & 1; const dy = variantOffset & 1; if (dx !== 0 || dy !== 0) { pattern.setTransform(new DOMMatrix().translateSelf(-dx, -dy)); } else { pattern.setTransform(new DOMMatrix()); } ctx.fillStyle = pattern; ctx.fillRect(xOffset, yOffset, deviceCellWidth, deviceCellHeight); } function drawPathFunctionCharacter( ctx: CanvasRenderingContext2D, charDefinition: string | ((xp: number, yp: number) => string), xOffset: number, yOffset: number, deviceCellWidth: number, deviceCellHeight: number, devicePixelRatio: number, strokeWidth?: number ): void { ctx.save(); ctx.beginPath(); ctx.rect(xOffset, yOffset, deviceCellWidth, deviceCellHeight); ctx.clip(); ctx.beginPath(); let actualInstructions: string; if (typeof charDefinition === 'function') { const xp = .15; const yp = .15 / deviceCellHeight * deviceCellWidth; actualInstructions = charDefinition(xp, yp); } else { actualInstructions = charDefinition; } const state: ISvgPathState = { currentX: 0, currentY: 0, lastControlX: 0, lastControlY: 0, lastCommand: '' }; for (const instruction of actualInstructions.split(' ')) { const type = instruction[0]; if (type === 'Z') { ctx.closePath(); state.lastCommand = type; continue; } const f = svgToCanvasInstructionMap[type]; if (!f) { console.error(`Could not find drawing instructions for "${type}"`); continue; } const args: string[] = instruction.substring(1).split(','); if (!args[0] || !args[1]) { continue; } f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio, 0, 0, false), state); state.lastCommand = type; } if (strokeWidth !== undefined) { ctx.strokeStyle = ctx.fillStyle; ctx.lineWidth = devicePixelRatio * strokeWidth; ctx.stroke(); } else { ctx.fill(); } ctx.closePath(); ctx.restore(); } /** * Applies a clip path to the canvas context from SVG-like path instructions. */ function applyClipPath( ctx: CanvasRenderingContext2D, clipPath: string, xOffset: number, yOffset: number, deviceCellWidth: number, deviceCellHeight: number ): void { ctx.beginPath(); for (const instruction of clipPath.split(' ')) { const type = instruction[0]; if (type === 'Z') { ctx.closePath(); continue; } const args: string[] = instruction.substring(1).split(','); if (!args[0] || !args[1]) { continue; } const x = xOffset + parseFloat(args[0]) * deviceCellWidth; const y = yOffset + parseFloat(args[1]) * deviceCellHeight; if (type === 'M') { ctx.moveTo(x, y); } else if (type === 'L') { ctx.lineTo(x, y); } } ctx.clip(); } function drawVectorShape( ctx: CanvasRenderingContext2D, charDefinition: ICustomGlyphVectorShape, xOffset: number, yOffset: number, deviceCellWidth: number, deviceCellHeight: number, fontSize: number, devicePixelRatio: number ): void { // Clip the cell to make sure drawing doesn't occur beyond bounds const clipRegion = new Path2D(); clipRegion.rect(xOffset, yOffset, deviceCellWidth, deviceCellHeight); ctx.clip(clipRegion); ctx.beginPath(); // Scale the stroke with DPR and font size const cssLineWidth = fontSize / 12; ctx.lineWidth = devicePixelRatio * cssLineWidth; const state: ISvgPathState = { currentX: 0, currentY: 0, lastControlX: 0, lastControlY: 0, lastCommand: '' }; for (const instruction of charDefinition.d.split(' ')) { const type = instruction[0]; if (type === 'Z') { ctx.closePath(); state.lastCommand = type; continue; } const f = svgToCanvasInstructionMap[type]; if (!f) { console.error(`Could not find drawing instructions for "${type}"`); continue; } const args: string[] = instruction.substring(1).split(','); if (!args[0] || !args[1]) { continue; } f(ctx, translateArgs( args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, false, devicePixelRatio, (charDefinition.leftPadding ?? 0) * (cssLineWidth / 2), (charDefinition.rightPadding ?? 0) * (cssLineWidth / 2) ), state); state.lastCommand = type; } if (charDefinition.type === CustomGlyphVectorType.STROKE) { ctx.strokeStyle = ctx.fillStyle; ctx.stroke(); } else { ctx.fill(); } ctx.closePath(); } function clamp(value: number, max: number, min: number = 0): number { return Math.max(Math.min(value, max), min); } interface ISvgPathState { currentX: number; currentY: number; lastControlX: number; lastControlY: number; lastCommand: string; } const svgToCanvasInstructionMap: { [index: string]: (ctx: CanvasRenderingContext2D, args: number[], state: ISvgPathState) => void } = { 'C': (ctx, args, state) => { ctx.bezierCurveTo(args[0], args[1], args[2], args[3], args[4], args[5]); state.lastControlX = args[2]; state.lastControlY = args[3]; state.currentX = args[4]; state.currentY = args[5]; }, 'L': (ctx, args, state) => { ctx.lineTo(args[0], args[1]); state.lastControlX = state.currentX = args[0]; state.lastControlY = state.currentY = args[1]; }, 'M': (ctx, args, state) => { ctx.moveTo(args[0], args[1]); state.lastControlX = state.currentX = args[0]; state.lastControlY = state.currentY = args[1]; }, 'Q': (ctx, args, state) => { ctx.quadraticCurveTo(args[0], args[1], args[2], args[3]); state.lastControlX = args[0]; state.lastControlY = args[1]; state.currentX = args[2]; state.currentY = args[3]; }, 'T': (ctx, args, state) => { let cpX: number; let cpY: number; if (state.lastCommand === 'Q' || state.lastCommand === 'T') { cpX = 2 * state.currentX - state.lastControlX; cpY = 2 * state.currentY - state.lastControlY; } else { cpX = state.currentX; cpY = state.currentY; } ctx.quadraticCurveTo(cpX, cpY, args[0], args[1]); state.lastControlX = cpX; state.lastControlY = cpY; state.currentX = args[0]; state.currentY = args[1]; } }; function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0, clampToCell: boolean = true): number[] { const result = args.map(e => parseFloat(e) || parseInt(e)); if (result.length < 2) { throw new Error('Too few arguments for instruction'); } for (let x = 0; x < result.length; x += 2) { // Translate from 0-1 to 0-cellWidth result[x] *= cellWidth - (leftPadding * devicePixelRatio) - (rightPadding * devicePixelRatio); // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally // clamp to the cell bounds. if (doClamp && result[x] !== 0) { const rounded = Math.round(result[x] + 0.5) - 0.5; result[x] = clampToCell ? clamp(rounded, cellWidth, 0) : rounded; } // Apply the cell's offset (ie. x*cellWidth) result[x] += xOffset + (leftPadding * devicePixelRatio); } for (let y = 1; y < result.length; y += 2) { // Translate from 0-1 to 0-cellHeight result[y] *= cellHeight; // Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally // clamp to the cell bounds. if (doClamp && result[y] !== 0) { const rounded = Math.round(result[y] + 0.5) - 0.5; result[y] = clampToCell ? clamp(rounded, cellHeight, 0) : rounded; } // Apply the cell's offset (ie. x*cellHeight) result[y] += yOffset; } return result; } ================================================ FILE: addons/addon-webgl/src/customGlyphs/Types.ts ================================================ /** * Copyright (c) 2021 The xterm.js authors. All rights reserved. * @license MIT */ export interface ICustomGlyphSolidOctantBlockVector { x: number; y: number; w: number; h: number; } /** * @param xp The percentage of 15% of the x axis. * @param yp The percentage of 15% of the x axis on the y axis. */ export type CustomGlyphPathDrawFunctionDefinition = (xp: number, yp: number) => string; export interface ICustomGlyphVectorShape { d: string; type: CustomGlyphVectorType; leftPadding?: number; rightPadding?: number; } export const enum CustomGlyphVectorType { FILL, STROKE } export type CustomGlyphPatternDefinition = number[][]; export const enum CustomGlyphDefinitionType { SOLID_OCTANT_BLOCK_VECTOR, BLOCK_PATTERN, PATH_FUNCTION, PATH, PATH_NEGATIVE, VECTOR_SHAPE, BRAILLE, } export type CustomGlyphDefinitionPartRaw = ( { type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: ICustomGlyphSolidOctantBlockVector[] } | { type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: CustomGlyphPatternDefinition } | { type: CustomGlyphDefinitionType.PATH_FUNCTION, data: CustomGlyphPathDrawFunctionDefinition | string } | { type: CustomGlyphDefinitionType.PATH, data: string } | { type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: ICustomGlyphVectorShape } | { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: ICustomGlyphVectorShape} | { type: CustomGlyphDefinitionType.BRAILLE, data: number } ); export const enum CustomGlyphScaleType { /** * Scale to the entire cell, including letter spacing and line height. */ CELL, /** * Scale to only the character area, excluding letter spacing and line height. */ CHAR, } export interface ICustomGlyphDefinitionCommon { /** * A custom clip path for the draw definition, restricting the area it can draw to. */ clipPath?: string; /** * The stroke width to use when drawing the path. Defaults to 1. */ strokeWidth?: number; /** * Defines how to scale the draw. Defaults to scaling to the full cell including letter spacing * and line height. */ scaleType?: CustomGlyphScaleType; } export type CustomGlyphDefinitionPart = CustomGlyphDefinitionPartRaw & ICustomGlyphDefinitionCommon; /** * A character definition that can be a single part or an array of parts drawn in sequence. */ export type CustomGlyphCharacterDefinition = CustomGlyphDefinitionPart | CustomGlyphDefinitionPart[]; ================================================ FILE: addons/addon-webgl/src/renderLayer/BaseRenderLayer.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { ReadonlyColorSet } from 'browser/Types'; import { acquireTextureAtlas } from '../CharAtlasCache'; import { IRenderDimensions } from 'browser/renderer/shared/Types'; import { ICoreBrowserService, IThemeService } from 'browser/services/Services'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { CellData } from 'common/buffer/CellData'; import { IOptionsService } from 'common/services/Services'; import { Terminal } from '@xterm/xterm'; import { IRenderLayer } from './Types'; import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils'; import { TEXT_BASELINE } from '../Constants'; import type { ITextureAtlas } 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 _charAtlas: ITextureAtlas | undefined; constructor( terminal: Terminal, private _container: HTMLElement, id: string, zIndex: number, private _alpha: boolean, protected readonly _coreBrowserService: ICoreBrowserService, protected readonly _optionsService: IOptionsService, protected readonly _themeService: IThemeService ) { super(); this._canvas = this._coreBrowserService.mainDocument.createElement('canvas'); this._canvas.classList.add(`xterm-${id}-layer`); this._canvas.style.zIndex = zIndex.toString(); this._initCanvas(); this._container.appendChild(this._canvas); this._register(this._themeService.onChangeColors(e => { this._refreshCharAtlas(terminal, e); this.reset(terminal); })); 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(terminal: Terminal): void {} public handleFocus(terminal: Terminal): void {} public handleCursorMove(terminal: Terminal): void {} public handleGridChanged(terminal: Terminal, startRow: number, endRow: number): void {} public handleSelectionChanged(terminal: Terminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean = false): void {} protected _setTransparency(terminal: Terminal, 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(terminal, this._themeService.colors); this.handleGridChanged(terminal, 0, terminal.rows - 1); } /** * Refreshes the char atlas, aquiring a new one if necessary. * @param terminal The terminal. * @param colorSet The color set to use for the char atlas. */ private _refreshCharAtlas(terminal: Terminal, colorSet: ReadonlyColorSet): void { if (this._deviceCharWidth <= 0 && this._deviceCharHeight <= 0) { return; } this._charAtlas = acquireTextureAtlas(terminal, this._optionsService.rawOptions, colorSet, this._deviceCellWidth, this._deviceCellHeight, this._deviceCharWidth, this._deviceCharHeight, this._coreBrowserService.dpr, 2048); this._charAtlas.warmUp(); } public resize(terminal: Terminal, 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(terminal, this._themeService.colors); } public abstract reset(terminal: Terminal): void; /** * 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): void { this._ctx.fillRect( x * this._deviceCellWidth, (y + 1) * this._deviceCellHeight - this._coreBrowserService.dpr - 1 /* Ensure it's drawn within the cell */, width * this._deviceCellWidth, this._coreBrowserService.dpr); } /** * 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 terminal The terminal. * @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(terminal: Terminal, cell: CellData, x: number, y: number): void { this._ctx.font = this._getFont(terminal, false, false); this._ctx.textBaseline = TEXT_BASELINE; this._clipCell(x, y, cell.getWidth()); this._ctx.fillText( cell.getChars(), x * this._deviceCellWidth + this._deviceCharLeft, y * this._deviceCellHeight + this._deviceCharTop + this._deviceCharHeight); } /** * Clips a cell to ensure no pixels will be drawn outside of it. * @param x The column to clip. * @param y The row to clip. * @param width The number of columns to clip. */ private _clipCell(x: number, y: number, width: number): void { this._ctx.beginPath(); this._ctx.rect( x * this._deviceCellWidth, y * this._deviceCellHeight, width * this._deviceCellWidth, this._deviceCellHeight); this._ctx.clip(); } /** * Gets the current font. * @param terminal The terminal. * @param isBold If we should use the bold fontWeight. */ protected _getFont(terminal: Terminal, isBold: boolean, isItalic: boolean): string { const fontWeight = isBold ? terminal.options.fontWeightBold : terminal.options.fontWeight; const fontStyle = isItalic ? 'italic' : ''; return `${fontStyle} ${fontWeight} ${terminal.options.fontSize! * this._coreBrowserService.dpr}px ${terminal.options.fontFamily}`; } } ================================================ FILE: addons/addon-webgl/src/renderLayer/LinkRenderLayer.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { is256Color } from '../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 { ILinkifier2, ILinkifierEvent } from 'browser/Types'; import { IOptionsService } from 'common/services/Services'; import { Terminal } from '@xterm/xterm'; import { BaseRenderLayer } from './BaseRenderLayer'; export class LinkRenderLayer extends BaseRenderLayer { private _state: ILinkifierEvent | undefined; constructor( container: HTMLElement, zIndex: number, terminal: Terminal, linkifier2: ILinkifier2, coreBrowserService: ICoreBrowserService, optionsService: IOptionsService, themeService: IThemeService ) { super(terminal, container, 'link', zIndex, true, coreBrowserService, optionsService, themeService); this._register(linkifier2.onShowLinkUnderline(e => this._handleShowLinkUnderline(e))); this._register(linkifier2.onHideLinkUnderline(e => this._handleHideLinkUnderline(e))); } public resize(terminal: Terminal, dim: IRenderDimensions): void { super.resize(terminal, dim); // Resizing the canvas discards the contents of the canvas so clear state this._state = undefined; } public reset(terminal: Terminal): void { this._clearCurrentLink(); } private _clearCurrentLink(): void { if (this._state) { this._clearCells(this._state.x1, this._state.y1, this._state.cols - this._state.x1, 1); const middleRowCount = this._state.y2 - this._state.y1 - 1; if (middleRowCount > 0) { this._clearCells(0, this._state.y1 + 1, this._state.cols, middleRowCount); } this._clearCells(0, this._state.y2, this._state.x2, 1); this._state = undefined; } } private _handleShowLinkUnderline(e: ILinkifierEvent): void { if (e.fg === INVERTED_DEFAULT_COLOR) { this._ctx.fillStyle = this._themeService.colors.background.css; } else if (e.fg !== undefined && is256Color(e.fg)) { // 256 color support this._ctx.fillStyle = this._themeService.colors.ansi[e.fg!].css; } else { this._ctx.fillStyle = this._themeService.colors.foreground.css; } if (e.y1 === e.y2) { // Single line link this._fillBottomLineAtCells(e.x1, e.y1, e.x2 - e.x1); } else { // Multi-line link this._fillBottomLineAtCells(e.x1, e.y1, e.cols - e.x1); for (let y = e.y1 + 1; y < e.y2; y++) { this._fillBottomLineAtCells(0, y, e.cols); } this._fillBottomLineAtCells(0, e.y2, e.x2); } this._state = e; } private _handleHideLinkUnderline(e: ILinkifierEvent): void { this._clearCurrentLink(); } } ================================================ FILE: addons/addon-webgl/src/renderLayer/Types.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { IDisposable, Terminal } from '@xterm/xterm'; import { IRenderDimensions } from 'browser/renderer/shared/Types'; export interface IRenderLayer extends IDisposable { /** * Called when the terminal loses focus. */ handleBlur(terminal: Terminal): void; /** * Called when the terminal gets focus. */ handleFocus(terminal: Terminal): void; /** * Called when the cursor is moved. */ handleCursorMove(terminal: Terminal): void; /** * Called when the data in the grid has changed (or needs to be rendered * again). */ handleGridChanged(terminal: Terminal, startRow: number, endRow: number): void; /** * Calls when the selection changes. */ handleSelectionChanged(terminal: Terminal, start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void; /** * Registers a handler to join characters to render as a group */ registerCharacterJoiner?(handler: (text: string) => [number, number][]): void; /** * Deregisters the specified character joiner handler */ deregisterCharacterJoiner?(joinerId: number): void; /** * Resize the render layer. */ resize(terminal: Terminal, dim: IRenderDimensions): void; /** * Clear the state of the render layer. */ reset(terminal: Terminal): void; } ================================================ FILE: addons/addon-webgl/src/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "es2021", "lib": [ "dom", "es2021" ], "rootDir": ".", "outDir": "../out", "sourceMap": true, "removeComments": true, "paths": { "common/*": [ "../../../src/common/*" ], "browser/*": [ "../../../src/browser/*" ], "@xterm/addon-webgl": [ "../typings/addon-webgl.d.ts" ], "*": [ "./*" ] }, "strict": true, "downlevelIteration": true, "experimentalDecorators": true, "types": [ "../../../node_modules/@types/mocha" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" } ] } ================================================ FILE: addons/addon-webgl/test/WebglRenderer.test.ts ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ import test from '@playwright/test'; import { ISharedRendererTestContext, injectSharedRendererTests, injectSharedRendererTestsStandalone } from '../../../test/playwright/SharedRendererTests'; import { ITestContext, createTestContext, openTerminal } from '../../../test/playwright/TestUtils'; import { platform } from 'os'; let ctx: ITestContext; const ctxWrapper: ISharedRendererTestContext = { value: undefined } as any; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); await openTerminal(ctx); ctxWrapper.value = ctx; await ctx.page.evaluate(` window.addon = new window.WebglAddon(true); window.term.loadAddon(window.addon); `); }); test.afterAll(async () => await ctx.page.close()); test.describe('WebGL Renderer Integration Tests', async () => { // HACK: webgl2 is often not supported in headless firefox on Linux // https://github.com/microsoft/playwright/issues/11566 if (platform() === 'linux') { test.skip(({ browserName }) => browserName === 'firefox'); } injectSharedRendererTests(ctxWrapper); injectSharedRendererTestsStandalone(ctxWrapper, async () => { await ctx.page.evaluate(` window.addon = new window.WebglAddon(true); window.term.loadAddon(window.addon); `); }); }); ================================================ FILE: addons/addon-webgl/test/playwright.config.ts ================================================ import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { testDir: '.', timeout: 10000, projects: [ { name: 'Chromium', use: { browserName: 'chromium' } }, { name: 'FirefoxStable', use: { browserName: 'firefox' } }, { name: 'WebKit', use: { browserName: 'webkit' } } ], reporter: 'list', webServer: { command: 'npm run start', port: 3000, timeout: 120000, reuseExistingServer: !process.env.CI } }; export default config; ================================================ FILE: addons/addon-webgl/test/tsconfig.json ================================================ { "compilerOptions": { "module": "commonjs", "target": "ESNext", "lib": [ "es2021", ], "rootDir": ".", "outDir": "../out-test", "sourceMap": true, "removeComments": true, "paths": { "common/*": [ "../../../src/common/*" ], "browser/*": [ "../../../src/browser/*" ], "*": [ "./*" ] }, "strict": true, "types": [ "../../../node_modules/@types/node", "../../../node_modules/@lunapaint/png-codec" ] }, "include": [ "./**/*", "../../../typings/xterm.d.ts" ], "references": [ { "path": "../../../src/common" }, { "path": "../../../src/browser" }, { "path": "../../../test/playwright" } ] } ================================================ FILE: addons/addon-webgl/tsconfig.json ================================================ { "files": [], "include": [], "references": [ { "path": "./src" }, { "path": "./test" } ] } ================================================ FILE: addons/addon-webgl/typings/addon-webgl.d.ts ================================================ /** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { Terminal, ITerminalAddon, IEvent } from '@xterm/xterm'; declare module '@xterm/addon-webgl' { /** * An xterm.js addon that provides hardware-accelerated rendering functionality via WebGL. */ export class WebglAddon implements ITerminalAddon { public textureAtlas?: HTMLCanvasElement; /** * An event that is fired when the renderer loses its canvas context. */ public readonly onContextLoss: IEvent; /** * An event that is fired when the texture atlas of the renderer changes. */ public readonly onChangeTextureAtlas: IEvent; /** * An event that is fired when the a new page is added to the texture atlas. */ public readonly onAddTextureAtlasCanvas: IEvent; /** * An event that is fired when the a page is removed from the texture atlas. */ public readonly onRemoveTextureAtlasCanvas: IEvent; constructor(options?: IWebglAddonOptions); /** * 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; } export interface IWebglAddonOptions { /** * Whether to draw custom glyphs instead of using the font for the following * unicode ranges: * * - Box Drawing (U+2500-U+257F) * - Block Elements (U+2580-U+259F) * - Braille Patterns (U+2800-U+28FF) * - Powerline Symbols (U+E0A0-U+E0D4, Private Use Area with widespread * adoption) * - Progress Indicators (U+EE00-U+EE0B, Private Use Area initially added in * [Fira Code](https://github.com/tonsky/FiraCode) and later * [Nerd Fonts](https://github.com/ryanoasis/nerd-fonts/pull/1733)). * - Git Branch Symbols (U+F5D0-U+F60D, Private Use Area initially adopted * in [Kitty in 2024](https://github.com/kovidgoyal/kitty/pull/7681) by * author of [vim-flog](https://github.com/rbong/vim-flog)) * - Symbols for Legacy Computing (U+1FB00-U+1FBFF) * * This will typically result in better rendering with continuous lines, * even when line height and letter spacing is used. The default is true. */ customGlyphs?: boolean; /** * Whether to enable the preserveDrawingBuffer flag when creating the WebGL * context. This may be useful in tests. This default is false. */ preserveDrawingBuffer?: boolean } } ================================================ FILE: addons/addon-webgl/webpack.config.js ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ const path = require('path'); const addonName = 'WebglAddon'; const mainFile = 'addon-webgl.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'), vs: path.resolve('../../out/vs') } }, output: { filename: mainFile, path: path.resolve('./lib'), library: addonName, libraryTarget: 'umd', // Force usage of globalThis instead of global / self. (This is cross-env compatible) globalObject: 'globalThis', }, mode: 'production' }; ================================================ FILE: bin/agent/setup-repo.mjs ================================================ // @ts-check import { cpSync, existsSync, lstatSync, readFileSync } from 'node:fs'; import { spawnSync } from 'node:child_process'; import { basename, dirname, resolve, sep } from 'node:path'; import { fileURLToPath } from 'node:url'; const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../..'); const nodeModulesPath = resolve(repoRoot, 'node_modules'); const npmExecutable = process.platform === 'win32' ? 'npm.cmd' : 'npm'; /** @typedef {{ folder: string; reason: string }} Candidate */ /** @param {string} message */ function log(message) { console.info(`[setup-fast] ${message}`); } /** @param {string[]} args */ function runNpm(args) { log(`Running: npm ${args.join(' ')}`); const result = spawnSync(npmExecutable, args, { cwd: repoRoot, stdio: 'inherit' }); if (result.error) { throw result.error; } if ((result.status ?? 1) !== 0) { process.exit(result.status ?? 1); } } /** * @param {Candidate[]} candidates * @param {string | undefined} folder * @param {string} reason */ function addCandidate(candidates, folder, reason) { if (!folder) { return; } if (candidates.some(candidate => candidate.folder === folder)) { return; } candidates.push({ folder, reason }); log(`Candidate found (${reason}): ${folder}`); } function detectMainSiblingFolder() { const currentFolderName = basename(repoRoot); if (!currentFolderName.startsWith('xterm.js') || currentFolderName === 'xterm.js') { log(`Current folder is "${currentFolderName}", skipping xterm.js sibling lookup.`); return undefined; } const siblingFolder = resolve(dirname(repoRoot), 'xterm.js'); log(`Current folder is "${currentFolderName}", sibling main folder candidate: ${siblingFolder}`); return siblingFolder; } function detectWorktreeMainFolder() { const gitPath = resolve(repoRoot, '.git'); if (!existsSync(gitPath)) { log('No .git entry found at repo root.'); return undefined; } const gitStat = lstatSync(gitPath); if (!gitStat.isFile()) { log('.git is not a file, this repo does not appear to be a worktree checkout.'); return undefined; } const gitFileContent = readFileSync(gitPath, 'utf8').trim(); const gitDirMatch = /^gitdir:\s*(.+)$/m.exec(gitFileContent); if (!gitDirMatch) { log('Could not parse gitdir from .git file.'); return undefined; } const gitDirPath = resolve(repoRoot, gitDirMatch[1].trim()); log(`Parsed gitdir from .git file: ${gitDirPath}`); const normalizedGitDirPath = gitDirPath.replace(/\\/g, '/'); const worktreeMarker = '/.git/worktrees/'; const markerIndex = normalizedGitDirPath.indexOf(worktreeMarker); if (markerIndex === -1) { log('gitdir path does not contain /.git/worktrees/, skipping worktree main folder lookup.'); return undefined; } const normalizedMainFolder = normalizedGitDirPath.slice(0, markerIndex); const mainFolder = sep === '/' ? normalizedMainFolder : normalizedMainFolder.split('/').join(sep); log(`Worktree main folder candidate: ${mainFolder}`); return mainFolder; } function resolveSourceFolder() { /** @type {Candidate[]} */ const candidates = []; addCandidate(candidates, detectMainSiblingFolder(), 'xterm.js sibling'); addCandidate(candidates, detectWorktreeMainFolder(), 'worktree main repo'); for (const candidate of candidates) { const candidateNodeModulesPath = resolve(candidate.folder, 'node_modules'); if (existsSync(candidateNodeModulesPath)) { log(`Using candidate (${candidate.reason}) with node_modules: ${candidate.folder}`); return candidate.folder; } log(`Candidate skipped (${candidate.reason}), node_modules missing: ${candidateNodeModulesPath}`); } log('No candidate folder with node_modules was found.'); return undefined; } if (!existsSync(nodeModulesPath)) { log(`node_modules missing: ${nodeModulesPath}`); const sourceFolder = resolveSourceFolder(); if (sourceFolder) { const sourceNodeModulesPath = resolve(sourceFolder, 'node_modules'); log(`Copying node_modules from ${sourceNodeModulesPath} to ${nodeModulesPath}`); try { cpSync(sourceNodeModulesPath, nodeModulesPath, { recursive: true }); log('node_modules copy completed.'); } catch (error) { const message = error instanceof Error ? error.message : String(error); log(`node_modules copy failed: ${message}`); log('Falling back to npm ci.'); runNpm(['ci']); } } else { log('No source folder available, running npm ci.'); runNpm(['ci']); } } else { log(`node_modules already exists: ${nodeModulesPath}`); } runNpm(['run', 'setup']); ================================================ FILE: bin/convert_svg_to_custom_glyph.js ================================================ /** * Converts an SVG file as exported by fontforge into the SVG-like format as expected by the custom * glyph rasterizer. * * Usage: node convert_svg_to_custom_glyph.js */ const fs = require('fs'); const path = require('path'); const input = process.argv[2]; if (!input) { console.error('Usage: node convert_svg_to_custom_glyph.js '); process.exit(1); } const inputPath = path.resolve(process.cwd(), input); const stat = fs.statSync(inputPath); const files = stat.isDirectory() ? fs.readdirSync(inputPath).filter(f => f.endsWith('.svg')).map(f => path.join(inputPath, f)) : [inputPath]; if (files.length === 0) { console.error('No SVG files found'); process.exit(1); } for (const file of files) { console.log(`\n${'='.repeat(60)}\nProcessing: ${path.basename(file)}\n${'='.repeat(60)}`); processFile(file); } function processFile(filePath) { // Get file content const content = fs.readFileSync(filePath, 'utf8'); // Get viewBox const viewBoxMatch = content.match(/viewBox="([^"]+)"/); if (!viewBoxMatch) { console.error('No viewBox found in SVG'); return; } const [minX, minY, width, height] = viewBoxMatch[1].split(/\s+/).map(Number); console.log(`ViewBox: ${minX} ${minY} ${width} ${height}`); // Get path `d` property const pathMatch = content.match(/]*\sd="([^"]+)"/); if (!pathMatch) { console.error('No path d attribute found in SVG'); return; } const originalPath = pathMatch[1].replace(/\s+/g, ' ').trim(); console.log(`\nOriginal path length: ${originalPath.length} chars`); // Parse path into commands function parsePath(d) { const commands = []; const regex = /([MmLlHhVvCcSsQqTtAaZz])([^MmLlHhVvCcSsQqTtAaZz]*)/g; let match; while ((match = regex.exec(d)) !== null) { const cmd = match[1]; const argsStr = match[2].trim(); const args = argsStr ? argsStr.split(/[\s,]+/).map(Number) : []; commands.push({ cmd, args }); } return commands; } // Convert relative commands to absolute and expand T/S to Q/C function toAbsolute(commands) { const result = []; let x = 0, y = 0; // Current position let startX = 0, startY = 0; // Start of current subpath let lastControlX = 0, lastControlY = 0; // Last control point for T/S let lastCmd = ''; for (const { cmd, args } of commands) { const isRelative = cmd === cmd.toLowerCase(); const absCmd = cmd.toUpperCase(); switch (absCmd) { case 'M': { // MoveTo: M x y (or m dx dy) const absArgs = []; for (let i = 0; i < args.length; i += 2) { const newX = isRelative ? x + args[i] : args[i]; const newY = isRelative ? y + args[i + 1] : args[i + 1]; absArgs.push(newX, newY); x = newX; y = newY; if (i === 0) { startX = x; startY = y; } } lastControlX = x; lastControlY = y; result.push({ cmd: 'M', args: absArgs }); break; } case 'L': { // LineTo: L x y (or l dx dy) const absArgs = []; for (let i = 0; i < args.length; i += 2) { const newX = isRelative ? x + args[i] : args[i]; const newY = isRelative ? y + args[i + 1] : args[i + 1]; absArgs.push(newX, newY); x = newX; y = newY; } lastControlX = x; lastControlY = y; result.push({ cmd: 'L', args: absArgs }); break; } case 'H': { // Horizontal LineTo - convert to L for (let i = 0; i < args.length; i++) { const newX = isRelative ? x + args[i] : args[i]; result.push({ cmd: 'L', args: [newX, y] }); x = newX; } lastControlX = x; lastControlY = y; break; } case 'V': { // Vertical LineTo - convert to L for (let i = 0; i < args.length; i++) { const newY = isRelative ? y + args[i] : args[i]; result.push({ cmd: 'L', args: [x, newY] }); y = newY; } lastControlX = x; lastControlY = y; break; } case 'C': { // CurveTo: C x1 y1 x2 y2 x y (or c dx1 dy1 dx2 dy2 dx dy) const absArgs = []; for (let i = 0; i < args.length; i += 6) { const x1 = isRelative ? x + args[i] : args[i]; const y1 = isRelative ? y + args[i + 1] : args[i + 1]; const x2 = isRelative ? x + args[i + 2] : args[i + 2]; const y2 = isRelative ? y + args[i + 3] : args[i + 3]; const newX = isRelative ? x + args[i + 4] : args[i + 4]; const newY = isRelative ? y + args[i + 5] : args[i + 5]; absArgs.push(x1, y1, x2, y2, newX, newY); lastControlX = x2; lastControlY = y2; x = newX; y = newY; } result.push({ cmd: 'C', args: absArgs }); break; } case 'S': { // Smooth CurveTo - expand to C for (let i = 0; i < args.length; i += 4) { // Reflect last control point let x1, y1; if (lastCmd === 'C' || lastCmd === 'S') { x1 = 2 * x - lastControlX; y1 = 2 * y - lastControlY; } else { x1 = x; y1 = y; } const x2 = isRelative ? x + args[i] : args[i]; const y2 = isRelative ? y + args[i + 1] : args[i + 1]; const newX = isRelative ? x + args[i + 2] : args[i + 2]; const newY = isRelative ? y + args[i + 3] : args[i + 3]; result.push({ cmd: 'C', args: [x1, y1, x2, y2, newX, newY] }); lastControlX = x2; lastControlY = y2; x = newX; y = newY; } break; } case 'Q': { // Quadratic CurveTo: Q x1 y1 x y (or q dx1 dy1 dx dy) const absArgs = []; for (let i = 0; i < args.length; i += 4) { const x1 = isRelative ? x + args[i] : args[i]; const y1 = isRelative ? y + args[i + 1] : args[i + 1]; const newX = isRelative ? x + args[i + 2] : args[i + 2]; const newY = isRelative ? y + args[i + 3] : args[i + 3]; absArgs.push(x1, y1, newX, newY); lastControlX = x1; lastControlY = y1; x = newX; y = newY; } result.push({ cmd: 'Q', args: absArgs }); break; } case 'T': { // Smooth Quadratic CurveTo - keep as T const absArgs = []; for (let i = 0; i < args.length; i += 2) { // Reflect last control point for tracking let cpX, cpY; if (lastCmd === 'Q' || lastCmd === 'T') { cpX = 2 * x - lastControlX; cpY = 2 * y - lastControlY; } else { cpX = x; cpY = y; } const newX = isRelative ? x + args[i] : args[i]; const newY = isRelative ? y + args[i + 1] : args[i + 1]; absArgs.push(newX, newY); lastControlX = cpX; lastControlY = cpY; x = newX; y = newY; lastCmd = 'T'; // For chained T commands } result.push({ cmd: 'T', args: absArgs }); break; } case 'A': { // Arc: A rx ry x-axis-rotation large-arc-flag sweep-flag x y const absArgs = []; for (let i = 0; i < args.length; i += 7) { const rx = args[i]; const ry = args[i + 1]; const rotation = args[i + 2]; const largeArc = args[i + 3]; const sweep = args[i + 4]; const newX = isRelative ? x + args[i + 5] : args[i + 5]; const newY = isRelative ? y + args[i + 6] : args[i + 6]; absArgs.push(rx, ry, rotation, largeArc, sweep, newX, newY); x = newX; y = newY; } lastControlX = x; lastControlY = y; result.push({ cmd: 'A', args: absArgs }); break; } case 'Z': { // ClosePath x = startX; y = startY; lastControlX = x; lastControlY = y; result.push({ cmd: 'Z', args: [] }); break; } } if (absCmd !== 'T') { lastCmd = absCmd; } } return result; } // Scale coordinates to 0-1 range function scaleToNormalized(commands, minX, minY, width, height) { function scaleX(val) { return (val - minX) / width; } function scaleY(val) { return (val - minY) / height; } function scaleRx(val) { return val / width; } function scaleRy(val) { return val / height; } const result = []; for (const { cmd, args } of commands) { const scaledArgs = []; switch (cmd) { case 'M': case 'L': case 'T': { for (let i = 0; i < args.length; i += 2) { scaledArgs.push(scaleX(args[i]), scaleY(args[i + 1])); } break; } case 'H': { for (let i = 0; i < args.length; i++) { scaledArgs.push(scaleX(args[i])); } break; } case 'V': { for (let i = 0; i < args.length; i++) { scaledArgs.push(scaleY(args[i])); } break; } case 'C': { for (let i = 0; i < args.length; i += 6) { scaledArgs.push( scaleX(args[i]), scaleY(args[i + 1]), scaleX(args[i + 2]), scaleY(args[i + 3]), scaleX(args[i + 4]), scaleY(args[i + 5]) ); } break; } case 'S': case 'Q': { for (let i = 0; i < args.length; i += 4) { scaledArgs.push( scaleX(args[i]), scaleY(args[i + 1]), scaleX(args[i + 2]), scaleY(args[i + 3]) ); } break; } case 'A': { for (let i = 0; i < args.length; i += 7) { // rx, ry need to be scaled; rotation and flags stay the same scaledArgs.push( scaleRx(args[i]), // rx scaleRy(args[i + 1]), // ry args[i + 2], // rotation args[i + 3], // large-arc args[i + 4], // sweep scaleX(args[i + 5]), // x scaleY(args[i + 6]) // y ); } break; } case 'Z': { // No args break; } } result.push({ cmd, args: scaledArgs }); } return result; } // Format number to reasonable precision function formatNum(n, precision = 4) { const rounded = Number(n.toFixed(precision)); return String(rounded); } // Convert commands back to path string function commandsToPath(commands) { return commands.map(({ cmd, args }, i) => { const prefix = i === 0 ? '' : ' '; if (args.length === 0) return prefix + cmd; return prefix + cmd + args.map(a => formatNum(a)).join(','); }).join(''); } // Main conversion const parsed = parsePath(originalPath); const absolute = toAbsolute(parsed); // Calculate actual bounding box from path data function getBoundingBox(commands) { let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; for (const { cmd, args } of commands) { switch (cmd) { case 'M': case 'L': case 'T': { for (let i = 0; i < args.length; i += 2) { minX = Math.min(minX, args[i]); maxX = Math.max(maxX, args[i]); minY = Math.min(minY, args[i + 1]); maxY = Math.max(maxY, args[i + 1]); } break; } case 'H': { for (let i = 0; i < args.length; i++) { minX = Math.min(minX, args[i]); maxX = Math.max(maxX, args[i]); } break; } case 'V': { for (let i = 0; i < args.length; i++) { minY = Math.min(minY, args[i]); maxY = Math.max(maxY, args[i]); } break; } case 'C': { for (let i = 0; i < args.length; i += 6) { // Include control points and endpoint minX = Math.min(minX, args[i], args[i + 2], args[i + 4]); maxX = Math.max(maxX, args[i], args[i + 2], args[i + 4]); minY = Math.min(minY, args[i + 1], args[i + 3], args[i + 5]); maxY = Math.max(maxY, args[i + 1], args[i + 3], args[i + 5]); } break; } case 'S': case 'Q': { for (let i = 0; i < args.length; i += 4) { minX = Math.min(minX, args[i], args[i + 2]); maxX = Math.max(maxX, args[i], args[i + 2]); minY = Math.min(minY, args[i + 1], args[i + 3]); maxY = Math.max(maxY, args[i + 1], args[i + 3]); } break; } case 'A': { for (let i = 0; i < args.length; i += 7) { minX = Math.min(minX, args[i + 5]); maxX = Math.max(maxX, args[i + 5]); minY = Math.min(minY, args[i + 6]); maxY = Math.max(maxY, args[i + 6]); } break; } } } return { minX, minY, width: maxX - minX, height: maxY - minY }; } const bbox = getBoundingBox(absolute); console.log(`Path bounding box: x=${bbox.minX}, y=${bbox.minY}, w=${bbox.width}, h=${bbox.height}`); // Use viewBox for normalization (not path bounding box) const normalized = scaleToNormalized(absolute, minX, minY, width, height); const result = commandsToPath(normalized); console.log(`\nConverted path (${result.length} chars):\n`); console.log(result); console.log(`\n\nFor CustomGlyphDefinitions.ts:\n`); console.log(`'\\u{E0C0}': { type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: { d: '${result}', type: CustomGlyphVectorType.FILL } },`); // Write output file const ext = path.extname(filePath); const outputPath = filePath.replace(ext, `_output${ext}`); const svgOutput = ` `; fs.writeFileSync(outputPath, svgOutput, 'utf8'); console.log(`\nOutput written to: ${outputPath}`); } ================================================ FILE: bin/esbuild.mjs ================================================ /** * Copyright (c) 2023 The xterm.js authors. All rights reserved. * @license MIT */ // @ts-check import { build, context, default as esbuild } from 'esbuild'; import { argv } from 'process'; const config = { isProd: argv.includes('--prod'), isWatch: argv.includes('--watch'), isDemoClient: argv.includes('--demo-client'), isDemoServer: argv.includes('--demo-server'), isHeadless: argv.includes('--headless'), addon: argv.find(e => e.startsWith('--addon='))?.replace(/^--addon=/, ''), }; // console.info('Running with config:', JSON.stringify(config, undefined, 2)); /** @type {esbuild.BuildOptions} */ const commonOptions = { bundle: true, format: 'esm', target: 'es2021', sourcemap: true, treeShaking: true, logLevel: 'warning', }; /** @type {esbuild.BuildOptions} */ const devOptions = { minify: false, }; /** @type {esbuild.BuildOptions} */ const prodOptions = { minify: true, legalComments: 'none', // TODO: Mangling private and protected properties will reduce bundle size quite a bit, we must // make sure we don't cast privates to `any` in order to prevent regressions. //mangleProps: /_.+/, banner: { js: `/** * Copyright (c) 2014-2024 The xterm.js authors. All rights reserved. * @license MIT * * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License) * @license MIT * * Originally forked from (with the author's permission): * Fabrice Bellard's javascript vt100 for jslinux: * http://bellard.org/jslinux/ * Copyright (c) 2011 Fabrice Bellard */ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/` }, }; /** * @param {string} addon */ function getAddonEntryPoint(addon) { let result = ''; let nextCap = true; for (const char of addon) { if (char === '-') { nextCap = true; continue; } result += nextCap ? char.toUpperCase() : char nextCap = false; } result += 'Addon'; return result; } /** @type {esbuild.BuildOptions} */ let bundleConfig = { ...commonOptions, ...(config.isProd ? prodOptions : devOptions) }; /** @type {esbuild.BuildOptions} */ let outConfig = { format: 'cjs', sourcemap: true, } let skipOut = false; /** @type {esbuild.BuildOptions} */ let outTestConfig = { format: 'cjs', sourcemap: true, } let skipOutTest = false; if (config.addon) { bundleConfig = { ...bundleConfig, entryPoints: [`addons/addon-${config.addon}/src/${getAddonEntryPoint(config.addon)}.ts`], outfile: `addons/addon-${config.addon}/lib/addon-${config.addon}.mjs`, }; outConfig = { ...outConfig, entryPoints: [`addons/addon-${config.addon}/src/**/*.ts`], outdir: `addons/addon-${config.addon}/out-esbuild/` }; outTestConfig = { ...outConfig, entryPoints: [`addons/addon-${config.addon}/test/**/*.ts`], outdir: `addons/addon-${config.addon}/out-esbuild-test/` }; if (config.addon === 'ligatures') { bundleConfig.platform = 'node'; skipOutTest = true; } if (config.addon === 'serialize') { bundleConfig.tsconfig = 'addons/addon-serialize/src/tsconfig.json' } } else if (config.isDemoClient) { bundleConfig = { ...bundleConfig, sourcemap: false, entryPoints: [`demo/client/client.ts`], outfile: 'demo/dist/client-bundle.js', external: ['util', 'os', 'fs', 'path', 'stream', 'Terminal'], alias: { // Library ESM imports "@xterm/xterm": ".", "@xterm/addon-attach": "./addons/addon-attach/lib/addon-attach.mjs", "@xterm/addon-clipboard": "./addons/addon-clipboard/lib/addon-clipboard.mjs", "@xterm/addon-fit": "./addons/addon-fit/lib/addon-fit.mjs", "@xterm/addon-image": "./addons/addon-image/lib/addon-image.mjs", "@xterm/addon-progress": "./addons/addon-progress/lib/addon-progress.mjs", "@xterm/addon-search": "./addons/addon-search/lib/addon-search.mjs", "@xterm/addon-serialize": "./addons/addon-serialize/lib/addon-serialize.mjs", "@xterm/addon-web-fonts": "./addons/addon-web-fonts/lib/addon-web-fonts.mjs", "@xterm/addon-web-links": "./addons/addon-web-links/lib/addon-web-links.mjs", "@xterm/addon-webgl": "./addons/addon-webgl/lib/addon-webgl.mjs", "@xterm/addon-unicode11": "./addons/addon-unicode11/lib/addon-unicode11.mjs", "@xterm/addon-unicode-graphemes": "./addons/addon-unicode-graphemes/lib/addon-unicode-graphemes.mjs", // Non-bundled ESM imports // HACK: Ligatures imports fs which in the esbuild bundle resolves at runtime _on startup_ // instead of only when it's needed. This causes a `Dynamic require of "fs" is not // supported` exception to be thrown. So the unbundled out-esbuild sources are used // instead of the .mjs file which seems to resolve the issue. "@xterm/addon-ligatures": "./addons/addon-ligatures/out-esbuild/LigaturesAddon", } } skipOut = true; skipOutTest = true; } else if (config.isDemoServer) { bundleConfig = { ...bundleConfig, entryPoints: [`demo/server/server.ts`], outfile: 'demo/dist/server-bundle.js', format: 'cjs', platform: 'node', external: ['node-pty'], } skipOut = true; skipOutTest = true; } else if (config.isHeadless) { bundleConfig = { ...bundleConfig, entryPoints: [`src/headless/public/Terminal.ts`], outfile: `headless/lib-headless/xterm-headless.mjs` }; outConfig = { ...outConfig, entryPoints: ['src/**/*.ts'], outdir: 'out-esbuild/' }; skipOut = true; } else { bundleConfig = { ...bundleConfig, entryPoints: [`src/browser/public/Terminal.ts`], outfile: `lib/xterm.mjs` }; outConfig = { ...outConfig, entryPoints: [ 'src/browser/**/*.ts', 'src/common/**/*.ts', 'src/headless/**/*.ts' ], outdir: 'out-esbuild/' }; outTestConfig = { ...outConfig, entryPoints: ['test/**/*.ts'], outdir: 'out-esbuild-test/' }; } if (config.isWatch) { context(bundleConfig).then(e => e.watch()); if (!skipOut) { context(outConfig).then(e => e.watch()); } if (!skipOutTest) { context(outTestConfig).then(e => e.watch()); } } else { await build(bundleConfig); if (!skipOut) { await build(outConfig); } if (!skipOutTest) { await build(outTestConfig); } } ================================================ FILE: bin/esbuild_all.mjs ================================================ // @ts-check import { spawn } from "child_process"; import { readdir } from "fs/promises"; import { argv } from "process"; /** @type {{cp: import("child_process").ChildProcessByStdio, name: string}[]} */ const jobs = []; // Core job jobs.push(createJob('xterm', [])); // Addon jobs const addons = (await readdir('addons')).filter(e => e.startsWith('addon-')).map(e => e.replace('addon-', '')); for (const addon of addons) { jobs.push(createJob(`xterm-addon-${addon}`, [`--addon=${addon}`])); } // Demo job - This requires the others to be built so it's not included when building all // jobs.push(createJob('demo-client', [`--demo-client`])); await Promise.all(jobs.map((job, i) => { return new Promise(r => { job.cp.on('exit', code => { log(`Finished \x1b[32m${job.name}\x1b[0m${code ? ' \x1b[31mwith errors\x1b[0m' : ''}`); r(code); }); }); })); /** * @param {string} message */ function log(message) { console.info(`[\x1b[2m${new Date().toLocaleTimeString('en-GB')}\x1b[0m] ${message}`); } /** * @param {string} name * @param {string[]} extraArgs */ function createJob(name, extraArgs) { log(`Starting \x1b[32m${name}\x1b[0m...`); const args = [ 'bin/esbuild.mjs', ...extraArgs, ...argv ]; return { name, cp: spawn('node', args, { stdio: ["inherit", "inherit", "inherit"] }) }; } ================================================ FILE: bin/extract_vtfeatures.js ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT * * Script to extract vt features documented in docstrings. */ const fs = require('fs'); const Mustache = require('mustache'); /** * regexp to fetch all comments * Fetches all multiline comments and single lines containing '// @vt:'. */ const REX_COMMENTS = /^\s*?\/\*\*([\S\s]*?)\*\/|^\s*?\/\/ (@vt:.*?)$/mug; /** * regexp to parse the @vt line * expected data - "@vt: "" "" "" */ const REX_VT_LINE = /^@vt:\s*(\w+|#\w+|#\w+\[.*?\])\s*(\w+)\s*(\w+)\s*"(.*?)"\s*"(.*?)"\s*"(.*?)".*$/; // known vt command types const TYPES = [ 'C0', 'C1', 'ESC', 'CSI', 'DCS', 'OSC', 'APC', 'PM', 'SOS' ]; const MARKDOWN_TMPL = `--- title: Supported Terminal Sequences category: API --- {::options parse_block_html="true" /} # Supported Terminal Sequences xterm.js version: {{version}} ## Table of Contents ## General notes This document lists xterm.js' support of terminal sequences. The sequences are grouped by their sequence type: - C0: single byte command (7bit control codes, byte range \`\\x00\` .. \`\\x1F\`, \`\\x7F\`) - C1: single byte command (8bit control codes, byte range \`\\x80\` .. \`\\x9F\`) - ESC: sequence starting with \`ESC\` (\`\\x1B\`) - CSI - Control Sequence Introducer: sequence starting with \`ESC [\` (7bit) or CSI (\`\\x9B\`, 8bit) - DCS - Device Control String: sequence starting with \`ESC P\` (7bit) or DCS (\`\\x90\`, 8bit) - OSC - Operating System Command: sequence starting with \`ESC ]\` (7bit) or OSC (\`\\x9D\`, 8bit) Application Program Command (APC), Privacy Message (PM) and Start of String (SOS) are recognized but not supported, any sequence of these types will be silently ignored. They are also not hookable by the API. Note that the list only marks sequences implemented in xterm.js' core codebase as supported. Missing sequences are either not supported or unstable/experimental. Furthermore addons or integrations can provide additional custom sequences (denoted as "External" where known). To denote the sequences the tables use the same abbreviations as xterm does: - \`Ps\`: A single (usually optional) numeric parameter, composed of one or more decimal digits. - \`Pm\`: Multiple numeric parameters composed of any number of single numeric parameters, separated by ; character(s), e.g. \` Ps ; Ps ; ... \`. - \`Pt\`: A text parameter composed of printable characters. Note that for most commands with \`Pt\` only ASCII printables are specified to work. Additionally the parser will let pass any codepoint greater than C1 as printable. {{#C0.length}} ## C0 | Mnemonic | Name | Sequence | Short Description | Support | | -------- | ---- | -------- | ----------------- | ------- | {{#C0}} | {{mnemonic}} | {{name}} | \`{{sequence}}\` | {{{shortDescription}}} {{#longDescription.length}}_[more](#{{longTarget}}){: .link-details}_{{/longDescription.length}} | {{{status}}} | {{/C0}} {{#C0.hasLongDescriptions}} {{#C0}} {{#longDescription.length}}
### {{name}} {{#longDescription}} {{{.}}} {{/longDescription}}
{{/longDescription.length}} {{/C0}} {{/C0.hasLongDescriptions}} {{/C0.length}} {{#C1.length}} ## C1 | Mnemonic | Name | Sequence | Short Description | Support | | -------- | ---- | -------- | ----------------- | ------- | {{#C1}} | {{mnemonic}} | {{name}} | \`{{sequence}}\` | {{{shortDescription}}} {{#longDescription.length}}_[more](#{{longTarget}}){: .link-details}_{{/longDescription.length}} | {{{status}}} | {{/C1}} {{#C1.hasLongDescriptions}} {{#C1}} {{#longDescription.length}}
### {{name}} {{#longDescription}} {{{.}}} {{/longDescription}}
{{/longDescription.length}} {{/C1}} {{/C1.hasLongDescriptions}} {{/C1.length}} {{#CSI.length}} ## CSI | Mnemonic | Name | Sequence | Short Description | Support | | -------- | ---- | -------- | ----------------- | ------- | {{#CSI}} | {{mnemonic}} | {{name}} | \`\`{{{sequence}}}\`\` | {{{shortDescription}}} {{#longDescription.length}}_[more](#{{longTarget}}){: .link-details}_{{/longDescription.length}} | {{{status}}} | {{/CSI}} {{#CSI.hasLongDescriptions}} {{#CSI}} {{#longDescription.length}}
### {{name}} {{#longDescription}} {{{.}}} {{/longDescription}}
{{/longDescription.length}} {{/CSI}} {{/CSI.hasLongDescriptions}} {{/CSI.length}} {{#DCS.length}} ## DCS | Mnemonic | Name | Sequence | Short Description | Support | | -------- | ---- | -------- | ----------------- | ------- | {{#DCS}} | {{mnemonic}} | {{name}} | \`{{sequence}}\` | {{{shortDescription}}} {{#longDescription.length}}_[more](#{{longTarget}}){: .link-details}_{{/longDescription.length}} | {{{status}}} | {{/DCS}} {{#DCS.hasLongDescriptions}} {{#DCS}} {{#longDescription.length}}
### {{name}} {{#longDescription}} {{{.}}} {{/longDescription}}
{{/longDescription.length}} {{/DCS}} {{/DCS.hasLongDescriptions}} {{/DCS.length}} {{#ESC.length}} ## ESC | Mnemonic | Name | Sequence | Short Description | Support | | -------- | ---- | -------- | ----------------- | ------- | {{#ESC}} | {{mnemonic}} | {{name}} | \`{{sequence}}\` | {{{shortDescription}}} {{#longDescription.length}}_[more](#{{longTarget}}){: .link-details}_{{/longDescription.length}} | {{{status}}} | {{/ESC}} {{#ESC.hasLongDescriptions}} {{#ESC}} {{#longDescription.length}}
### {{name}} {{#longDescription}} {{{.}}} {{/longDescription}}
{{/longDescription.length}} {{/ESC}} {{/ESC.hasLongDescriptions}} {{/ESC.length}} {{#OSC.length}} ## OSC **Note**: Other than listed in the table, the parser recognizes both ST (ECMA-48) and BEL (xterm) as OSC sequence finalizer. | Identifier | Sequence | Short Description | Support | | ---------- | -------- | ----------------- | ------- | {{#OSC}} | {{mnemonic}} | \`{{sequence}}\` | {{{shortDescription}}} {{#longDescription.length}}_[more](#{{longTarget}}){: .link-details}_{{/longDescription.length}} | {{{status}}} | {{/OSC}} {{#OSC.hasLongDescriptions}} {{#OSC}} {{#longDescription.length}}
### {{name}} {{#longDescription}} {{{.}}} {{/longDescription}}
{{/longDescription.length}} {{/OSC}} {{/OSC.hasLongDescriptions}} {{/OSC.length}} ` // support status marcos // applied for: // - status field of single @vt line // - all lines in long description const MACRO = [ // #Y - supported [/#Y/g, s => ''], // #N - unsupported [/#N/g, s => ''], // #P[reason] - partial support with a reason as title [/#P\[(.*?)\]/g, (s, p1) => `Partial`], // #B[reason] - supported but broken in a certain way, reason in title [/#B\[(.*?)\]/g, (s, p1) => `Broken`], // #E[notes] - support via external resource, eg. addon [/#E\[(.*?)\]/g, (s, p1) => `External`] ]; function applyMacros(s) { for (let i = 0; i < MACRO.length; ++i) { s = s.replace(MACRO[i][0], MACRO[i][1]); } return s; } // function replaceStatus(s) { // if (s === 'supported') return ''; // if (s === 'unsupported') return ''; // return s; // } function createAnchorSlug(s) { return s.toLowerCase().split(' ').join('-'); } function empty(ar) { return !ar.filter(Boolean, ar).length; } function* parseMultiLineGen(filename, s) { if (!s.includes('@vt:')) { return; } const lines = s.split('\n').map(el => el.trim().replace(/\*/, '').replace(/\s/, '')); let grabLine = false; let longDescription = []; let feature = undefined; let noLineCount = 0; for (const line of lines) { if (grabLine) { if (!line) noLineCount++; if (noLineCount >= 2) { if (feature) { feature.longDescription = empty(longDescription) ? [] : longDescription; feature.longTarget = createAnchorSlug(feature.name); yield feature; } grabLine = false; longDescription = []; feature = undefined; noLineCount = 0; } else if (line.indexOf('@vt:') === 0) { if (feature) { feature.longDescription = empty(longDescription) ? [] : longDescription; feature.longTarget = createAnchorSlug(feature.name); yield feature; } grabLine = true; longDescription = []; feature = undefined; noLineCount = 0; } else { //longDescription.push(line); longDescription.push(applyMacros(line)); if (line) noLineCount = 0; } } if (line.indexOf('@vt:') === 0) { feature = parseSingleLine(filename, line); grabLine = true; } } if (grabLine && feature) { feature.longDescription = empty(longDescription) ? [] : longDescription; feature.longTarget = createAnchorSlug(feature.name); yield feature; } } function parseSingleLine(filename, s) { const line = s.trim(); const match = line.match(REX_VT_LINE); if (match !== null) { if (!TYPES.includes(match[2])) { throw new Error(`unkown vt-command type "${match[2]}" specified in "${filename}"`); } return { status: match[1], type: match[2], mnemonic: match[3], name: match[4], sequence: match[5], shortDescription: match[6], longDescription: [], longTarget: '', source: filename }; } } function getSorter(entry) { switch (entry) { case 'C0': case 'C1': // NOTE: expects hex value notation at last position in sequence return (a, b) => parseInt(a.sequence.slice(-2), 16) - parseInt(b.sequence.slice(-2), 16); case 'OSC': // NOTE: expects the decimal function identifier in mnemonic return (a, b) => parseInt(a.mnemonic) - parseInt(b.mnemonic); case 'DCS': return (a, b) => a.mnemonic > b.mnemonic; case 'CSI': case 'ESC': // default sort order by final byte return (a, b) => { // ugly hack to fix sorting of workaround in HPA sequence "CSI Ps ` " // for HPA compare with length - 2 instead const HPA = 'CSI Ps ` '; const aa = a.sequence === HPA ? a.sequence.slice(0, -1) : a.sequence; const bb = b.sequence === HPA ? b.sequence.slice(0, -1) : b.sequence; return aa.charCodeAt(aa.length - 1) - bb.charCodeAt(bb.length - 1); }; default: return (a, b) => a.sequence > b.sequence; } }; function postProcessData(features) { const featureTable = {}; for (const feature of features) { // feature.status = replaceStatus(feature.status); feature.status = applyMacros(feature.status); if (featureTable[feature.type] === undefined) { featureTable[feature.type] = []; } featureTable[feature.type].push(feature); if (feature.longDescription.length) { featureTable[feature.type].hasLongDescriptions = true; } } for (const entry in featureTable) { featureTable[entry].sort(getSorter(entry)); } // console.error(featureTable); featureTable.version = require('../package.json').version; console.log(Mustache.render(MARKDOWN_TMPL, featureTable)); } function main(filenames) { // console.error(filenames); let leftToProcess = filenames.length; const features = []; for (const filename of filenames) { fs.readFile(filename, 'utf-8', (err, data) => { let match; while ((match = REX_COMMENTS.exec(data)) !== null) { if (match.index === REX_COMMENTS.lastIndex) { REX_COMMENTS.lastIndex++; } if (match[1]) { for (let feature of parseMultiLineGen(filename, match[1])) { if (feature) features.push(feature); } } else { const feature = parseSingleLine(filename, match[2]); if (feature) features.push(feature); } } leftToProcess--; if (!leftToProcess) { postProcessData(features); } }); } } main(process.argv.slice(2)) ================================================ FILE: bin/lint_changes.js ================================================ /** * Copyright (c) 2026 The xterm.js authors. All rights reserved. * @license MIT */ // @ts-check const { execSync, spawn } = require('child_process'); const path = require('path'); const extensions = ['.ts', '.mts']; const fix = process.argv.includes('--fix'); // Get uncommitted changed files (staged + unstaged) function getChangedFiles() { try { const output = execSync('git diff --name-only --diff-filter=ACMR HEAD', { encoding: 'utf-8', cwd: path.join(__dirname, '..') }); return output.split('\n').filter(f => f && extensions.some(ext => f.endsWith(ext))); } catch { return []; } } const files = getChangedFiles(); if (files.length === 0) { console.log('No changed TypeScript files to lint.'); process.exit(0); } console.log(`Linting ${files.length} changed file(s)...`); const eslintArgs = ['--max-warnings', '0', '--no-warn-ignored']; if (fix) { eslintArgs.push('--fix'); } eslintArgs.push(...files); const eslint = process.platform === 'win32' ? 'eslint.cmd' : 'eslint'; const child = spawn(eslint, eslintArgs, { stdio: 'inherit', cwd: path.join(__dirname, '..'), shell: process.platform === 'win32' }); child.on('close', code => process.exit(code ?? 0)); ================================================ FILE: bin/package_headless.js ================================================ /** * Copyright (c) 2021 The xterm.js authors. All rights reserved. * @license MIT */ const { exec } = require('child_process'); const fs = require('fs'); const { join } = require('path'); const repoRoot = join(__dirname, '..'); const headlessRoot = join(repoRoot, 'headless'); console.log('> headless/package.json'); const xtermPackageJson = require('../package.json'); const xtermHeadlessPackageJson = { ...xtermPackageJson, name: '@xterm/headless', description: 'A headless terminal component that runs in Node.js', main: 'lib-headless/xterm-headless.js', types: 'typings/xterm-headless.d.ts', }; delete xtermHeadlessPackageJson['scripts']; delete xtermHeadlessPackageJson['devDependencies']; delete xtermHeadlessPackageJson['style']; fs.writeFileSync(join(headlessRoot, 'package.json'), JSON.stringify(xtermHeadlessPackageJson, null, 1)); console.log(fs.readFileSync(join(headlessRoot, 'package.json')).toString()); console.log('> headless/typings/'); mkdirF(join(headlessRoot, 'typings')); fs.copyFileSync( join(repoRoot, 'typings/xterm-headless.d.ts'), join(headlessRoot, 'typings/xterm-headless.d.ts') ); console.log('> headless/logo-full.png'); fs.copyFileSync( join(repoRoot, 'images/logo-full.png'), join(headlessRoot, 'logo-full.png') ); function mkdirF(p) { if (!fs.existsSync(p)) { fs.mkdirSync(p); } } console.log('> Publish dry run'); exec('npm publish --dry-run', { cwd: headlessRoot }, (error, stdout, stderr) => { if (error) { console.log(`error: ${error.message}`); return; } if (stderr) { console.error(`stderr:\n${stderr}`); } console.log(`stdout:\n${stdout}`); }); ================================================ FILE: bin/publish.js ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ const cp = require('child_process'); const fs = require('fs'); const os = require('os'); const path = require('path'); // Setup auth if (process.env['NPM_AUTH_TOKEN']) { fs.writeFileSync(`${process.env['HOME']}/.npmrc`, `//registry.npmjs.org/:_authToken=${process.env['NPM_AUTH_TOKEN']}`); } log('Configuration:', 'green') const isDryRun = process.argv.includes('--dry'); if (isDryRun) { log(' Publish dry run'); } const allAddons = process.argv.includes('--all-addons'); if (allAddons) { log(' Publish all addons'); } const repoCommit = getRepoCommit(); const changedFiles = getChangedFilesInCommit('HEAD'); // Always publish xterm, technically this isn't needed if // `changedFiles.some(e => e.search(/^addons\//)`, but it's here for convenience to get the right // peer dependencies for addons. const result = checkAndPublishPackage(path.resolve(__dirname, '..'), repoCommit, undefined, true); const isStableRelease = result.isStableRelease; const peerDependencies = result.nextVersion.includes('beta') ? { '@xterm/xterm': `^${result.nextVersion}`, } : undefined; checkAndPublishPackage(path.resolve(__dirname, '../headless'), repoCommit); // Addon peer dependencies // Publish addons if any files were changed inside of the addon const addonPackageDirs = [ path.resolve(__dirname, '../addons/addon-attach'), path.resolve(__dirname, '../addons/addon-clipboard'), path.resolve(__dirname, '../addons/addon-fit'), path.resolve(__dirname, '../addons/addon-image'), path.resolve(__dirname, '../addons/addon-ligatures'), path.resolve(__dirname, '../addons/addon-progress'), path.resolve(__dirname, '../addons/addon-search'), path.resolve(__dirname, '../addons/addon-serialize'), path.resolve(__dirname, '../addons/addon-unicode11'), path.resolve(__dirname, '../addons/addon-unicode-graphemes'), path.resolve(__dirname, '../addons/addon-web-fonts'), path.resolve(__dirname, '../addons/addon-web-links'), path.resolve(__dirname, '../addons/addon-webgl') ]; log(`Checking if addons need to be published`, 'green'); for (const p of addonPackageDirs) { const addon = path.basename(p); if (allAddons || changedFiles.some(e => e.includes(addon))) { checkAndPublishPackage(p, repoCommit, peerDependencies); } } // Publish website if it's a stable release if (isStableRelease) { updateWebsite(); } function checkAndPublishPackage(packageDir, repoCommit, peerDependencies, updateVersionTs = false) { const packageJson = require(path.join(packageDir, 'package.json')); // Determine if this is a stable or beta release const publishedVersions = getPublishedVersions(packageJson); const isStableRelease = !publishedVersions.includes(packageJson.version); // Get the next version let nextVersion = isStableRelease ? packageJson.version : getNextBetaVersion(packageJson); log(`Publishing version: ${nextVersion}`, 'green'); // Update Version.ts with the new version if (updateVersionTs) { const versionTsPath = path.join(packageDir, 'src/common/Version.ts'); const versionTsContent = fs.readFileSync(versionTsPath, 'utf8'); const updatedVersionTs = versionTsContent.replace( /export const XTERM_VERSION = '[^']+';/, `export const XTERM_VERSION = '${nextVersion}';` ); fs.writeFileSync(versionTsPath, updatedVersionTs); log(`Updated ${versionTsPath} to version ${nextVersion}`); } // Set the version in package.json const packageJsonFile = path.join(packageDir, 'package.json'); packageJson.version = nextVersion; // Set the commit in package.json if (repoCommit) { packageJson.commit = repoCommit; log(`Set commit of ${packageJsonFile} to ${repoCommit}`); } else { throw new Error(`No commit set`); } // Set peer dependencies if (peerDependencies) { packageJson.peerDependencies = peerDependencies; log(`Set peerDependencies of ${packageJsonFile} to ${JSON.stringify(peerDependencies)}`); } else { log(`Skipping peerDependencies`); } // Write new package.json fs.writeFileSync(packageJsonFile, JSON.stringify(packageJson, null, 2)); // Publish const args = ['publish', '--access', 'public']; if (!isStableRelease) { args.push('--tag', 'beta'); } if (isDryRun) { args.push('--dry-run'); } log(`Spawn: npm ${args.join(' ')}`); const result = cp.spawnSync('npm', args, { cwd: packageDir, stdio: 'inherit' }); if (result.status) { throw new Error(`Spawn exited with code ${result.status}`); } return { isStableRelease, nextVersion }; } function getRepoCommit() { const commitProcess = cp.spawnSync('git', ['log', '-1', '--format="%H"']); if (commitProcess.stdout.length === 0 && commitProcess.stderr) { const err = versionsProcess.stderr.toString(); throw new Error('Could not get repo commit\n' + err); } const output = commitProcess.stdout.toString().trim(); return output.replace(/^"/, '').replace(/"$/, ''); } function getNextBetaVersion(packageJson) { if (!/^\d+\.\d+\.\d+$/.exec(packageJson.version)) { throw new Error('The package.json version must be of the form x.y.z'); } const tag = 'beta'; const stableVersion = packageJson.version.split('.'); const nextStableVersion = `${stableVersion[0]}.${parseInt(stableVersion[1]) + 1}.0`; const publishedVersions = getPublishedVersions(packageJson, nextStableVersion, tag); if (publishedVersions.length === 0) { return `${nextStableVersion}-${tag}.1`; } const latestPublishedVersion = publishedVersions.sort((a, b) => { const aVersion = parseInt(a.slice(a.search(/\d+$/))); const bVersion = parseInt(b.slice(b.search(/\d+$/))); return aVersion > bVersion ? -1 : 1; })[0]; const latestTagVersion = parseInt(latestPublishedVersion.slice(latestPublishedVersion.search(/\d+$/)), 10); return `${nextStableVersion}-${tag}.${latestTagVersion + 1}`; } function asArray(value) { return Array.isArray(value) ? value : [value]; } function getPublishedVersions(packageJson, version, tag) { const versionsProcess = cp.spawnSync( os.platform === 'win32' ? 'npm.cmd' : 'npm', ['view', packageJson.name, 'versions', '--json'], { shell: true } ); if (versionsProcess.stdout.length === 0 && versionsProcess.stderr) { const err = versionsProcess.stderr.toString(); if (err.indexOf('404 Not Found - GET https://registry.npmjs.org/@xterm') > 0) { return []; } throw new Error('Could not get published versions\n' + err); } const output = JSON.parse(versionsProcess.stdout); if (typeof output === 'object' && !Array.isArray(output)) { if (output.error?.code === 'E404') { return []; } throw new Error('Could not get published versions\n' + output); } if (!output || Array.isArray(output) && output.length === 0) { return []; } const versionsJson = asArray(output); if (tag) { return versionsJson.filter(v => !v.search(new RegExp(`${version}-${tag}.[0-9]+`))); } return versionsJson; } function getChangedFilesInCommit(commit) { const args = ['log', '-m', '-1', '--name-only', `--pretty=format:`, commit]; const result = cp.spawnSync('git', args); const output = result.stdout.toString(); const changedFiles = output.split('\n').filter(e => e.length > 0); return changedFiles; } function updateWebsite() { log('Updating website', 'green'); const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'website-')); const packageJson = require(path.join(path.resolve(__dirname, '..'), 'package.json')); if (!isDryRun) { cp.spawnSync('sh', [path.join(__dirname, 'update-website.sh'), packageJson.version], { cwd, stdio: [process.stdin, process.stdout, process.stderr] }); } } /** * @param {string} message */ function log(message, color) { let colorSequence = ''; switch (color) { case 'green': { colorSequence = '\x1b[32m'; break; } } console.info([ `[\x1b[2m${new Date().toLocaleTimeString('en-GB')}\x1b[0m] `, colorSequence, message, '\x1b[0m', ].join('')); } ================================================ FILE: bin/test_integration.js ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT */ // @ts-check const cp = require('child_process'); const path = require('path'); let argv = process.argv.slice(2); let suiteFilter = undefined; while (argv.some(e => e.startsWith('--suite='))) { const i = argv.findIndex(e => e.startsWith('--suite=')); const match = argv[i].match(/--suite=(?.+)/) suiteFilter = match?.groups?.suitename ?? undefined; argv.splice(i, 1); } let configs = [ { name: 'core', path: 'out-esbuild-test/playwright/playwright.config.js' } ]; const addons = [ 'attach', 'clipboard', 'fit', 'image', 'progress', 'search', 'serialize', 'unicode-graphemes', 'unicode11', 'web-fonts', 'web-links', 'webgl', ]; for (const addon of addons) { configs.push({ name: `addon-${addon}`, path: `addons/addon-${addon}/out-esbuild-test/playwright.config.js` }); } if (suiteFilter) { configs = configs.filter(e => e.name === suiteFilter); } function npmBinScript(script) { return path.resolve(__dirname, `../node_modules/.bin/` + (process.platform === 'win32' ? `${script}.cmd` : script)); } async function run() { for (const config of configs) { const command = npmBinScript('playwright'); const args = ['test', '-c', config.path, ...argv]; console.log(`Running suite \x1b[1;34m${config.name}...\x1b[0m`); console.log(`\n\x1b[32m${command}\x1b[0m`, args); const run = cp.spawnSync(command, args, { cwd: path.resolve(__dirname, '..'), shell: true, stdio: 'inherit' } ); if (run.error) { console.error(run.error); process.exit(run.status ?? -1); } process.exit(run.status); } } run(); ================================================ FILE: bin/test_mousemodes.js ================================================ /** * Copyright (c) 2019 The xterm.js authors. All rights reserved. * @license MIT * * Script to test different mouse modes in terminal emulators. * Tests for protocols DECSET 9, 1000, 1002, 1003 with different * report encodings (default, UTF8, SGR, URXVT, SGR-pixels). * * VT200 Highlight mode (DECSET 1001) is not implemented. * * The test basically applies the report data to the cursor, thus * a mouse report should move the cursor to the cell under the mouse. * Furthermore the reports are printed in the left lower corner as * raw data and their meaning. * * A failing test might show: * - wrong coords: the cursor will jump to some other place * - wrong buttons: see meaning output and check whether it makes sense * - faulty reports: inspect the raw data and compare with other emulators * - missing events: compare with spec / other emulators */ let activeProtocol = 0; let activeEnc = 0; const stdin = process.openStdin(); process.stdin.setRawMode(true); // close handler - reset terminal on exit stdin.addListener('data', function(data) { if (data[0] === 0x03) { process.stdin.setRawMode(false); process.stdout.write('\x1bc'); process.exit(); } if (data[0] === 0x01) { switchActiveProtocol(); printMenu(); } if (data[0] === 0x02) { switchActiveEnc(); printMenu(); } console.log('\x1b[100;H\x1b[2A\x1b[2KReport:', data, [data.toString('binary')]); // filter mouse reports if (data[0] === 0x1b && data[1] === '['.charCodeAt(0)) { applyReportData(data); } }); // button definitions const buttons = { '': -1, left: 0, middle: 1, right: 2, released: 3, wheelUp: 4, wheelDown: 5, wheelLeft: 6, wheelRight: 7, aux1: 8, aux2: 9, aux3: 10, aux4: 11, aux5: 12, aux6: 13, aux7: 14, aux8: 15 }; const reverseButtons = {}; for (const el in buttons) { reverseButtons[buttons[el]] = el; } // extract button data from buttonCode function evalButtonCode(code) { // more than 15 buttons are not supported if (code > 255) { return {button: 'invalid', action: 'invalid', modifier: {}}; } const modifier = {shift: !!(code & 4), meta: !!(code & 8), control: !!(code & 16)}; const move = code & 32; let button = code & 3; if (code & 128) { button |= 8; } if (code & 64) { button |= 4 } let action = 'press'; let buttonS = reverseButtons[button]; if (button === 3) { buttonS = ''; action = 'release'; } if (move) { action = 'move'; } else if (4 <= button && button <= 7) { buttonS = 'wheel'; action = button === 4 ? 'up' : button === 5 ? 'down' : button === 6 ? 'left' : 'right'; } return {button: buttonS, action, modifier}; } // protocols const PROTOCOLS = { '9 (X10: press only)': '\x1b[?9h', '1000 (VT200: press, release, wheel)': '\x1b[?1000h', // '1001 (VT200 highlight)': '\x1b[?1001h', // handle of backreport - not implemented '1002 (press, release, move on pressed, wheel)': '\x1b[?1002h', '1003 (press, relase, move, wheel)': '\x1b[?1003h' } // encodings: ENCODING_NAME => [sequence, parse_report] const ENC = { 'DEFAULT' : [ '', // format: CSI M