├── .gitignore
├── .vscode
├── settings.json
└── tasks.json
├── img
├── 1.png
├── 2.png
├── 3.png
└── 4.png
├── test
├── demo.pdf
├── themes.pdf
├── test.txt
├── demo.typ
├── themes.typ
└── color.txt
├── typst.toml
├── CHANGELOG.md
├── LICENSE
├── .github
└── workflows
│ └── update-package.yml
├── README.md
└── ansi-render.typ
/.gitignore:
--------------------------------------------------------------------------------
1 | ansi_render.pdf
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.tabSize": 2
3 | }
--------------------------------------------------------------------------------
/img/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gobidev/typst-ansi-render/master/img/1.png
--------------------------------------------------------------------------------
/img/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gobidev/typst-ansi-render/master/img/2.png
--------------------------------------------------------------------------------
/img/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gobidev/typst-ansi-render/master/img/3.png
--------------------------------------------------------------------------------
/img/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gobidev/typst-ansi-render/master/img/4.png
--------------------------------------------------------------------------------
/test/demo.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gobidev/typst-ansi-render/master/test/demo.pdf
--------------------------------------------------------------------------------
/test/themes.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Gobidev/typst-ansi-render/master/test/themes.pdf
--------------------------------------------------------------------------------
/typst.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "ansi-render"
3 | version = "0.4.1"
4 | entrypoint = "ansi-render.typ"
5 | authors = ["8LWXpg"]
6 | license = "MIT"
7 | description = "provides a simple way to render text with ANSI escape sequences."
8 | repository = "https://github.com/8LWXpg/typst-ansi-render"
9 |
--------------------------------------------------------------------------------
/test/test.txt:
--------------------------------------------------------------------------------
1 | [1;31m----------------------------------------------------------------------[0m
2 | [1;31mNameError[0m Traceback (most recent call last)
3 | Cell [1;32mIn[9], line 1[0m
4 | [1;32m----> 1[0m this_will_error
5 |
6 | [1;31mNameError[0m: name 'this_will_error' is not defined
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "label": "watch",
8 | "type": "shell",
9 | "command": "typst",
10 | "args": [
11 | "w",
12 | "test/demo.typ",
13 | "--root",
14 | "${workspaceFolder}"
15 | ],
16 | "group": {
17 | "kind": "build",
18 | "isDefault": true
19 | }
20 | }
21 | ]
22 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog 📝
2 |
3 | ## [0.4.1] - 2023-09-22
4 |
5 | ### Changed
6 |
7 | * Changed default font size to `9pt`
8 | * Prevent set from outside of the function
9 |
10 | ## [0.4.0] - 2023-09-13
11 |
12 | ### Added
13 |
14 | * Added most options from [`block`]([https://](https://typst.app/docs/reference/layout/block/)) function
15 | * Added `vscode-light` theme
16 |
17 | ### Changed
18 |
19 | * Changed outmost layout from `rect` to `block`
20 | * Changed default theme to `VSCode Light`
21 |
22 | ## [0.3.0] - 2023-09-09
23 |
24 | ### Added
25 |
26 | * Added `radius` option, default is `3pt`
27 |
28 | ### Changed
29 |
30 | * Changed default font size to `10pt`
31 | * Changed default font to `Cascadia Code`
32 | * Changed default theme to `Solarized Light`
33 |
34 | ## [0.2.0] 2023-08-05
35 |
36 | ### Changed
37 |
38 | * Changed coding style to kebab-case and two spaces
39 |
40 | ## [0.1.0] 2023-07-02
41 |
42 | first release
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 8LWXpg
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.github/workflows/update-package.yml:
--------------------------------------------------------------------------------
1 | name: Update package
2 |
3 | # push a new tag before running this action
4 | on:
5 | workflow_dispatch:
6 |
7 | jobs:
8 | push:
9 | name: push to package
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v3
13 | with:
14 | fetch-depth: 0
15 |
16 | - name: get latest tag
17 | id: latest_tag
18 | uses: WyriHaximus/github-action-get-previous-tag@v1
19 |
20 | - name: set tag
21 | id: parsed_tag
22 | run: |
23 | version=${{ steps.latest_tag.outputs.tag }}
24 | echo "tag=${version:1}" >> $GITHUB_OUTPUT
25 |
26 | - name: push
27 | uses: nkoppel/push-files-to-another-repository@v1.1.1
28 | # repo PAT
29 | env:
30 | API_TOKEN_GITHUB: ${{ secrets.PERSONAL_TOKEN }}
31 | with:
32 | commit-email: v3ak6xhthk@gmail.com
33 | source-files: ansi-render.typ CHANGELOG.md LICENSE README.md typst.toml
34 | destination-username: ${{ github.repository_owner }}
35 | destination-repository: packages
36 | destination-directory: packages/preview/ansi-render/${{ steps.parsed_tag.outputs.tag }}
37 | destination-branch: main
38 |
--------------------------------------------------------------------------------
/test/demo.typ:
--------------------------------------------------------------------------------
1 | #import "../ansi-render.typ": *
2 |
3 | = Render text directly
4 | #ansi-render(
5 | "\u{1b}[38;2;255;0;0mThis text is red.\u{1b}[0m
6 | \u{1b}[48;2;0;255;0mThis background is green.\u{1b}[0m
7 | \u{1b}[38;2;255;255;255m\u{1b}[48;2;0;0;255mThis text is white on a blue background.\u{1b}[0m
8 | \u{1b}[1mThis text is bold.\u{1b}[0m
9 | \u{1b}[4mThis text is underlined.\u{1b}[0m
10 | \u{1b}[38;2;255;165;0m\u{1b}[48;2;255;255;0mThis text is orange on a yellow background.\u{1b}[0m
11 | ",
12 | inset: 5pt,
13 | radius: 3pt,
14 | theme: terminal-themes.vscode
15 | )
16 |
17 | #ansi-render(
18 | "\u{1b}[38;5;196mRed text\u{1b}[0m
19 | \u{1b}[48;5;27mBlue background\u{1b}[0m
20 | \u{1b}[38;5;226;48;5;18mYellow text on blue background\u{1b}[0m
21 | \u{1b}[7mInverted text\u{1b}[0m
22 | \u{1b}[38;5;208;48;5;237mOrange text on gray background\u{1b}[0m
23 | \u{1b}[38;5;39;48;5;208mBlue text on orange background\u{1b}[0m
24 | \u{1b}[38;5;255;48;5;0mWhite text on black background\u{1b}[0m",
25 | inset: 5pt,
26 | radius: 3pt,
27 | theme: terminal-themes.vscode
28 | )
29 |
30 | #ansi-render(
31 | "\u{1b}[31;1mHello \u{1b}[7mWorld\u{1b}[0m
32 |
33 | \u{1b}[53;4;36mOver and \u{1b}[35m Under!
34 | \u{1b}[7;90mreverse\u{1b}[101m and \u{1b}[94;27mreverse",
35 | inset: 5pt,
36 | radius: 3pt,
37 | theme: terminal-themes.vscode
38 | )
39 |
40 | = Render text from a file
41 | #ansi-render(read("test.txt"), inset: 5pt, radius: 3pt)
42 |
43 | = Uses the font supports ligatures
44 | #ansi-render(read("test.txt"), font: "CaskaydiaCove Nerd Font Mono", inset: 5pt, radius: 3pt, theme: terminal-themes.putty)
45 |
--------------------------------------------------------------------------------
/test/themes.typ:
--------------------------------------------------------------------------------
1 | #import "../ansi-render.typ": *
2 |
3 | = List of built-in themes
4 | == VSCode
5 | #ansi-render(read("color.txt"), size: 8pt, inset: 5pt, radius: 3pt, theme: terminal-themes.vscode)
6 | == VSCode Light
7 | #ansi-render(read("color.txt"), size: 8pt, inset: 5pt, radius: 3pt, theme: terminal-themes.vscode-light)
8 | == Putty
9 | #ansi-render(read("color.txt"), size: 8pt, inset: 5pt, radius: 3pt, theme: terminal-themes.putty)
10 | == Campbell
11 | #ansi-render(read("color.txt"), size: 8pt, inset: 5pt, radius: 3pt, theme: terminal-themes.campbell)
12 | == Campbell Powershell
13 | #ansi-render(read("color.txt"), size: 8pt, inset: 5pt, radius: 3pt, theme: terminal-themes.campbell-powershell)
14 | == Vintage
15 | #ansi-render(read("color.txt"), size: 8pt, inset: 5pt, radius: 3pt, theme: terminal-themes.vintage)
16 | == One Half Dark
17 | #ansi-render(read("color.txt"), size: 8pt, inset: 5pt, radius: 3pt, theme: terminal-themes.one-half-dark)
18 | == One Half Light
19 | #ansi-render(read("color.txt"), size: 8pt, inset: 5pt, radius: 3pt, theme: terminal-themes.one-half-light)
20 | == Solarized Dark
21 | #ansi-render(read("color.txt"), size: 8pt, inset: 5pt, radius: 3pt, theme: terminal-themes.solarized-dark)
22 | == Solarized Light
23 | #ansi-render(read("color.txt"), size: 8pt, inset: 5pt, radius: 3pt, theme: terminal-themes.solarized-light)
24 | == Tango Dark
25 | #ansi-render(read("color.txt"), size: 8pt, inset: 5pt, radius: 3pt, theme: terminal-themes.tango-dark)
26 | == Tango Light
27 | #ansi-render(read("color.txt"), size: 8pt, inset: 5pt, radius: 3pt, theme: terminal-themes.tango-light)
--------------------------------------------------------------------------------
/test/color.txt:
--------------------------------------------------------------------------------
1 | 40m 41m 42m 43m 44m 45m 46m 47m
2 | 30m [30;40m gYw [0m [30;41m gYw [0m [30;42m gYw [0m [30;43m gYw [0m [30;44m gYw [0m [30;45m gYw [0m [30;46m gYw [0m [30;47m gYw [0m
3 | 31m [31;40m gYw [0m [31;41m gYw [0m [31;42m gYw [0m [31;43m gYw [0m [31;44m gYw [0m [31;45m gYw [0m [31;46m gYw [0m [31;47m gYw [0m
4 | 32m [32;40m gYw [0m [32;41m gYw [0m [32;42m gYw [0m [32;43m gYw [0m [32;44m gYw [0m [32;45m gYw [0m [32;46m gYw [0m [32;47m gYw [0m
5 | 33m [33;40m gYw [0m [33;41m gYw [0m [33;42m gYw [0m [33;43m gYw [0m [33;44m gYw [0m [33;45m gYw [0m [33;46m gYw [0m [33;47m gYw [0m
6 | 34m [34;40m gYw [0m [34;41m gYw [0m [34;42m gYw [0m [34;43m gYw [0m [34;44m gYw [0m [34;45m gYw [0m [34;46m gYw [0m [34;47m gYw [0m
7 | 35m [35;40m gYw [0m [35;41m gYw [0m [35;42m gYw [0m [35;43m gYw [0m [35;44m gYw [0m [35;45m gYw [0m [35;46m gYw [0m [35;47m gYw [0m
8 | 36m [36;40m gYw [0m [36;41m gYw [0m [36;42m gYw [0m [36;43m gYw [0m [36;44m gYw [0m [36;45m gYw [0m [36;46m gYw [0m [36;47m gYw [0m
9 | 37m [37;40m gYw [0m [37;41m gYw [0m [37;42m gYw [0m [37;43m gYw [0m [37;44m gYw [0m [37;45m gYw [0m [37;46m gYw [0m [37;47m gYw [0m
10 |
11 | 100m 101m 102m 103m 104m 105m 106m 107m
12 | 90m [90;100m gYw [0m [90;101m gYw [0m [90;102m gYw [0m [90;103m gYw [0m [90;104m gYw [0m [90;105m gYw [0m [90;106m gYw [0m [90;107m gYw [0m
13 | 91m [91;100m gYw [0m [91;101m gYw [0m [91;102m gYw [0m [91;103m gYw [0m [91;104m gYw [0m [91;105m gYw [0m [91;106m gYw [0m [91;107m gYw [0m
14 | 92m [92;100m gYw [0m [92;101m gYw [0m [92;102m gYw [0m [92;103m gYw [0m [92;104m gYw [0m [92;105m gYw [0m [92;106m gYw [0m [92;107m gYw [0m
15 | 93m [93;100m gYw [0m [93;101m gYw [0m [93;102m gYw [0m [93;103m gYw [0m [93;104m gYw [0m [93;105m gYw [0m [93;106m gYw [0m [93;107m gYw [0m
16 | 94m [94;100m gYw [0m [94;101m gYw [0m [94;102m gYw [0m [94;103m gYw [0m [94;104m gYw [0m [94;105m gYw [0m [94;106m gYw [0m [94;107m gYw [0m
17 | 95m [95;100m gYw [0m [95;101m gYw [0m [95;102m gYw [0m [95;103m gYw [0m [95;104m gYw [0m [95;105m gYw [0m [95;106m gYw [0m [95;107m gYw [0m
18 | 96m [96;100m gYw [0m [96;101m gYw [0m [96;102m gYw [0m [96;103m gYw [0m [96;104m gYw [0m [96;105m gYw [0m [96;106m gYw [0m [96;107m gYw [0m
19 | 97m [97;100m gYw [0m [97;101m gYw [0m [97;102m gYw [0m [97;103m gYw [0m [97;104m gYw [0m [97;105m gYw [0m [97;106m gYw [0m [97;107m gYw [0m
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ANSI Escape Sequence Renderer
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | 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`.
14 |
15 | contribution is welcomed!
16 |
17 | ## Usage
18 |
19 | ```typst
20 | #import "@preview/ansi-render:0.4.1": *
21 |
22 | #ansi-render(
23 | string,
24 | font: string,
25 | size: length,
26 | width: auto or relative length,
27 | height: auto or relative length,
28 | breakable: boolean,
29 | radius: relative length or dictionary,
30 | inset: relative length or dictionary,
31 | outset: relative length or dictionary,
32 | spacing: relative length or fraction,
33 | above: relative length or fraction,
34 | below: relative length or fraction,
35 | clip: boolean,
36 | theme: terminal-themes.theme,
37 | )
38 | ```
39 |
40 | ### Parameters
41 |
42 | most parameters comes from [`block`]([https://](https://typst.app/docs/reference/layout/block/)) function, adjust the layout outmost block.
43 |
44 | - `string` - string with ANSI escape sequences
45 | - `font` - font name, default is `Cascadia Code`
46 | - `size` - font size, default is `9pt`
47 | - `theme` - theme, default is `vscode-light`
48 | - parameters from [`block`]([https://](https://typst.app/docs/reference/layout/block/)) function with the same default value, change to adjust the outmost layout:
49 | - `width`
50 | - `height`
51 | - `breakable`
52 | - `radius`
53 | - `inset`
54 | - `outset`
55 | - `spacing`
56 | - `above`
57 | - `below`
58 | - `clip`
59 |
60 | ## Themes
61 |
62 | see [themes](https://github.com/8LWXpg/typst-ansi-render/blob/master/test/themes.pdf)
63 |
64 | ## Demo
65 |
66 | see [demo.typ](https://github.com/8LWXpg/typst-ansi-render/blob/master/test/demo.typ) [demo.pdf](https://github.com/8LWXpg/typst-ansi-render/blob/master/test/demo.pdf)
67 |
68 | ```typst
69 | #ansi-render(
70 | "\u{1b}[38;2;255;0;0mThis text is red.\u{1b}[0m
71 | \u{1b}[48;2;0;255;0mThis background is green.\u{1b}[0m
72 | \u{1b}[38;2;255;255;255m\u{1b}[48;2;0;0;255mThis text is white on a blue background.\u{1b}[0m
73 | \u{1b}[1mThis text is bold.\u{1b}[0m
74 | \u{1b}[4mThis text is underlined.\u{1b}[0m
75 | \u{1b}[38;2;255;165;0m\u{1b}[48;2;255;255;0mThis text is orange on a yellow background.\u{1b}[0m
76 | ",
77 | theme: terminal-themes.vscode
78 | )
79 | ```
80 |
81 | 
82 |
83 | ```typst
84 | #ansi-render(
85 | "\u{1b}[38;5;196mRed text\u{1b}[0m
86 | \u{1b}[48;5;27mBlue background\u{1b}[0m
87 | \u{1b}[38;5;226;48;5;18mYellow text on blue background\u{1b}[0m
88 | \u{1b}[7mInverted text\u{1b}[0m
89 | \u{1b}[38;5;208;48;5;237mOrange text on gray background\u{1b}[0m
90 | \u{1b}[38;5;39;48;5;208mBlue text on orange background\u{1b}[0m
91 | \u{1b}[38;5;255;48;5;0mWhite text on black background\u{1b}[0m",
92 | theme: terminal-themes.vscode
93 | )
94 | ```
95 |
96 | 
97 |
98 | ```typst
99 | #ansi-render(
100 | "\u{1b}[31;1mHello \u{1b}[7mWorld\u{1b}[0m
101 |
102 | \u{1b}[53;4;36mOver and \u{1b}[35m Under!
103 | \u{1b}[7;90mreverse\u{1b}[101m and \u{1b}[94;27mreverse",
104 | theme: terminal-themes.vscode
105 | )
106 | ```
107 |
108 | 
109 |
110 | ```typst
111 | // uses the font that supports ligatures
112 | #ansi-render(read("test.txt"), font: "CaskaydiaCove Nerd Font Mono", theme: terminal-themes.putty)
113 | ```
114 |
115 | 
116 |
--------------------------------------------------------------------------------
/ansi-render.typ:
--------------------------------------------------------------------------------
1 | // add your theme here!
2 | #let terminal-themes = (
3 | // vscode terminal theme
4 | vscode: (
5 | black: rgb(0, 0, 0),
6 | red: rgb(205, 49, 49),
7 | green: rgb(13, 188, 121),
8 | yellow: rgb(229, 229, 16),
9 | blue: rgb(36, 114, 200),
10 | magenta: rgb(188, 63, 188),
11 | cyan: rgb(17, 168, 205),
12 | white: rgb(229, 229, 229),
13 | gray: rgb(102, 102, 102),
14 | bright-red: rgb(214, 76, 76),
15 | bright-green: rgb(35, 209, 139),
16 | bright-yellow: rgb(245, 245, 67),
17 | bright-blue: rgb(59, 142, 234),
18 | bright-magenta: rgb(214, 112, 214),
19 | bright-cyan: rgb(41, 184, 219),
20 | bright-white: rgb(229, 229, 229),
21 | default-text: rgb(229, 229, 229), // white
22 | default-bg: rgb(0, 0, 0), // black
23 | ),
24 |
25 | // vscode light theme
26 | vscode-light: (
27 | black: rgb("#F8F8F8"),
28 | red: rgb("#CD3131"),
29 | green: rgb("#00BC00"),
30 | yellow: rgb("#949800"),
31 | blue: rgb("#0451A5"),
32 | magenta: rgb("#BC05BC"),
33 | cyan: rgb("#0598BC"),
34 | white: rgb("#555555"),
35 | gray: rgb("#666666"),
36 | bright-red: rgb("#CD3131"),
37 | bright-green: rgb("#14CE14"),
38 | bright-yellow: rgb("#B5BA00"),
39 | bright-blue: rgb("#0451A5"),
40 | bright-magenta: rgb("#BC05BC"),
41 | bright-cyan: rgb("#0598BC"),
42 | bright-white: rgb("#A5A5A5"),
43 | default-text: rgb("#A5A5A5"), // white
44 | default-bg: rgb("#F8F8F8"), // black
45 | ),
46 |
47 | // putty terminal theme
48 | putty: (
49 | black: rgb(0, 0, 0),
50 | red: rgb(187, 0, 0),
51 | green: rgb(0, 187, 0),
52 | yellow: rgb(187, 187, 0),
53 | blue: rgb(0, 0, 187),
54 | magenta: rgb(187, 0, 187),
55 | cyan: rgb(0, 187, 187),
56 | white: rgb(187, 187, 187),
57 | gray: rgb(85, 85, 85),
58 | bright-red: rgb(255, 0, 0),
59 | bright-green: rgb(0, 255, 0),
60 | bright-yellow: rgb(255, 255, 0),
61 | bright-blue: rgb(0, 0, 255),
62 | bright-magenta: rgb(255, 0, 255),
63 | bright-cyan: rgb(0, 255, 255),
64 | bright-white: rgb(255, 255, 255),
65 | default-text: rgb(187, 187, 187), // white
66 | default-bg: rgb(0, 0, 0), // black
67 | ),
68 |
69 | // themes from Windows Terminal
70 | campbell: (
71 | black: rgb("#0C0C0C"),
72 | red: rgb("#C50F1F"),
73 | green: rgb("#13A10E"),
74 | yellow: rgb("#C19C00"),
75 | blue: rgb("#0037DA"),
76 | magenta: rgb("#881798"),
77 | cyan: rgb("#3A96DD"),
78 | white: rgb("#CCCCCC"),
79 | gray: rgb("#767676"),
80 | bright-red: rgb("#E74856"),
81 | bright-green: rgb("#16C60C"),
82 | bright-yellow: rgb("#F9F1A5"),
83 | bright-blue: rgb("#3B78FF"),
84 | bright-magenta: rgb("#B4009E"),
85 | bright-cyan: rgb("#61D6D6"),
86 | bright-white: rgb("#F2F2F2"),
87 | default-text: rgb("#CCCCCC"),
88 | default-bg: rgb("#0C0C0C"),
89 | ),
90 |
91 | campbell-powershell: (
92 | black: rgb("#0C0C0C"),
93 | red: rgb("#C50F1F"),
94 | green: rgb("#13A10E"),
95 | yellow: rgb("#C19C00"),
96 | blue: rgb("#0037DA"),
97 | magenta: rgb("#881798"),
98 | cyan: rgb("#3A96DD"),
99 | white: rgb("#CCCCCC"),
100 | gray: rgb("#767676"),
101 | bright-red: rgb("#E74856"),
102 | bright-green: rgb("#16C60C"),
103 | bright-yellow: rgb("#F9F1A5"),
104 | bright-blue: rgb("#3B78FF"),
105 | bright-magenta: rgb("#B4009E"),
106 | bright-cyan: rgb("#61D6D6"),
107 | bright-white: rgb("#F2F2F2"),
108 | default-text: rgb("#CCCCCC"),
109 | default-bg: rgb("#012456"),
110 | ),
111 |
112 | vintage: (
113 | black: rgb("#000000"),
114 | red: rgb("#800000"),
115 | green: rgb("#008000"),
116 | yellow: rgb("#808000"),
117 | blue: rgb("#000080"),
118 | magenta: rgb("#800080"),
119 | cyan: rgb("#008080"),
120 | white: rgb("#C0C0C0"),
121 | gray: rgb("#808080"),
122 | bright-red: rgb("#FF0000"),
123 | bright-green: rgb("#00FF00"),
124 | bright-yellow: rgb("#FFFF00"),
125 | bright-blue: rgb("#0000FF"),
126 | bright-magenta: rgb("#FF00FF"),
127 | bright-cyan: rgb("#00FFFF"),
128 | bright-white: rgb("#FFFFFF"),
129 | default-text: rgb("#C0C0C0"),
130 | default-bg: rgb("#000000"),
131 | ),
132 |
133 | one-half-dark: (
134 | black: rgb("#282C34"),
135 | red: rgb("#E06C75"),
136 | green: rgb("#98C379"),
137 | yellow: rgb("#E5C07B"),
138 | blue: rgb("#61AFEF"),
139 | magenta: rgb("#C678DD"),
140 | cyan: rgb("#56B6C2"),
141 | white: rgb("#DCDFE4"),
142 | gray: rgb("#5A6374"),
143 | bright-red: rgb("#E06C75"),
144 | bright-green: rgb("#98C379"),
145 | bright-yellow: rgb("#E5C07B"),
146 | bright-blue: rgb("#61AFEF"),
147 | bright-magenta: rgb("#C678DD"),
148 | bright-cyan: rgb("#56B6C2"),
149 | bright-white: rgb("#DCDFE4"),
150 | default-text: rgb("#DCDFE4"),
151 | default-bg: rgb("#282C34"),
152 | ),
153 |
154 | one-half-light: (
155 | black: rgb("#383A42"),
156 | red: rgb("#E45649"),
157 | green: rgb("#50A14F"),
158 | yellow: rgb("#C18301"),
159 | blue: rgb("#0184BC"),
160 | magenta: rgb("#A626A4"),
161 | cyan: rgb("#0997B3"),
162 | white: rgb("#FAFAFA"),
163 | gray: rgb("#4F525D"),
164 | bright-red: rgb("#DF6C75"),
165 | bright-green: rgb("#98C379"),
166 | bright-yellow: rgb("#E4C07A"),
167 | bright-blue: rgb("#61AFEF"),
168 | bright-magenta: rgb("#C577DD"),
169 | bright-cyan: rgb("#56B5C1"),
170 | bright-white: rgb("#FFFFFF"),
171 | default-text: rgb("#383A42"),
172 | default-bg: rgb("#FAFAFA"),
173 | ),
174 |
175 | solarized-dark: (
176 | black: rgb("#002B36"),
177 | red: rgb("#DC322F"),
178 | green: rgb("#859900"),
179 | yellow: rgb("#B58900"),
180 | blue: rgb("#268BD2"),
181 | magenta: rgb("#D33682"),
182 | cyan: rgb("#2AA198"),
183 | white: rgb("#EEE8D5"),
184 | gray: rgb("#073642"),
185 | bright-red: rgb("#CB4B16"),
186 | bright-green: rgb("#586E75"),
187 | bright-yellow: rgb("#657B83"),
188 | bright-blue: rgb("#839496"),
189 | bright-magenta: rgb("#6C71C4"),
190 | bright-cyan: rgb("#93A1A1"),
191 | bright-white: rgb("#FDF6E3"),
192 | default-text: rgb("#839496"),
193 | default-bg: rgb("#002B36"),
194 | ),
195 |
196 | solarized-light: (
197 | black: rgb("#002B36"),
198 | red: rgb("#DC322F"),
199 | green: rgb("#859900"),
200 | yellow: rgb("#B58900"),
201 | blue: rgb("#268BD2"),
202 | magenta: rgb("#D33682"),
203 | cyan: rgb("#2AA198"),
204 | white: rgb("#EEE8D5"),
205 | gray: rgb("#073642"),
206 | bright-red: rgb("#CB4B16"),
207 | bright-green: rgb("#586E75"),
208 | bright-yellow: rgb("#657B83"),
209 | bright-blue: rgb("#839496"),
210 | bright-magenta: rgb("#6C71C4"),
211 | bright-cyan: rgb("#93A1A1"),
212 | bright-white: rgb("#FDF6E3"),
213 | default-text: rgb("#657B83"),
214 | default-bg: rgb("#FDF6E3"),
215 | ),
216 |
217 | tango-dark: (
218 | black: rgb("#000000"),
219 | red: rgb("#CC0000"),
220 | green: rgb("#4E9A06"),
221 | yellow: rgb("#C4A000"),
222 | blue: rgb("#3465A4"),
223 | magenta: rgb("#75507B"),
224 | cyan: rgb("#06989A"),
225 | white: rgb("#D3D7CF"),
226 | gray: rgb("#555753"),
227 | bright-red: rgb("#EF2929"),
228 | bright-green: rgb("#8AE234"),
229 | bright-yellow: rgb("#FCE94F"),
230 | bright-blue: rgb("#729FCF"),
231 | bright-magenta: rgb("#AD7FA8"),
232 | bright-cyan: rgb("#34E2E2"),
233 | bright-white: rgb("#EEEEEC"),
234 | default-text: rgb("#D3D7CF"),
235 | default-bg: rgb("#000000"),
236 | ),
237 |
238 | tango-light: (
239 | black: rgb("#000000"),
240 | red: rgb("#CC0000"),
241 | green: rgb("#4E9A06"),
242 | yellow: rgb("#C4A000"),
243 | blue: rgb("#3465A4"),
244 | magenta: rgb("#75507B"),
245 | cyan: rgb("#06989A"),
246 | white: rgb("#D3D7CF"),
247 | gray: rgb("#555753"),
248 | bright-red: rgb("#EF2929"),
249 | bright-green: rgb("#8AE234"),
250 | bright-yellow: rgb("#FCE94F"),
251 | bright-blue: rgb("#729FCF"),
252 | bright-magenta: rgb("#AD7FA8"),
253 | bright-cyan: rgb("#34E2E2"),
254 | bright-white: rgb("#EEEEEC"),
255 | default-text: rgb("#555753"),
256 | default-bg: rgb("#FFFFFF"),
257 | ),
258 | )
259 |
260 | // ansi rendering function
261 | #let ansi-render(
262 | body,
263 | font: "Cascadia Code",
264 | size: 9pt,
265 | width: auto,
266 | height: auto,
267 | breakable: true,
268 | radius: 0pt,
269 | inset: 0pt,
270 | outset: 0pt,
271 | spacing: 1.2em,
272 | above: 1.2em,
273 | below: 1.2em,
274 | clip: false,
275 | theme: terminal-themes.vscode-light) = {
276 | // dict with text style
277 | let match-text = (
278 | "1": (weight: "bold"),
279 | "3": (style: "italic"),
280 | "23": (style: "normal"),
281 | "30": (fill: theme.black),
282 | "31": (fill: theme.red),
283 | "32": (fill: theme.green),
284 | "33": (fill: theme.yellow),
285 | "34": (fill: theme.blue),
286 | "35": (fill: theme.magenta),
287 | "36": (fill: theme.cyan),
288 | "37": (fill: theme.white),
289 | "39": (fill: theme.default-text),
290 | "90": (fill: theme.gray),
291 | "91": (fill: theme.bright-red),
292 | "92": (fill: theme.bright-green),
293 | "93": (fill: theme.bright-yellow),
294 | "94": (fill: theme.bright-blue),
295 | "95": (fill: theme.bright-magenta),
296 | "96": (fill: theme.bright-cyan),
297 | "97": (fill: theme.bright-white),
298 | "default": (weight: "regular", style: "normal", fill: theme.default-text)
299 | )
300 | // dict with background style
301 | let match-bg = (
302 | "40": (fill: theme.black),
303 | "41": (fill: theme.red),
304 | "42": (fill: theme.green),
305 | "43": (fill: theme.yellow),
306 | "44": (fill: theme.blue),
307 | "45": (fill: theme.magenta),
308 | "46": (fill: theme.cyan),
309 | "47": (fill: theme.white),
310 | "49": (fill: theme.default-bg),
311 | "100": (fill: theme.gray),
312 | "101": (fill: theme.bright-red),
313 | "102": (fill: theme.bright-green),
314 | "103": (fill: theme.bright-yellow),
315 | "104": (fill: theme.bright-blue),
316 | "105": (fill: theme.bright-magenta),
317 | "106": (fill: theme.bright-cyan),
318 | "107": (fill: theme.bright-white),
319 | "default": (fill: theme.default-bg)
320 | )
321 |
322 | let match-options(opt) = {
323 | // parse 38;5 48;5
324 | let parse-8bit-color(num) = {
325 | num = int(num)
326 | let colors = (0, 95, 135, 175, 215, 255)
327 | if num <= 7 { match-text.at(str(num+30)) }
328 | else if num <= 15 { match-bg.at(str(num+32)) }
329 | else if num <= 231 {
330 | num -= 16
331 | let (r, g, b) = (colors.at(int(num/36)), colors.at(calc.rem(int(num/6),6)), colors.at(calc.rem(num,6)))
332 | (fill: rgb(r, g, b))
333 | } else {
334 | num -= 232
335 | let (r, g, b) = (8+10*num, 8+10*num, 8+10*num)
336 | (fill: rgb(r, g, b))
337 | }
338 | }
339 |
340 | let (opt-text, opt-bg) = ((:), (:))
341 | let (ul, ol, reverse, last) = (none, none, none, none)
342 | let count = 0
343 | let color = (0, 0, 0)
344 |
345 | // match options
346 | for i in opt {
347 | if last == "382" or last =="482" {
348 | color.at(count) = int(i)
349 | count += 1
350 | if count == 3 {
351 | if last == "382" { opt-text += (fill: rgb(..color)) }
352 | else { opt-bg += (fill: rgb(..color)) }
353 | count = 0
354 | last = none
355 | }
356 | continue
357 | }
358 | else if last == "385" {
359 | opt-text += parse-8bit-color(i)
360 | last = none
361 | continue
362 | }
363 | else if last == "485" {
364 | opt-bg += parse-8bit-color(i)
365 | last = none
366 | continue
367 | }
368 | else if i == "0" {
369 | opt-text += match-text.default
370 | opt-bg += match-bg.default
371 | ul = false
372 | ol = false
373 | reverse = false
374 | }
375 | else if i in match-bg.keys() { opt-bg += match-bg.at(i) }
376 | else if i in match-text.keys() { opt-text += match-text.at(i) }
377 | else if i == "4" { ul = true }
378 | else if i == "24" { ul = false }
379 | else if i == "53" { ol = true }
380 | else if i == "55" { ol = false }
381 | else if i == "7" { reverse = true }
382 | else if i == "27" { reverse = false }
383 | else if i == "38" or i == "48" {
384 | last = i
385 | continue
386 | }
387 | else if i == "2" or i == "5" {
388 | if last == "38" or last == "48" {
389 | last += i
390 | count = 0
391 | continue
392 | }
393 | }
394 | last = none
395 | }
396 | (text: opt-text, bg: opt-bg, ul: ul, ol: ol, reverse: reverse)
397 | }
398 |
399 | let parse-option(body) = {
400 | let arr = ()
401 | let cur = 0
402 | for map in body.matches(regex("\x1b\[([0-9;]*)m([^\x1b]*)")) {
403 | // loop through all matches
404 | let str = map.captures.at(1)
405 | // split the string by newline and preserve newline
406 | let split = str.split("\n")
407 | for (k, v) in split.enumerate() {
408 | if k != split.len()-1 {
409 | v = v + "\n"
410 | }
411 | let temp = (v, ())
412 | for option in map.captures.at(0).split(";") {
413 | temp.at(1).push(option)
414 | }
415 | arr.push(temp)
416 | }
417 | cur += 1
418 | }
419 | arr
420 | }
421 |
422 | set text(..(match-text.default), font: font, size: size, top-edge: "ascender", bottom-edge: "descender")
423 | set par(leading: 0em)
424 |
425 | let option = (
426 | text: match-text.default,
427 | bg: match-bg.default,
428 | ul: false,
429 | ol: false,
430 | reverse: false
431 | )
432 | show: block.with(
433 | ..(match-bg.default),
434 | width: width,
435 | height: height,
436 | breakable: breakable,
437 | radius: radius,
438 | inset: inset,
439 | outset: outset,
440 | spacing: spacing,
441 | above: above,
442 | below: below,
443 | clip: clip,
444 | )
445 | // prevent set from outside of the function
446 | set box(
447 | width: auto,
448 | height: auto,
449 | baseline: 0pt,
450 | fill: none,
451 | stroke: none,
452 | radius: 0pt,
453 | inset: 0pt,
454 | outset: 0pt,
455 | clip: false,
456 | )
457 | // work around for rendering first line without escape sequence
458 | body = "\u{1b}[0m" + body
459 | for (str, opt) in parse-option(body) {
460 | let m = match-options(opt)
461 | option.text += m.text
462 | option.bg += m.bg
463 | if m.reverse != none { option.reverse = m.reverse }
464 | if option.reverse { (option.text.fill, option.bg.fill) = (option.bg.fill, option.text.fill) }
465 | if m.ul != none { option.ul = m.ul }
466 | if m.ol != none { option.ol = m.ol }
467 |
468 | // work around for trailing whitespace with under/overline
469 | str = str.replace(regex("([ \t]+)$"), m => m.captures.at(0) + "\u{200b}")
470 | {
471 | show: box.with(..option.bg)
472 | show: text.with(..option.text)
473 | show: c => if option.ul {
474 | underline(c)
475 | } else {
476 | c
477 | }
478 | show: c => if option.ol {
479 | overline(c)
480 | } else {
481 | c
482 | }
483 | [#str]
484 | }
485 | // fill trailing newlines
486 | let s = str.find(regex("\n+$"))
487 | if s != none {
488 | for i in s {
489 | linebreak()
490 | }
491 | }
492 | }
493 | }
494 |
--------------------------------------------------------------------------------