diff --git a/packages/preview/ansi-render/0.9.2/CHANGELOG.md b/packages/preview/ansi-render/0.9.2/CHANGELOG.md new file mode 100644 index 0000000000..dc312835c2 --- /dev/null +++ b/packages/preview/ansi-render/0.9.2/CHANGELOG.md @@ -0,0 +1,113 @@ +# Changelog + +## [0.9.2] - 2026-07-18 + +* Same behavior as `0` for empty control code. + +## [0.9.1] - 2026-05-11 + +### Fixed + +* Fixed `bold-is-bright` option rendering logic +* Removed workaround for under/overline with trailing whitespace + +## [0.9.0] - 2026-05-06 + +### Added + +* Added support for `CSI 22` + +## [0.8.0] - 2024-12-29 + +### Added + +* Added `rose-pine` theme + +## [0.7.0] - 2024-12-12 + +### Changed + +* Changed default entry point to `lib.typ` +* Added package details + +## [0.6.1] - 2023-12-26 + +### Changed + +* Changed default foreground color name to `default-fg` +* Slightly reduce pdf size by removing default box fill + +## [0.6.0] - 2023-12-06 + +### Fixed + +* Removed workaround for a bug in `raw` that fixed in Typst 0.10.0 + +## [0.5.1] - 2023-10-21 + +### Fixed + +* Fixed height with empty newline + +## [0.5.0] - 2023-09-29 + +### Added + +* Added `bold-is-bright` option #2 + +### Changed + +* Allow setting font to none #3 +* Changed default font size to `1em` +* Use `raw` to render content now + +### Fixed + +* Fixed 8-bit colors 8-15 use the wrong colors #4 + +## [0.4.2] - 2023-09-24 + +### Added + +* Added gruvbox themes #1 + +## [0.4.1] - 2023-09-22 + +### Changed + +* Changed default font size to `9pt` +* Prevent `set` affects box layout from outside of the function + +## [0.4.0] - 2023-09-13 + +### Added + +* Added most options from [`block`]([https://](https://typst.app/docs/reference/layout/block/)) function with the same names and default values +* Added `vscode-light` theme + +### Changed + +* Changed outmost layout from `rect` to `block` +* Changed default theme to `vscode-light` + +## [0.3.0] - 2023-09-09 + +### Added + +* Added `radius` option, default is `3pt` + +### Changed + +* Changed default font size to `10pt` +* Changed default font to `Cascadia Code` +* Changed default theme to `solarized-light` + +## [0.2.0] 2023-08-05 + +### Changed + +* Changed coding style to kebab-case and two spaces + +## [0.1.0] 2023-07-02 + +first release diff --git a/packages/preview/ansi-render/0.9.2/LICENSE b/packages/preview/ansi-render/0.9.2/LICENSE new file mode 100644 index 0000000000..a05c753ced --- /dev/null +++ b/packages/preview/ansi-render/0.9.2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 8LWXpg + +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. diff --git a/packages/preview/ansi-render/0.9.2/README.md b/packages/preview/ansi-render/0.9.2/README.md new file mode 100644 index 0000000000..4064259bd8 --- /dev/null +++ b/packages/preview/ansi-render/0.9.2/README.md @@ -0,0 +1,133 @@ +# ANSI Escape Sequence Renderer + +[![Tagged version](https://img.shields.io/github/v/tag/8LWXpg/typst-ansi-render)](https://github.com/8LWXpg/typst-ansi-render/tags) +[![GitHub Repo stars](https://img.shields.io/github/stars/8LWXpg/typst-ansi-render?style=flat)](https://github.com/8LWXpg/typst-ansi-render) +[![License on GitHub](https://img.shields.io/github/license/8LWXpg/typst-ansi-render)](https://github.com/8LWXpg/typst-ansi-render/blob/v0.9.2/LICENSE) +[![typst package](https://img.shields.io/badge/typst-package-239dad)](https://typst.app/universe/package/ansi-render) + +This script provides a simple way to render text with ANSI escape sequences. Package `ansi-render` provides a function `ansi-render`, and a dictionary of themes `terminal-themes`. + +## Usage + +```typst +#import "@preview/ansi-render:0.9.2": * + +#ansi-render( + string, + font: string or none, + size: length, + width: auto or relative length, + height: auto or relative length, + breakable: boolean, + radius: relative length or dictionary, + inset: relative length or dictionary, + outset: relative length or dictionary, + spacing: relative length or fraction, + above: relative length or fraction, + below: relative length or fraction, + clip: boolean, + bold-is-bright: boolean, + theme: terminal-themes.theme, +) +``` + +### Parameters + +- `string` - string with ANSI escape sequences +- `font` - font name or none, default is `Cascadia Code`, set to `none` to use the same font as `raw` +- `size` - font size, default is `1em` +- `bold-is-bright` - boolean, whether bold text is rendered with bright colors, default is `false` +- `theme` - theme, default is `vscode-light` +- parameters from [`block`](https://typst.app/docs/reference/layout/block/) function with the same default value, only affects outmost block layout: + - `width` + - `height` + - `breakable` + - `radius` + - `inset` + - `outset` + - `spacing` + - `above` + - `below` + - `clip` + +## Themes + +see [themes](https://github.com/8LWXpg/typst-ansi-render/blob/v0.9.2/test/themes.pdf) + +## Demo + +see [demo.typ](https://github.com/8LWXpg/typst-ansi-render/blob/v0.9.2/test/demo.typ) [demo.pdf](https://github.com/8LWXpg/typst-ansi-render/blob/v0.9.2/test/demo.pdf) + +```typst +#ansi-render( +"\u{1b}[38;2;255;0;0mThis text is red.\u{1b}[0m +\u{1b}[48;2;0;255;0mThis background is green.\u{1b}[0m +\u{1b}[38;2;255;255;255m\u{1b}[48;2;0;0;255mThis text is white on a blue background.\u{1b}[0m +\u{1b}[1mThis text is bold.\u{1b}[0m +\u{1b}[4mThis text is underlined.\u{1b}[0m +\u{1b}[38;2;255;165;0m\u{1b}[48;2;255;255;0mThis text is orange on a yellow background.\u{1b}[0m", +inset: 5pt, radius: 3pt, +theme: terminal-themes.vscode +) +``` + +![RGB color with bold and underlined text](https://github.com/8LWXpg/typst-ansi-render/blob/v0.9.2/img/1.png?raw=true) + +```typst +#ansi-render( +"\u{1b}[38;5;196mRed text\u{1b}[0m +\u{1b}[48;5;27mBlue background\u{1b}[0m +\u{1b}[38;5;226;48;5;18mYellow text on blue background\u{1b}[0m +\u{1b}[7mInverted text\u{1b}[0m +\u{1b}[38;5;208;48;5;237mOrange text on gray background\u{1b}[0m +\u{1b}[38;5;39;48;5;208mBlue text on orange background\u{1b}[0m +\u{1b}[38;5;255;48;5;0mWhite text on black background\u{1b}[0m", +inset: 5pt, radius: 3pt, +theme: terminal-themes.vscode +) +``` + +![256 color](https://github.com/8LWXpg/typst-ansi-render/blob/v0.9.2/img/2.png?raw=true) + +```typst +#ansi-render( +"\u{1b}[31;1mHello \u{1b}[7mWorld\u{1b}[0m + +\u{1b}[53;4;36mOver and \u{1b}[35m Under! +\u{1b}[7;90mreverse\u{1b}[101m and \u{1b}[94;27mreverse", +inset: 5pt, radius: 3pt, +theme: terminal-themes.vscode +) +``` + +![16 color with overline and underline and reverse](https://github.com/8LWXpg/typst-ansi-render/blob/v0.9.2/img/3.png?raw=true) + +```typst +// uses the font that supports ligatures +#ansi-render(read("./test/test.txt"), inset: 5pt, radius: 3pt, font: "Cascadia Code", theme: terminal-themes.putty) +``` + +![render file output](https://github.com/8LWXpg/typst-ansi-render/blob/v0.9.2/img/4.png?raw=true) + +## Capturing ANSI Output + +### Output to File + +The most straight forward way is writing to a file then read that in Typst. + +```bash +command > out.txt +``` + +### Other Method + +If writing to a file doesn't work, that means the program will detect the output and trim ANSI sequence accordingly, which means we need a pty interface to execute the script, the following should work in Linux: + +```bash +script "command" out.txt +exit +``` + +## About Default Font + +Typst's default font for `raw` is `Dejavu Sans Mono`, but it has [incorrect top-edge](https://github.com/typst/typst/issues/2231), so the default is set to `Cascadia Code`, which is included in web editor. diff --git a/packages/preview/ansi-render/0.9.2/lib.typ b/packages/preview/ansi-render/0.9.2/lib.typ new file mode 100644 index 0000000000..b118957628 --- /dev/null +++ b/packages/preview/ansi-render/0.9.2/lib.typ @@ -0,0 +1,625 @@ + +// add your theme here! +#let terminal-themes = ( + vscode: ( + black: rgb("#000000"), + red: rgb("#CD3131"), + green: rgb("#0DBC79"), + yellow: rgb("#E5E510"), + blue: rgb("#2472C8"), + magenta: rgb("#BC3FBC"), + cyan: rgb("#11A8CD"), + white: rgb("#E5E5E5"), + gray: rgb("#666666"), + bright-red: rgb("#D64C4C"), + bright-green: rgb("#23D18B"), + bright-yellow: rgb("#F5F543"), + bright-blue: rgb("#3B8EEA"), + bright-magenta: rgb("#D670D6"), + bright-cyan: rgb("#29B8DB"), + bright-white: rgb("#E5E5E5"), + default-fg: rgb("#E5E5E5"), // white + default-bg: rgb("#000000"), // black + ), + vscode-light: ( + black: rgb("#F8F8F8"), + red: rgb("#CD3131"), + green: rgb("#00BC00"), + yellow: rgb("#949800"), + blue: rgb("#0451A5"), + magenta: rgb("#BC05BC"), + cyan: rgb("#0598BC"), + white: rgb("#555555"), + gray: rgb("#666666"), + bright-red: rgb("#CD3131"), + bright-green: rgb("#14CE14"), + bright-yellow: rgb("#B5BA00"), + bright-blue: rgb("#0451A5"), + bright-magenta: rgb("#BC05BC"), + bright-cyan: rgb("#0598BC"), + bright-white: rgb("#A5A5A5"), + default-fg: rgb("#A5A5A5"), // white + default-bg: rgb("#F8F8F8"), // black + ), + putty: ( + black: rgb("#000000"), + red: rgb("#BB0000"), + green: rgb("#00BB00"), + yellow: rgb("#BBBB00"), + blue: rgb("#0000BB"), + magenta: rgb("#BB00BB"), + cyan: rgb("#00BBBB"), + white: rgb("#BBBBBB"), + gray: rgb("#555555"), + bright-red: rgb("#FF0000"), + bright-green: rgb("#00FF00"), + bright-yellow: rgb("#FFFF00"), + bright-blue: rgb("#0000FF"), + bright-magenta: rgb("#FF00FF"), + bright-cyan: rgb("#00FFFF"), + bright-white: rgb("#FFFFFF"), + default-fg: rgb("#BBBBBB"), // white + default-bg: rgb("#000000"), // black + ), + campbell: ( + black: rgb("#0C0C0C"), + red: rgb("#C50F1F"), + green: rgb("#13A10E"), + yellow: rgb("#C19C00"), + blue: rgb("#0037DA"), + magenta: rgb("#881798"), + cyan: rgb("#3A96DD"), + white: rgb("#CCCCCC"), + gray: rgb("#767676"), + bright-red: rgb("#E74856"), + bright-green: rgb("#16C60C"), + bright-yellow: rgb("#F9F1A5"), + bright-blue: rgb("#3B78FF"), + bright-magenta: rgb("#B4009E"), + bright-cyan: rgb("#61D6D6"), + bright-white: rgb("#F2F2F2"), + default-fg: rgb("#CCCCCC"), + default-bg: rgb("#0C0C0C"), + ), + campbell-powershell: ( + black: rgb("#0C0C0C"), + red: rgb("#C50F1F"), + green: rgb("#13A10E"), + yellow: rgb("#C19C00"), + blue: rgb("#0037DA"), + magenta: rgb("#881798"), + cyan: rgb("#3A96DD"), + white: rgb("#CCCCCC"), + gray: rgb("#767676"), + bright-red: rgb("#E74856"), + bright-green: rgb("#16C60C"), + bright-yellow: rgb("#F9F1A5"), + bright-blue: rgb("#3B78FF"), + bright-magenta: rgb("#B4009E"), + bright-cyan: rgb("#61D6D6"), + bright-white: rgb("#F2F2F2"), + default-fg: rgb("#CCCCCC"), + default-bg: rgb("#012456"), + ), + vintage: ( + black: rgb("#000000"), + red: rgb("#800000"), + green: rgb("#008000"), + yellow: rgb("#808000"), + blue: rgb("#000080"), + magenta: rgb("#800080"), + cyan: rgb("#008080"), + white: rgb("#C0C0C0"), + gray: rgb("#808080"), + bright-red: rgb("#FF0000"), + bright-green: rgb("#00FF00"), + bright-yellow: rgb("#FFFF00"), + bright-blue: rgb("#0000FF"), + bright-magenta: rgb("#FF00FF"), + bright-cyan: rgb("#00FFFF"), + bright-white: rgb("#FFFFFF"), + default-fg: rgb("#C0C0C0"), + default-bg: rgb("#000000"), + ), + one-half-dark: ( + black: rgb("#282C34"), + red: rgb("#E06C75"), + green: rgb("#98C379"), + yellow: rgb("#E5C07B"), + blue: rgb("#61AFEF"), + magenta: rgb("#C678DD"), + cyan: rgb("#56B6C2"), + white: rgb("#DCDFE4"), + gray: rgb("#5A6374"), + bright-red: rgb("#E06C75"), + bright-green: rgb("#98C379"), + bright-yellow: rgb("#E5C07B"), + bright-blue: rgb("#61AFEF"), + bright-magenta: rgb("#C678DD"), + bright-cyan: rgb("#56B6C2"), + bright-white: rgb("#DCDFE4"), + default-fg: rgb("#DCDFE4"), + default-bg: rgb("#282C34"), + ), + one-half-light: ( + black: rgb("#383A42"), + red: rgb("#E45649"), + green: rgb("#50A14F"), + yellow: rgb("#C18301"), + blue: rgb("#0184BC"), + magenta: rgb("#A626A4"), + cyan: rgb("#0997B3"), + white: rgb("#FAFAFA"), + gray: rgb("#4F525D"), + bright-red: rgb("#DF6C75"), + bright-green: rgb("#98C379"), + bright-yellow: rgb("#E4C07A"), + bright-blue: rgb("#61AFEF"), + bright-magenta: rgb("#C577DD"), + bright-cyan: rgb("#56B5C1"), + bright-white: rgb("#FFFFFF"), + default-fg: rgb("#383A42"), + default-bg: rgb("#FAFAFA"), + ), + solarized-dark: ( + black: rgb("#002B36"), + red: rgb("#DC322F"), + green: rgb("#859900"), + yellow: rgb("#B58900"), + blue: rgb("#268BD2"), + magenta: rgb("#D33682"), + cyan: rgb("#2AA198"), + white: rgb("#EEE8D5"), + gray: rgb("#073642"), + bright-red: rgb("#CB4B16"), + bright-green: rgb("#586E75"), + bright-yellow: rgb("#657B83"), + bright-blue: rgb("#839496"), + bright-magenta: rgb("#6C71C4"), + bright-cyan: rgb("#93A1A1"), + bright-white: rgb("#FDF6E3"), + default-fg: rgb("#839496"), + default-bg: rgb("#002B36"), + ), + solarized-light: ( + black: rgb("#002B36"), + red: rgb("#DC322F"), + green: rgb("#859900"), + yellow: rgb("#B58900"), + blue: rgb("#268BD2"), + magenta: rgb("#D33682"), + cyan: rgb("#2AA198"), + white: rgb("#EEE8D5"), + gray: rgb("#073642"), + bright-red: rgb("#CB4B16"), + bright-green: rgb("#586E75"), + bright-yellow: rgb("#657B83"), + bright-blue: rgb("#839496"), + bright-magenta: rgb("#6C71C4"), + bright-cyan: rgb("#93A1A1"), + bright-white: rgb("#FDF6E3"), + default-fg: rgb("#657B83"), + default-bg: rgb("#FDF6E3"), + ), + tango-dark: ( + black: rgb("#000000"), + red: rgb("#CC0000"), + green: rgb("#4E9A06"), + yellow: rgb("#C4A000"), + blue: rgb("#3465A4"), + magenta: rgb("#75507B"), + cyan: rgb("#06989A"), + white: rgb("#D3D7CF"), + gray: rgb("#555753"), + bright-red: rgb("#EF2929"), + bright-green: rgb("#8AE234"), + bright-yellow: rgb("#FCE94F"), + bright-blue: rgb("#729FCF"), + bright-magenta: rgb("#AD7FA8"), + bright-cyan: rgb("#34E2E2"), + bright-white: rgb("#EEEEEC"), + default-fg: rgb("#D3D7CF"), + default-bg: rgb("#000000"), + ), + tango-light: ( + black: rgb("#000000"), + red: rgb("#CC0000"), + green: rgb("#4E9A06"), + yellow: rgb("#C4A000"), + blue: rgb("#3465A4"), + magenta: rgb("#75507B"), + cyan: rgb("#06989A"), + white: rgb("#D3D7CF"), + gray: rgb("#555753"), + bright-red: rgb("#EF2929"), + bright-green: rgb("#8AE234"), + bright-yellow: rgb("#FCE94F"), + bright-blue: rgb("#729FCF"), + bright-magenta: rgb("#AD7FA8"), + bright-cyan: rgb("#34E2E2"), + bright-white: rgb("#EEEEEC"), + default-fg: rgb("#555753"), + default-bg: rgb("#FFFFFF"), + ), + gruvbox-dark: ( + black: rgb("#282828"), + red: rgb("#cc241d"), + green: rgb("#98971a"), + yellow: rgb("#d79921"), + blue: rgb("#458588"), + magenta: rgb("#b16286"), + cyan: rgb("#689d6a"), + white: rgb("#ebdbb2"), + gray: rgb("#928374"), + bright-red: rgb("#fb4934"), + bright-green: rgb("#b8bb26"), + bright-yellow: rgb("#fabd2f"), + bright-blue: rgb("#83a598"), + bright-magenta: rgb("#d3869b"), + bright-cyan: rgb("#8ec07c"), + bright-white: rgb("#ebdbb2"), + default-fg: rgb("#ebdbb2"), + default-bg: rgb("#282828"), + ), + gruvbox-light: ( + black: rgb("#3c3836"), + red: rgb("#cc241d"), + green: rgb("#98971a"), + yellow: rgb("#d79921"), + blue: rgb("#458588"), + magenta: rgb("#b16286"), + cyan: rgb("#689d6a"), + white: rgb("#fbf1c7"), + gray: rgb("#7c6f64"), + bright-red: rgb("#9d0006"), + bright-green: rgb("#79740e"), + bright-yellow: rgb("#b57614"), + bright-blue: rgb("#076678"), + bright-magenta: rgb("#8f3f71"), + bright-cyan: rgb("#427b58"), + bright-white: rgb("#fbf1c7"), + default-fg: rgb("#3c3836"), + default-bg: rgb("#fbf1c7"), + ), + rose-pine: ( + black: rgb("#191724"), + red: rgb("#eb6f92"), + green: rgb("#31748f"), + yellow: rgb("#f6c177"), + blue: rgb("#9ccfd8"), + magenta: rgb("#c4a7e7"), + cyan: rgb("#ebbcba"), + white: rgb("#e0def4"), + gray: rgb("#6e6a86"), + bright-red: rgb("#eb6f92"), + bright-green: rgb("#31748f"), + bright-yellow: rgb("#f6c177"), + bright-blue: rgb("#9ccfd8"), + bright-magenta: rgb("#c4a7e7"), + bright-cyan: rgb("#ebbcba"), + bright-white: rgb("#e0def4"), + default-fg: rgb("#e0def4"), + default-bg: rgb("#191724"), + ), + rose-pine-moon: ( + black: rgb("#232136"), + red: rgb("#eb6f92"), + green: rgb("#3e8fb0"), + yellow: rgb("#f6c177"), + blue: rgb("#9ccfd8"), + magenta: rgb("#c4a7e7"), + cyan: rgb("#ea9a97"), + white: rgb("#e0def4"), + gray: rgb("#817c9c"), + bright-red: rgb("#eb6f92"), + bright-green: rgb("#3e8fb0"), + bright-yellow: rgb("#f6c177"), + bright-blue: rgb("#9ccfd8"), + bright-magenta: rgb("#c4a7e7"), + bright-cyan: rgb("#ea9a97"), + bright-white: rgb("#e0def4"), + default-fg: rgb("#e0def4"), + default-bg: rgb("#232136"), + ), + rose-pine-dawn: ( + black: rgb("#faf4ed"), + red: rgb("#b4637a"), + green: rgb("#286983"), + yellow: rgb("#ea9d34"), + blue: rgb("#56949f"), + magenta: rgb("#907aa9"), + cyan: rgb("#d7827e"), + white: rgb("#575279"), + gray: rgb("#6e6a86"), + bright-red: rgb("#b4637a"), + bright-green: rgb("#286983"), + bright-yellow: rgb("#ea9d34"), + bright-blue: rgb("#56949f"), + bright-magenta: rgb("#907aa9"), + bright-cyan: rgb("#d7827e"), + bright-white: rgb("#575279"), + default-fg: rgb("#575279"), + default-bg: rgb("#faf4ed"), + ), +) + +// ansi rendering function +#let ansi-render( + body, + font: "Cascadia Code", + size: 1em, + width: auto, + height: auto, + breakable: true, + radius: 0pt, + inset: 0pt, + outset: 0pt, + spacing: 1.2em, + above: 1.2em, + below: 1.2em, + clip: false, + bold-is-bright: false, + theme: terminal-themes.vscode-light, +) = { + // dict with text style + let match-text = ( + "1": (weight: "bold"), + "3": (style: "italic"), + "22": (weight: "regular"), + "23": (style: "normal"), + "30": (fill: theme.black), + "31": (fill: theme.red), + "32": (fill: theme.green), + "33": (fill: theme.yellow), + "34": (fill: theme.blue), + "35": (fill: theme.magenta), + "36": (fill: theme.cyan), + "37": (fill: theme.white), + "39": (fill: theme.default-fg), + "90": (fill: theme.gray), + "91": (fill: theme.bright-red), + "92": (fill: theme.bright-green), + "93": (fill: theme.bright-yellow), + "94": (fill: theme.bright-blue), + "95": (fill: theme.bright-magenta), + "96": (fill: theme.bright-cyan), + "97": (fill: theme.bright-white), + "default": (weight: "regular", style: "normal", fill: theme.default-fg), + ) + // dict with background style + let match-bg = ( + "40": (fill: theme.black), + "41": (fill: theme.red), + "42": (fill: theme.green), + "43": (fill: theme.yellow), + "44": (fill: theme.blue), + "45": (fill: theme.magenta), + "46": (fill: theme.cyan), + "47": (fill: theme.white), + "49": (fill: theme.default-bg), + "100": (fill: theme.gray), + "101": (fill: theme.bright-red), + "102": (fill: theme.bright-green), + "103": (fill: theme.bright-yellow), + "104": (fill: theme.bright-blue), + "105": (fill: theme.bright-magenta), + "106": (fill: theme.bright-cyan), + "107": (fill: theme.bright-white), + "default": (fill: theme.default-bg), + ) + // convert normal colors to their bright variants + let convert-color-bright(color) = { + if color == theme.black { + theme.gray + } else if color == theme.red { + theme.bright-red + } else if color == theme.green { + theme.bright-green + } else if color == theme.yellow { + theme.bright-yellow + } else if color == theme.blue { + theme.bright-blue + } else if color == theme.magenta { + theme.bright-magenta + } else if color == theme.cyan { + theme.bright-cyan + } else if color == theme.white { + theme.bright-white + } else { + color + } + } + + // match for regex parsed options + // input: array of options in string inside escape sequence + // return: a dict with text and background style + let match-options(opt) = { + // parse 38;5 48;5 256-color + let parse-8bit-color(num) = { + num = int(num) + let colors = (0, 95, 135, 175, 215, 255) + if num <= 7 { match-text.at(str(num + 30)) } else if num <= 15 { + match-text.at(str(num + 82)) + } else if num <= 231 { + num -= 16 + ( + fill: rgb( + colors.at(int(num / 36)), + colors.at(calc.rem(int(num / 6), 6)), + colors.at(calc.rem(num, 6)), + ), + ) + } else { + num -= 232 + (fill: rgb(8 + 10 * num, 8 + 10 * num, 8 + 10 * num)) + } + } + + let (opt-text, opt-bg) = ((:), (:)) + let (ul, ol, rev, last) = (none, none, none, none) + let count = 0 + let color = (0, 0, 0) + + // match options + for i in opt { + if last == "382" or last == "482" { + color.at(count) = int(i) + count += 1 + if count == 3 { + if last == "382" { + opt-text += (fill: rgb(..color)) + } else { + opt-bg += (fill: rgb(..color)) + } + count = 0 + last = none + } + continue + } else if last == "385" { + opt-text += parse-8bit-color(i) + last = none + continue + } else if last == "485" { + opt-bg += parse-8bit-color(i) + last = none + continue + } else if i == "0" or i == "" { + opt-text += match-text.default + opt-bg += match-bg.default + ul = false + ol = false + rev = false + } else if i in match-bg.keys() { + opt-bg += match-bg.at(i) + } else if i in match-text.keys() { + opt-text += match-text.at(i) + } else if i == "4" { + ul = true + } else if i == "24" { + ul = false + } else if i == "53" { + ol = true + } else if i == "55" { + ol = false + } else if i == "7" { + rev = true + } else if i == "27" { + rev = false + } else if i == "38" or i == "48" { + last = i + continue + } else if i == "2" or i == "5" { + if last == "38" or last == "48" { + last += i + count = 0 + continue + } + } + last = none + } + (text: opt-text, bg: opt-bg, ul: ul, ol: ol, rev: rev) + } + + // parse escape sequence + // return: array of (str, options) + // str is split by newline + let parse-option(body) = { + let arr = () + let cur = 0 + for map in body.matches(regex("\x1b\[([0-9;]*)m([^\x1b]*)")) { + // loop through all matches + let str = map.captures.at(1) + // split the string by newline and preserve newline + let split = str.split("\n") + for (k, v) in split.enumerate() { + if k != split.len() - 1 { + v = v + "\n" + } + let temp = (v, ()) + for option in map.captures.at(0).split(";") { + temp.at(1).push(option) + } + arr.push(temp) + } + cur += 1 + } + arr + } + + // prevent set from outside of the function + set box( + width: auto, + height: auto, + baseline: 0pt, + fill: none, + stroke: none, + radius: 0pt, + inset: 0pt, + outset: 0pt, + clip: false, + ) + + // settings + show raw: if font == none { + text.with(top-edge: "ascender", bottom-edge: "descender") + } else { + text.with(font: font, top-edge: "ascender", bottom-edge: "descender") + } + set text(..(match-text.default), size: size) + set par(leading: 0em) + show: block.with( + ..(match-bg.default), + width: width, + height: height, + breakable: breakable, + radius: radius, + inset: inset, + outset: outset, + spacing: spacing, + above: above, + below: below, + clip: clip, + ) + + // current option + let option = ( + text: match-text.default, + bg: match-bg.default, + ul: false, + ol: false, + rev: false, + ) + // workaround for rendering first line without escape sequence + body = "\u{1b}[0m" + body + // workaround for one trailing newline consumed by raw + if body.ends-with("\n") { + body = body + "\n" + } + for (str, opt) in parse-option(body) { + let m = match-options(opt) + option.text += m.text + option.bg += m.bg + if m.rev != none { option.rev = m.rev } + if option.rev { (option.text.fill, option.bg.fill) = (option.bg.fill, option.text.fill) } + if m.ul != none { option.ul = m.ul } + if m.ol != none { option.ol = m.ol } + // slightly reduce pdf size by removing default box fill + if option.bg.fill == theme.default-bg { option.bg.fill = none } + if option.text.fill == none { option.text.fill = theme.default-bg } + + { + show: box.with(..option.bg) + set text(..option.text, ..if bold-is-bright and option.text.weight == "bold" { + (fill: convert-color-bright(option.text.fill)) + }) + show: c => if option.ul { underline(c) } else { c } + show: c => if option.ol { overline(c) } else { c } + raw(str) + } + // fill trailing newlines + let s = str.find(regex("\n+$")) + if s != none { + linebreak() * s.len() + } + } +} diff --git a/packages/preview/ansi-render/0.9.2/typst.toml b/packages/preview/ansi-render/0.9.2/typst.toml new file mode 100644 index 0000000000..1c02712759 --- /dev/null +++ b/packages/preview/ansi-render/0.9.2/typst.toml @@ -0,0 +1,12 @@ +[package] +name = "ansi-render" +version = "0.9.2" +entrypoint = "lib.typ" +authors = ["8LWXpg"] +license = "MIT" +description = "provides a simple way to render text with ANSI escape sequences." +repository = "https://github.com/8LWXpg/typst-ansi-render" +keywords = ["ansi", "escape", "terminal"] +categories = ["visualization"] +disciplines = ["computer-science"] +compiler = "0.14.0"