diff --git a/packages/preview/codly/1.3.1/.gitignore b/packages/preview/codly/1.3.1/.gitignore
new file mode 100644
index 0000000000..77cfdcc281
--- /dev/null
+++ b/packages/preview/codly/1.3.1/.gitignore
@@ -0,0 +1,8 @@
+# added by typst-test
+tests/*/out/**
+tests/*/diff/**
+test*.png
+record-*.json
+_*@bench/
+report/
+.DS_STORE
\ No newline at end of file
diff --git a/packages/preview/codly/1.3.1/LICENSE b/packages/preview/codly/1.3.1/LICENSE
new file mode 100644
index 0000000000..f4687cbe64
--- /dev/null
+++ b/packages/preview/codly/1.3.1/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) [2023] [Sébastien d'Herbais de Thun, et al.]
+
+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.
\ No newline at end of file
diff --git a/packages/preview/codly/1.3.1/README.md b/packages/preview/codly/1.3.1/README.md
new file mode 100644
index 0000000000..cc8dc5234d
--- /dev/null
+++ b/packages/preview/codly/1.3.1/README.md
@@ -0,0 +1,239 @@
+# Codly: simple yet beautiful and powerful code blocks
+
+
+
+
+
+
+
+
+
+
+
+Codly superchargescode blocks for your Typst documents. It allows you to add annotations, skip lines, customize numberings, add language icons, and much more. Codly is even better with its companion package [`codly-languages`](https://typst.app/universe/package/codly-languages) which provides a wide range of language icons and colors to choose from.
+
+A full set of documentation can be found [in the repo](https://raw.githubusercontent.com/Dherse/codly/main/docs.pdf).
+
+
+
+````typ
+#import "@preview/codly:1.3.1": *
+#import "@preview/codly-languages:0.1.1": *
+#show: codly-init.with()
+
+#codly(languages: codly-languages)
+```rust
+pub fn main() {
+ println!("Hello, world!");
+}
+```
+````
+
+### Setup
+
+To start using codly, you need to initialize codly using a show rule, this need only be done once per document:
+
+```typ
+#show: codly-init.with()
+```
+
+Then you *can* configure codly with your parameters:
+
+```typ
+#codly(
+ languages: (
+ rust: (name: "Rust", icon: "🦀", color: rgb("#CE412B")),
+ )
+)
+```
+
+---
+**Note**: Any parameter that you leave blank will use the previous values (or the default value if never set) similar to a `set` rule in regular typst. But the changes are always global unless you use the provided `codly.local` function. To get a full list of all settings, see the [documentation](https://raw.githubusercontent.com/Dherse/codly/main/docs.pdf).
+
+---
+
+Then you just need to add a code block and it will be automatically displayed correctly:
+
+````
+```rust
+pub fn main() {
+ println!("Hello, world!");
+}
+```
+````
+
+
+
+### Disabling & Enabling
+
+To locally disable codly, you can just do the following, you can then later re-enable it using the `codly` configuration function.
+
+```typ
+#codly-disable()
+```
+
+
+
+Alternatively, you can use the `no-codly` function to achieve the same effect locally:
+
+````typ
+#no-codly[
+ ```typ
+ I will be displayed using the normal raw blocks.
+ ```
+]
+````
+
+### Smart indentation
+
+By default Codly ships with `smart-indent` enabled, this means that Codly will automatically detect the indentation of your code block and adjust the horizontal offset on line wrapping accordingly. This can be disabled using the `smart-indent` parameter.
+
+```typ
+#codly(smart-indent: false)
+```
+
+
+
+### Referencing code blocks
+
+Codly offers a wide range of features for referencing code blocks, lines, highlights, and annotations. This is done using:
+- the line shorthand `@:`
+- the highlight or annotation label `@`
+
+
+
+
+
+### Setting an offset
+
+If you wish to add an offset to your code block, but without selecting a subset of lines, you can use the `codly-offset` function:
+
+```typ
+// Sets a 5 line offset
+#codly-offset(5)
+```
+
+
+
+### Setting an offset relative to another code block
+
+This is done by using the `offset-from` argument and by specifying a Typst `label` to the "parent" code block:
+
+````typ
+#codly(offset-from: )
+````
+
+
+
+### Selecting a subset of lines
+
+If you wish to select a subset of lines, you can use the `codly-range` function. By setting the start to 1 and the end to `none` you can select all lines from the start to the end of the code block.
+
+```typ
+#codly-range(start: 5, end: 10)
+```
+
+
+
+### Adding a "skip"
+
+You can add a "fake" skip between lines using the `skips` parameters:
+
+```typ
+// Before the 5th line (indexing start at 0), insert a 32 line jump.
+#codly(skips: ((5, 32), ))
+```
+The code inside your block will be the same (except for the added line containing the … character), but the line numbers will be adjusted to reflect the skip.
+
+This can be customized using the `skip-line` and `skip-number` to customize what it looks like.
+
+### Adding highlights
+
+You can highlight part of lines using the `highlights` parameters:
+
+````typ
+#codly(highlights: (
+ (line: 4, start: 2, end: none, fill: red),
+ (line: 5, start: 13, end: 19, fill: green, tag: "(a)"),
+ (line: 5, start: 26, fill: blue, tag: "(b)"),
+))
+```py
+def fib(n):
+ if n <= 1:
+ return n
+ else:
+ return fib(n - 1) + fib(n - 2)
+print(fib(25))
+```
+````
+
+
+
+### Adding annotations
+
+You can annotate a line/group of lines using the `annotations` parameters :
+
+```typ
+// Add an annotation from the second line (0 indexing) to the 5th line included.
+#codly(
+ annotations: (
+ (
+ start: 2,
+ end: 4,
+ content: block(
+ width: 2em,
+ // Rotate the element to make it look nice
+ rotate(
+ -90deg,
+ align(center, box(width: 100pt)[Function body])
+ )
+ )
+ ),
+ )
+)
+```
+
+
+
+### Disabling line numbers
+
+You can configure this with the `codly` function:
+
+```typ
+#codly(number-format: none)
+```
+
+### Disabling zebra striping
+
+You disable zebra striping by setting the `zebra-fill` to white or none.
+
+```typ
+#codly(zebra-fill: none)
+```
+
+### Customize the stroke
+
+You can customize the stroke surrounding the figure using the `stroke` parameter of the `codly` function:
+
+```typ
+#codly(stroke: 1pt + red)
+```
+
+### Misc
+
+You can also disable the icon by setting the `display-icon` parameter to `false`:
+
+```typ
+#codly(display-icon: false)
+```
+
+This applies to:
+- the name
+- the radius
+- whether the block is breakable
+- the padding
+- the width of the numbers columns
+
+and so many more.
+
+For more detailed information check out the [documentation](https://raw.githubusercontent.com/Dherse/codly/main/docs.pdf).
\ No newline at end of file
diff --git a/packages/preview/codly/1.3.1/assets/annotations.png b/packages/preview/codly/1.3.1/assets/annotations.png
new file mode 100644
index 0000000000..67d3338345
Binary files /dev/null and b/packages/preview/codly/1.3.1/assets/annotations.png differ
diff --git a/packages/preview/codly/1.3.1/assets/codly-disable.png b/packages/preview/codly/1.3.1/assets/codly-disable.png
new file mode 100644
index 0000000000..8228cde581
Binary files /dev/null and b/packages/preview/codly/1.3.1/assets/codly-disable.png differ
diff --git a/packages/preview/codly/1.3.1/assets/codly-offset.png b/packages/preview/codly/1.3.1/assets/codly-offset.png
new file mode 100644
index 0000000000..0499d052cd
Binary files /dev/null and b/packages/preview/codly/1.3.1/assets/codly-offset.png differ
diff --git a/packages/preview/codly/1.3.1/assets/codly-range.png b/packages/preview/codly/1.3.1/assets/codly-range.png
new file mode 100644
index 0000000000..fbd17c1754
Binary files /dev/null and b/packages/preview/codly/1.3.1/assets/codly-range.png differ
diff --git a/packages/preview/codly/1.3.1/assets/crab.png b/packages/preview/codly/1.3.1/assets/crab.png
new file mode 100644
index 0000000000..8b56f69261
Binary files /dev/null and b/packages/preview/codly/1.3.1/assets/crab.png differ
diff --git a/packages/preview/codly/1.3.1/assets/demo.png b/packages/preview/codly/1.3.1/assets/demo.png
new file mode 100644
index 0000000000..9c541db3e3
Binary files /dev/null and b/packages/preview/codly/1.3.1/assets/demo.png differ
diff --git a/packages/preview/codly/1.3.1/assets/highlight-ref.png b/packages/preview/codly/1.3.1/assets/highlight-ref.png
new file mode 100644
index 0000000000..0d388f189c
Binary files /dev/null and b/packages/preview/codly/1.3.1/assets/highlight-ref.png differ
diff --git a/packages/preview/codly/1.3.1/assets/highlights.png b/packages/preview/codly/1.3.1/assets/highlights.png
new file mode 100644
index 0000000000..4047fe876c
Binary files /dev/null and b/packages/preview/codly/1.3.1/assets/highlights.png differ
diff --git a/packages/preview/codly/1.3.1/assets/line-ref.png b/packages/preview/codly/1.3.1/assets/line-ref.png
new file mode 100644
index 0000000000..cf0d85164d
Binary files /dev/null and b/packages/preview/codly/1.3.1/assets/line-ref.png differ
diff --git a/packages/preview/codly/1.3.1/assets/offset-from.png b/packages/preview/codly/1.3.1/assets/offset-from.png
new file mode 100644
index 0000000000..6b37f0055b
Binary files /dev/null and b/packages/preview/codly/1.3.1/assets/offset-from.png differ
diff --git a/packages/preview/codly/1.3.1/assets/smart-indent.png b/packages/preview/codly/1.3.1/assets/smart-indent.png
new file mode 100644
index 0000000000..f100305108
Binary files /dev/null and b/packages/preview/codly/1.3.1/assets/smart-indent.png differ
diff --git a/packages/preview/codly/1.3.1/codly.typ b/packages/preview/codly/1.3.1/codly.typ
new file mode 100644
index 0000000000..39c18ab74f
--- /dev/null
+++ b/packages/preview/codly/1.3.1/codly.typ
@@ -0,0 +1,607 @@
+#import "src/lib.typ": (
+ codly-init,
+ codly-reset,
+ no-codly,
+ yes-codly,
+ codly-enable,
+ codly-disable,
+ codly-range,
+ codly-offset,
+ codly-skip,
+ typst-icon,
+)
+
+#let __codly-default = context [ Codly Default ]
+
+/// See the full documentation: https://raw.githubusercontent.com/Dherse/codly/main/docs.pdf
+///
+/// - enabled (bool, function): enabled
+/// - header (content, none, function): header
+/// - header-repeat (bool, function): header-repeat
+/// - header-cell-args (array, dictionary, arguments, function): header-cell-args
+/// - header-transform (function): header-transform
+/// - footer (content, none, function): footer
+/// - footer-repeat (bool, function): footer-repeat
+/// - footer-cell-args (array, dictionary, arguments, function): footer-cell-args
+/// - footer-transform (function): footer-transform
+/// - offset (int, function): offset
+/// - offset-from (none, label, function): offset-from
+/// - range (none, array, function): range
+/// - ranges (none, array, function): ranges
+/// - smart-skip (bool, dictionary, function): smart-skip
+/// - languages (dictionary, function): languages
+/// - default-color (color, gradient, tiling, function): default-color
+/// - radius (length, function): radius
+/// - inset (length, dictionary, function): inset
+/// - fill (none, color, gradient, tiling, function): fill
+/// - zebra-fill (none, color, gradient, tiling, function): zebra-fill
+/// - stroke (none, stroke, function): stroke
+/// - lang-inset (length, dictionary, function): lang-inset
+/// - lang-outset (dictionary, function): lang-outset
+/// - lang-radius (length, function): lang-radius
+/// - lang-stroke (none, stroke, function): lang-stroke
+/// - lang-fill (none, color, gradient, tiling, function): lang-fill
+/// - lang-format (auto, none, function): lang-format
+/// - display-name (bool, function): display-name
+/// - display-icon (bool, function): display-icon
+/// - filename (str, content, none, function): filename
+/// - filename-separator (str, content, function): filename-separator
+/// - number-format (function, none): number-format
+/// - number-align (alignment, function): number-align
+/// - number-placement (str): number-placement
+/// - smart-indent (bool): smart-indent
+/// - skip-last-empty (bool, function): skip-last-empty
+/// - breakable (bool): breakable
+/// - skips (array, none, function): skips
+/// - skip-line (content, none, function): skip-line
+/// - skip-number (content, none, function): skip-number
+/// - annotations (array, none, function): annotations
+/// - annotation-format (none, function): annotation-format
+/// - highlighted-lines (array, none, function): highlighted-lines
+/// - highlighted-default-color (color, tiling, gradient, function): highlighted-default-color
+/// - highlights (array, none, function): highlights
+/// - highlight-radius (length, function): highlight-radius
+/// - highlight-fill (function): highlight-fill
+/// - highlight-stroke (stroke, function): highlight-stroke
+/// - highlight-inset (length, dictionary, function): highlight-inset
+/// - highlight-outset (length, dictionary, function): highlight-outset
+/// - highlight-clip (bool, function): highlight-clip
+/// - reference-by (str, function): reference-by
+/// - reference-sep (str, content, function): reference-sep
+/// - reference-number-format (function): reference-number-format
+/// - aliases (dictionary): aliases
+/// -> content
+#let codly(
+ enabled: __codly-default,
+ header: __codly-default,
+ header-repeat: __codly-default,
+ header-cell-args: __codly-default,
+ header-transform: __codly-default,
+ footer: __codly-default,
+ footer-repeat: __codly-default,
+ footer-cell-args: __codly-default,
+ footer-transform: __codly-default,
+ offset: __codly-default,
+ offset-from: __codly-default,
+ range: __codly-default,
+ ranges: __codly-default,
+ smart-skip: __codly-default,
+ languages: __codly-default,
+ default-color: __codly-default,
+ radius: __codly-default,
+ inset: __codly-default,
+ fill: __codly-default,
+ zebra-fill: __codly-default,
+ stroke: __codly-default,
+ lang-inset: __codly-default,
+ lang-outset: __codly-default,
+ lang-radius: __codly-default,
+ lang-stroke: __codly-default,
+ lang-fill: __codly-default,
+ lang-format: __codly-default,
+ display-name: __codly-default,
+ display-icon: __codly-default,
+ filename: __codly-default,
+ filename-separator: __codly-default,
+ number-format: __codly-default,
+ number-align: __codly-default,
+ number-placement: __codly-default,
+ smart-indent: __codly-default,
+ skip-last-empty: __codly-default,
+ breakable: __codly-default,
+ skips: __codly-default,
+ skip-line: __codly-default,
+ skip-number: __codly-default,
+ annotations: __codly-default,
+ annotation-format: __codly-default,
+ highlighted-lines: __codly-default,
+ highlighted-default-color: __codly-default,
+ highlights: __codly-default,
+ highlight-radius: __codly-default,
+ highlight-fill: __codly-default,
+ highlight-stroke: __codly-default,
+ highlight-inset: __codly-default,
+ highlight-outset: __codly-default,
+ highlight-clip: __codly-default,
+ reference-by: __codly-default,
+ reference-sep: __codly-default,
+ reference-number-format: __codly-default,
+ aliases: __codly-default,
+) = {
+ import "src/lib.typ": __codly-inner
+ let out = (:)
+ if enabled != __codly-default {
+ out.insert("enabled", enabled)
+ }
+ if header != __codly-default {
+ out.insert("header", header)
+ }
+ if header-repeat != __codly-default {
+ out.insert("header-repeat", header-repeat)
+ }
+ if header-cell-args != __codly-default {
+ out.insert("header-cell-args", header-cell-args)
+ }
+ if header-transform != __codly-default {
+ out.insert("header-transform", header-transform)
+ }
+ if footer != __codly-default {
+ out.insert("footer", footer)
+ }
+ if footer-repeat != __codly-default {
+ out.insert("footer-repeat", footer-repeat)
+ }
+ if footer-cell-args != __codly-default {
+ out.insert("footer-cell-args", footer-cell-args)
+ }
+ if footer-transform != __codly-default {
+ out.insert("footer-transform", footer-transform)
+ }
+ if offset != __codly-default {
+ out.insert("offset", offset)
+ }
+ if offset-from != __codly-default {
+ out.insert("offset-from", offset-from)
+ }
+ if range != __codly-default {
+ out.insert("range", range)
+ }
+ if ranges != __codly-default {
+ out.insert("ranges", ranges)
+ }
+ if smart-skip != __codly-default {
+ out.insert("smart-skip", smart-skip)
+ }
+ if languages != __codly-default {
+ out.insert("languages", languages)
+ }
+ if default-color != __codly-default {
+ out.insert("default-color", default-color)
+ }
+ if radius != __codly-default {
+ out.insert("radius", radius)
+ }
+ if inset != __codly-default {
+ out.insert("inset", inset)
+ }
+ if fill != __codly-default {
+ out.insert("fill", fill)
+ }
+ if zebra-fill != __codly-default {
+ out.insert("zebra-fill", zebra-fill)
+ }
+ if stroke != __codly-default {
+ out.insert("stroke", stroke)
+ }
+ if lang-inset != __codly-default {
+ out.insert("lang-inset", lang-inset)
+ }
+ if lang-outset != __codly-default {
+ out.insert("lang-outset", lang-outset)
+ }
+ if lang-radius != __codly-default {
+ out.insert("lang-radius", lang-radius)
+ }
+ if lang-stroke != __codly-default {
+ out.insert("lang-stroke", lang-stroke)
+ }
+ if lang-fill != __codly-default {
+ out.insert("lang-fill", lang-fill)
+ }
+ if lang-format != __codly-default {
+ out.insert("lang-format", lang-format)
+ }
+ if display-name != __codly-default {
+ out.insert("display-name", display-name)
+ }
+ if display-icon != __codly-default {
+ out.insert("display-icon", display-icon)
+ }
+ if filename != __codly-default {
+ out.insert("filename", filename)
+ }
+ if filename-separator != __codly-default {
+ out.insert("filename-separator", filename-separator)
+ }
+ if number-format != __codly-default {
+ out.insert("number-format", number-format)
+ }
+ if number-align != __codly-default {
+ out.insert("number-align", number-align)
+ }
+ if number-placement != __codly-default {
+ out.insert("number-placement", number-placement)
+ }
+ if smart-indent != __codly-default {
+ out.insert("smart-indent", smart-indent)
+ }
+ if skip-last-empty != __codly-default {
+ out.insert("skip-last-empty", skip-last-empty)
+ }
+ if breakable != __codly-default {
+ out.insert("breakable", breakable)
+ }
+ if skips != __codly-default {
+ out.insert("skips", skips)
+ }
+ if skip-line != __codly-default {
+ out.insert("skip-line", skip-line)
+ }
+ if skip-number != __codly-default {
+ out.insert("skip-number", skip-number)
+ }
+ if annotations != __codly-default {
+ out.insert("annotations", annotations)
+ }
+ if annotation-format != __codly-default {
+ out.insert("annotation-format", annotation-format)
+ }
+ if highlighted-lines != __codly-default {
+ out.insert("highlighted-lines", highlighted-lines)
+ }
+ if highlighted-default-color != __codly-default {
+ out.insert("highlighted-default-color", highlighted-default-color)
+ }
+ if highlights != __codly-default {
+ out.insert("highlights", highlights)
+ }
+ if highlight-radius != __codly-default {
+ out.insert("highlight-radius", highlight-radius)
+ }
+ if highlight-fill != __codly-default {
+ out.insert("highlight-fill", highlight-fill)
+ }
+ if highlight-stroke != __codly-default {
+ out.insert("highlight-stroke", highlight-stroke)
+ }
+ if highlight-inset != __codly-default {
+ out.insert("highlight-inset", highlight-inset)
+ }
+ if highlight-outset != __codly-default {
+ out.insert("highlight-outset", highlight-outset)
+ }
+ if highlight-clip != __codly-default {
+ out.insert("highlight-clip", highlight-clip)
+ }
+ if reference-by != __codly-default {
+ out.insert("reference-by", reference-by)
+ }
+ if reference-sep != __codly-default {
+ out.insert("reference-sep", reference-sep)
+ }
+ if reference-number-format != __codly-default {
+ out.insert("reference-number-format", reference-number-format)
+ }
+ if aliases != __codly-default {
+ out.insert("aliases", aliases)
+ }
+
+ __codly-inner(..out)
+}
+
+/// Allows setting codly setting locally.
+/// Anything that happens inside the block will have the settings applied only to it.
+/// The pre-existing settings will be restored after the block. This is useful
+/// if you want to apply settings to a specific block only.
+///
+/// *Special:*
+/// #local(default-color: red)[
+/// ```
+/// Hello, world!
+/// ```
+/// ]
+///
+/// *Normal:*
+/// ```
+/// Hello, world!
+/// ```
+///
+/// See the full documentation: https://raw.githubusercontent.com/Dherse/codly/main/docs.pdf
+///
+/// - body (content): the content to be locally styled
+/// - nested (bool): whether to enable nested local states
+/// - enabled (bool, function): enabled
+/// - header (content, none, function): header
+/// - header-repeat (bool, function): header-repeat
+/// - header-cell-args (array, dictionary, arguments, function): header-cell-args
+/// - header-transform (function): header-transform
+/// - footer (content, none, function): footer
+/// - footer-repeat (bool, function): footer-repeat
+/// - footer-cell-args (array, dictionary, arguments, function): footer-cell-args
+/// - footer-transform (function): footer-transform
+/// - offset (int, function): offset
+/// - offset-from (none, label, function): offset-from
+/// - range (none, array, function): range
+/// - ranges (none, array, function): ranges
+/// - smart-skip (bool, dictionary, function): smart-skip
+/// - languages (dictionary, function): languages
+/// - default-color (color, gradient, tiling, function): default-color
+/// - radius (length, function): radius
+/// - inset (length, dictionary, function): inset
+/// - fill (none, color, gradient, tiling, function): fill
+/// - zebra-fill (none, color, gradient, tiling, function): zebra-fill
+/// - stroke (none, stroke, function): stroke
+/// - lang-inset (length, dictionary, function): lang-inset
+/// - lang-outset (dictionary, function): lang-outset
+/// - lang-radius (length, function): lang-radius
+/// - lang-stroke (none, stroke, function): lang-stroke
+/// - lang-fill (none, color, gradient, tiling, function): lang-fill
+/// - lang-format (auto, none, function): lang-format
+/// - display-name (bool, function): display-name
+/// - display-icon (bool, function): display-icon
+/// - filename (str, content, none, function): filename
+/// - filename-separator (str, content, function): filename-separator
+/// - number-format (function, none): number-format
+/// - number-align (alignment, function): number-align
+/// - number-placement (str): number-placement
+/// - smart-indent (bool): smart-indent
+/// - skip-last-empty (bool, function): skip-last-empty
+/// - breakable (bool): breakable
+/// - skips (array, none, function): skips
+/// - skip-line (content, none, function): skip-line
+/// - skip-number (content, none, function): skip-number
+/// - annotations (array, none, function): annotations
+/// - annotation-format (none, function): annotation-format
+/// - highlighted-lines (array, none, function): highlighted-lines
+/// - highlighted-default-color (color, tiling, gradient, function): highlighted-default-color
+/// - highlights (array, none, function): highlights
+/// - highlight-radius (length, function): highlight-radius
+/// - highlight-fill (function): highlight-fill
+/// - highlight-stroke (stroke, function): highlight-stroke
+/// - highlight-inset (length, dictionary, function): highlight-inset
+/// - highlight-outset (length, dictionary, function): highlight-outset
+/// - highlight-clip (bool, function): highlight-clip
+/// - reference-by (str, function): reference-by
+/// - reference-sep (str, content, function): reference-sep
+/// - reference-number-format (function): reference-number-format
+/// - aliases (dictionary): aliases
+/// -> content
+#let local(
+ body,
+ nested: false,
+ enabled: __codly-default,
+ header: __codly-default,
+ header-repeat: __codly-default,
+ header-cell-args: __codly-default,
+ header-transform: __codly-default,
+ footer: __codly-default,
+ footer-repeat: __codly-default,
+ footer-cell-args: __codly-default,
+ footer-transform: __codly-default,
+ offset: __codly-default,
+ offset-from: __codly-default,
+ range: __codly-default,
+ ranges: __codly-default,
+ smart-skip: __codly-default,
+ languages: __codly-default,
+ default-color: __codly-default,
+ radius: __codly-default,
+ inset: __codly-default,
+ fill: __codly-default,
+ zebra-fill: __codly-default,
+ stroke: __codly-default,
+ lang-inset: __codly-default,
+ lang-outset: __codly-default,
+ lang-radius: __codly-default,
+ lang-stroke: __codly-default,
+ lang-fill: __codly-default,
+ lang-format: __codly-default,
+ display-name: __codly-default,
+ display-icon: __codly-default,
+ filename: __codly-default,
+ filename-separator: __codly-default,
+ number-format: __codly-default,
+ number-align: __codly-default,
+ number-placement: __codly-default,
+ smart-indent: __codly-default,
+ skip-last-empty: __codly-default,
+ breakable: __codly-default,
+ skips: __codly-default,
+ skip-line: __codly-default,
+ skip-number: __codly-default,
+ annotations: __codly-default,
+ annotation-format: __codly-default,
+ highlighted-lines: __codly-default,
+ highlighted-default-color: __codly-default,
+ highlights: __codly-default,
+ highlight-radius: __codly-default,
+ highlight-fill: __codly-default,
+ highlight-stroke: __codly-default,
+ highlight-inset: __codly-default,
+ highlight-outset: __codly-default,
+ highlight-clip: __codly-default,
+ reference-by: __codly-default,
+ reference-sep: __codly-default,
+ reference-number-format: __codly-default,
+ aliases: __codly-default,
+) = {
+ import "src/lib.typ": __local-inner
+ let out = (:)
+ if enabled != __codly-default {
+ out.insert("enabled", enabled)
+ }
+ if header != __codly-default {
+ out.insert("header", header)
+ }
+ if header-repeat != __codly-default {
+ out.insert("header-repeat", header-repeat)
+ }
+ if header-cell-args != __codly-default {
+ out.insert("header-cell-args", header-cell-args)
+ }
+ if header-transform != __codly-default {
+ out.insert("header-transform", header-transform)
+ }
+ if footer != __codly-default {
+ out.insert("footer", footer)
+ }
+ if footer-repeat != __codly-default {
+ out.insert("footer-repeat", footer-repeat)
+ }
+ if footer-cell-args != __codly-default {
+ out.insert("footer-cell-args", footer-cell-args)
+ }
+ if footer-transform != __codly-default {
+ out.insert("footer-transform", footer-transform)
+ }
+ if offset != __codly-default {
+ out.insert("offset", offset)
+ }
+ if offset-from != __codly-default {
+ out.insert("offset-from", offset-from)
+ }
+ if range != __codly-default {
+ out.insert("range", range)
+ }
+ if ranges != __codly-default {
+ out.insert("ranges", ranges)
+ }
+ if smart-skip != __codly-default {
+ out.insert("smart-skip", smart-skip)
+ }
+ if languages != __codly-default {
+ out.insert("languages", languages)
+ }
+ if default-color != __codly-default {
+ out.insert("default-color", default-color)
+ }
+ if radius != __codly-default {
+ out.insert("radius", radius)
+ }
+ if inset != __codly-default {
+ out.insert("inset", inset)
+ }
+ if fill != __codly-default {
+ out.insert("fill", fill)
+ }
+ if zebra-fill != __codly-default {
+ out.insert("zebra-fill", zebra-fill)
+ }
+ if stroke != __codly-default {
+ out.insert("stroke", stroke)
+ }
+ if lang-inset != __codly-default {
+ out.insert("lang-inset", lang-inset)
+ }
+ if lang-outset != __codly-default {
+ out.insert("lang-outset", lang-outset)
+ }
+ if lang-radius != __codly-default {
+ out.insert("lang-radius", lang-radius)
+ }
+ if lang-stroke != __codly-default {
+ out.insert("lang-stroke", lang-stroke)
+ }
+ if lang-fill != __codly-default {
+ out.insert("lang-fill", lang-fill)
+ }
+ if lang-format != __codly-default {
+ out.insert("lang-format", lang-format)
+ }
+ if display-name != __codly-default {
+ out.insert("display-name", display-name)
+ }
+ if display-icon != __codly-default {
+ out.insert("display-icon", display-icon)
+ }
+ if filename != __codly-default {
+ out.insert("filename", filename)
+ }
+ if filename-separator != __codly-default {
+ out.insert("filename-separator", filename-separator)
+ }
+ if number-format != __codly-default {
+ out.insert("number-format", number-format)
+ }
+ if number-align != __codly-default {
+ out.insert("number-align", number-align)
+ }
+ if number-placement != __codly-default {
+ out.insert("number-placement", number-placement)
+ }
+ if smart-indent != __codly-default {
+ out.insert("smart-indent", smart-indent)
+ }
+ if skip-last-empty != __codly-default {
+ out.insert("skip-last-empty", skip-last-empty)
+ }
+ if breakable != __codly-default {
+ out.insert("breakable", breakable)
+ }
+ if skips != __codly-default {
+ out.insert("skips", skips)
+ }
+ if skip-line != __codly-default {
+ out.insert("skip-line", skip-line)
+ }
+ if skip-number != __codly-default {
+ out.insert("skip-number", skip-number)
+ }
+ if annotations != __codly-default {
+ out.insert("annotations", annotations)
+ }
+ if annotation-format != __codly-default {
+ out.insert("annotation-format", annotation-format)
+ }
+ if highlighted-lines != __codly-default {
+ out.insert("highlighted-lines", highlighted-lines)
+ }
+ if highlighted-default-color != __codly-default {
+ out.insert("highlighted-default-color", highlighted-default-color)
+ }
+ if highlights != __codly-default {
+ out.insert("highlights", highlights)
+ }
+ if highlight-radius != __codly-default {
+ out.insert("highlight-radius", highlight-radius)
+ }
+ if highlight-fill != __codly-default {
+ out.insert("highlight-fill", highlight-fill)
+ }
+ if highlight-stroke != __codly-default {
+ out.insert("highlight-stroke", highlight-stroke)
+ }
+ if highlight-inset != __codly-default {
+ out.insert("highlight-inset", highlight-inset)
+ }
+ if highlight-outset != __codly-default {
+ out.insert("highlight-outset", highlight-outset)
+ }
+ if highlight-clip != __codly-default {
+ out.insert("highlight-clip", highlight-clip)
+ }
+ if reference-by != __codly-default {
+ out.insert("reference-by", reference-by)
+ }
+ if reference-sep != __codly-default {
+ out.insert("reference-sep", reference-sep)
+ }
+ if reference-number-format != __codly-default {
+ out.insert("reference-number-format", reference-number-format)
+ }
+ if aliases != __codly-default {
+ out.insert("aliases", aliases)
+ }
+
+ __local-inner(body, nested: nested, ..out)
+}
diff --git a/packages/preview/codly/1.3.1/docs.pdf b/packages/preview/codly/1.3.1/docs.pdf
new file mode 100644
index 0000000000..915cbe493c
Binary files /dev/null and b/packages/preview/codly/1.3.1/docs.pdf differ
diff --git a/packages/preview/codly/1.3.1/src/args.json b/packages/preview/codly/1.3.1/src/args.json
new file mode 100644
index 0000000000..7ed868ca49
--- /dev/null
+++ b/packages/preview/codly/1.3.1/src/args.json
@@ -0,0 +1,460 @@
+{
+ "enabled": {
+ "title": "Enabled",
+ "description": "Whether codly is enabled or not.\nIf it is disabled, the code block will be displayed as a normal code block, without any additional codly-specific formatting. This is useful if you want to disable codly for a specific block. You can also disable codly locally using the #link()[`no-codly`] function, or disable it and enable it again using the #link()[`codly-disable`] and #link()[`codly-enable`] functions.",
+ "default": "true",
+ "ty": [ "bool" ],
+ "function": true,
+ "reset": false,
+ "example": "*Enabled = true*:\n#codly(enabled: true)\n```typ\nHello, world!\n``` \n \n*Enabled = false*:\n#codly(enabled: false)\n```typ\nHello, world!\n```"
+ },
+ "header": {
+ "title": "Header",
+ "description": "An optional header to display above the code block. It can be optionally repeated on all subsequent pages with the #link()[`header-repeat`] argument. And additional customizations are available with the #link()[`header-cell-args`] and #link()[`header-transform`] arguments.",
+ "default": "none",
+ "ty": [ "content", "type(none)" ],
+ "function": true,
+ "reset": true,
+ "example": "#codly(header: [*Hello, world!*])\n```typ\nHello, world!\n```"
+ },
+ "header-repeat": {
+ "title": "Header Repeat",
+ "description": "Whether to repeat the header on each page. This is only applicable if a header is provided, if the code block is #link()[`breakable`], and if it actually breaks on more than one page. For more information see #link(\"https://typst.app/docs/reference/layout/grid/#definitions-header-repeat\")[`grid.header:repeat`].",
+ "default": "false",
+ "ty": [ "bool" ],
+ "function": true
+ },
+ "header-cell-args": {
+ "title": "Header Cell Args",
+ "description": "Additional arguments to be provided to the `grid.cell` containing the header. Lets you customize the header cell further. Internally, codly wraps the content of the #link()[`header`] argument in a `grid.cell` with these arguments. The only argument that is always common is the `body` argument which is the value of the #link()[`header`] argument, and the `colspan` which is always set to `2`.\n\n For a full description of the argument, look at the documentation of the #link(\"https://typst.app/docs/reference/layout/grid/#definitions-cell\")[`grid.cell`] function.",
+ "default": "()",
+ "ty": [ "array", "dictionary", "arguments" ],
+ "function": true,
+ "example": "//Centering the header:\n#codly(\n header: [*Hello, world!*],\n header-cell-args: (align: center, )\n)\n\n```typ\nHello, world!\n```"
+ },
+ "header-transform": {
+ "title": "Header Transform",
+ "description": "Function that transforms the header into arbitrary content to be stored in the `grid.cell`. Can be seen as a show-rule for the header. This allows to perform global transformation/show-rule like operations on the header.",
+ "default": "(x) => x",
+ "ty": [ "function" ],
+ "function": false,
+ "example": "//Making the header bold and blue:\n#codly(\n header: [Hello, world!],\n header-transform: (x) => {\n set text(fill: blue)\n strong(x)\n }\n)\n\n```typ\nHello, world!\n```"
+ },
+ "footer": {
+ "title": "Footer",
+ "default": "none",
+ "ty": [ "content", "type(none)" ],
+ "function": true,
+ "reset": true,
+ "description": "An optional footer to display below the code block. See #link()[`header`] for more information.",
+ "example": "#codly(footer: [*Hello, world!*])\n```typ\nHello, world!\n```"
+ },
+ "footer-repeat": {
+ "title": "Footer Repeat",
+ "description": "Whether to repeat the footer on each page. See #link()[`header-repeat`] for more information.",
+ "default": "false",
+ "ty": [ "bool" ],
+ "function": true
+ },
+ "footer-cell-args": {
+ "title": "Footer Cell Args",
+ "description": "Additional arguments to be provided to the `grid.cell` containing the footer. See #link()[`header-cell-args`] for more information.",
+ "default": "()",
+ "ty": [ "array", "dictionary", "arguments" ],
+ "function": true,
+ "example": "//Centering the footer:\n#codly(\n footer: [*Hello, world!*],\n footer-cell-args: (align: center, )\n)\n\n```typ\nHello, world!\n```"
+ },
+ "footer-transform": {
+ "title": "Footer Transform",
+ "description": "Function that transforms the footer into arbitrary content to be stored in the `grid.cell`. Can be seen as a show-rule for the footer. See #link()[`header-transform`] for more information.",
+ "default": "(x) => x",
+ "ty": [ "function" ],
+ "function": false,
+ "example": "//Making the footer bold and blue:\n#codly(\n footer: [Hello, world!],\n footer-transform: (x) => {\n set text(fill: blue)\n strong(x)\n }\n)\n\n```typ\nHello, world!\n```"
+ },
+ "offset": {
+ "title": "Offset",
+ "description": "The offset to apply to line numbers.\n\n This is purely cosmetic, only impacting the shown line numbers in the final output.",
+ "default": "0",
+ "ty": [ "int" ],
+ "function": true,
+ "reset": true,
+ "example": "*No offset:*\n```typ\nHello, world!\n```\n\n*Offset by 5:*\n#codly(offset: 5)\n```typ\nHello, world!\n```"
+ },
+ "offset-from": {
+ "title": "Offset from other code block",
+ "description": "The offset to apply to line numbers, relative to another code block. This is useful when you want to match line numbers between two code blocks. This code block will continue the line numbers from the other code block, with the specified offset.\n\n This is done by giving a `label` to the parent raw block, and then setting it as the `offset-from` on the second code block.\n#info[Note that the offset obtained from the other code block is added to the offset specified in the `offset` argument.]\n#warning[*Important*: this feature works with any `offset` set on the other code block, including `offset-from` but may give unexpected results if both code blocks have `offset-from` set to each other or if the preceeding code block has #link()[`range`] or #link()[`skips`] set.]",
+ "default": "none",
+ "ty": [ "type(none)", "label" ],
+ "experimental": true,
+ "function": true,
+ "reset": true,
+ "example": "```py\ndef fib(n):\n if n <= 1:\n return n\n return fib(n - 1) + fib(n - 2)\n``` \n\n*Will continue at line 5*\n#codly(offset-from: )\n```py\nfib(25)\n```"
+ },
+ "range": {
+ "title": "Range",
+ "description": "The range of line numbers to display, one-indexed. If set to `none`, all lines are displayed. Can also be achieved using the convenience function #link()[`codly-range`]. If set to `none`, all lines are displayed.#one-indexed",
+ "default": "none",
+ "ty": [ "type(none)", "array"],
+ "function": true,
+ "reset": true,
+ "example": "#codly(range: (2, 4))\n```py\ndef fib(n):\n if n <= 1:\n return n\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```"
+ },
+ "ranges": {
+ "title": "Ranges",
+ "description": "The ranges of line numbers to display, one-indexed. If set to `none`, all lines are displayed. Can also be achieved using the convenience function #link()[`codly-range`] if provided with more than one range. If set to `none`, all lines are displayed. Note that it override the #link()[`range`] argument. #one-indexed",
+ "default": "none",
+ "ty": [ "type(none)", "array"],
+ "function": true,
+ "reset": true,
+ "example": "#codly(ranges: ((2, 2), (4, 4)))\n```py\ndef fib(n):\n if n <= 1:\n return n\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```"
+ },
+ "smart-skip": {
+ "title": "Smart skip",
+ "description": "Whether to automatically insert a #link()[`skips`] entry between ranges of displayed values. They must be discontinuous for a skip to be added. The skip will be the size of the discontinuity. It can also be a dictionary with the keys:\n- `first`: whether to include a skip if the start of the block is outside of the ranges\n- `last`: whether to include a skip if the end of the code block is outside of the ranges\n- `rest`: whether to include a skip for unspecified values and/or in the middle of the code block.\nYou can specify one or more of these keys, if the `rest` is not specified, it defaults to `none`.",
+ "default": "false",
+ "ty": [ "bool", "dictionary" ],
+ "function": true,
+ "reset": false,
+ "example": "#codly(\n smart-skip: true,\n ranges: ((2, 2), (4, 4))\n)\n```py\ndef fib(n):\n if n <= 1:\n return n\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```",
+ "examples": [
+ {
+ "title": "Using a dictionary",
+ "code": "#codly(\n smart-skip: (\n first: false,\n last: false,\n rest: true\n ),\n ranges: ((2, 2), (4, 4))\n)\n```py\ndef fib(n):\n if n <= 1:\n return n\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```"
+ }
+ ],
+ "experimental": true,
+ "upcoming": false
+ },
+ "languages": {
+ "title": "Languages",
+ "description": "The language definitions to use for language block formatting. It is defined as a dictionary where the keys are the language names andeach value is another dictionary containing the following keys:\n - `name`: the \"pretty\" name of the language as a content/showable value\n - `color`: the color of the language, if omitted uses the default color\n - `icon`: the icon of the language, if omitted no icon is shown.\nIf an entry is missing, and language blocks are enabled, will show the \"un-prettified\" language name, with the default color.",
+ "post": "=== Pre-existing language definitions\n#info[Check out the #link(\"https://typst.app/universe/package/codly-languages\")[`codly-languages`] package on Typst universe. It contains pre-definition for many language and is extremely easy to use. You can consider it officially endorsed by the codly author as of the 19th of November 2024.]\n\n#example(````typ\n#import \"@preview/codly-languages:0.1.7\": *\n#codly(languages: codly-languages)\n```rust\nfn main() {\n println!(\"Hello, world!\");\n}\n```\n```zig\nconst std = @import(\"std\");\n\npub fn main() void {\n std.debug.print(\"Hello, world!\", .{});\n}\n```\n````)",
+ "default": "(:)",
+ "ty": [ "dictionary" ],
+ "function": true,
+ "example":"#codly(\n languages: (\n py: (\n name: [Python], color: green, icon: \"🐍\"\n ),\n )\n)\n```py\ndef fib(n):\n if n <= 1:\n return n\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```"
+ },
+ "default-color": {
+ "title": "Default language color",
+ "description": "The default color to use for language blocks. Used when a language is not defined in the #link()[`languages`] argument. Also note that it is only used when the #link()[`lang-format`] is its `auto` or you are using it in a custom formatter. If you are using a custom formatter, it is passed to the formatter as a named argument `color`.",
+ "default": "rgb(\"#283593\")",
+ "ty": [ "color", "gradient", "tiling" ],
+ "function": true,
+ "example": "*Default color:*\n```py\nprint('Hello, world!')\nprint('Hello, world!')\n```\n*Overriden default color:*\n#codly(default-color: orange)\n```py\nprint('Hello, world!')\nprint('Hello, world!')\n```"
+ },
+ "radius": {
+ "title": "Radius",
+ "description": "The radius of the border of the code block, see #link(\"https://typst.app/docs/reference/layout/block/#parameters-radius\")[`block.radius`] for more information.",
+ "default": "0.32em",
+ "ty": [ "length" ],
+ "function": true,
+ "example": "*Default radius:*\n```py\nprint('Hello, world!')\nprint('Hello, world!')\n```\n*Overriden radius:*\n#codly(radius: 2em)\n```py\nprint('Hello, world!')\nprint('Hello, world!')\n```\n*Zero radius:*\n#codly(radius: 0pt)\n```py\nprint('Hello, world!')\nprint('Hello, world!')\n```"
+ },
+ "inset": {
+ "title": "Inset",
+ "description": "Inset of the code lines, that is the distance between the border and the code lines. It can also be a dictionary with the keys same keys as in the Tyspt built-in #link(\"https://typst.app/docs/reference/layout/block/#parameters-inset\")[`block.inset`].",
+ "default": "0.32em",
+ "ty": [ "length", "dictionary" ],
+ "function": true,
+ "example": "*Default inset:*\n```py\nprint('Hello, world!')\n```\n*Overriden inset:*\n#codly(inset: 1em)\n```py\nprint('Hello, world!')\n```",
+ "examples":[
+ {
+ "title": "Dictionary inset",
+ "code": "*Default inset:*\n```py\nprint('Hello, world!')\n```\n*Overriden inset:*\n#codly(inset: (x: 0.32em, y: 0.1em))\n```py\nprint('Hello, world!')\n```"
+ }
+ ]
+ },
+ "fill": {
+ "title": "Fill",
+ "description": "The fill of the code block when not zebra-striped.",
+ "default": "none",
+ "ty": [ "type(none)", "color", "gradient", "tiling" ],
+ "function": true,
+ "example": "*Default fill:*\n```py\nprint('Hello, world!')\nprint('Hello, world!')\n```\n*Overriden fill:*\n#codly(fill: gradient.linear(..color.map.flare))\n```py\nprint('Hello, world!')\nprint('Hello, world!')\n```\n*No fill:*\n#codly(fill: none)\n```py\nprint('Hello, world!')\nprint('Hello, world!')\n```"
+ },
+ "zebra-fill": {
+ "title": "Zebra fill",
+ "description": "Background color of the code lines when zebra-stripped. If set to `none`, no zebra-striping is applied.",
+ "default": "luma(240)",
+ "ty": [ "type(none)", "color", "gradient", "tiling" ],
+ "function": true,
+ "example": "*Default zebra:*\n```py\nprint('Hello, world!')\nprint('Hello, world!')\n```\n*No zebra:*\n#codly(zebra-fill: none)\n```py\nprint('Hello, world!')\nprint('Hello, world!')\n```\n*Overriden zebra:*\n#codly(zebra-fill: gradient.linear(..color.map.flare))\n```py\nprint('Hello, world!')\n```"
+ },
+ "stroke": {
+ "title": "Stroke",
+ "description": "The stroke to surround the whole code block with?",
+ "default": "1pt + luma(240)",
+ "ty": [ "type(none)", "stroke" ],
+ "function": true,
+ "example": "*Default stroke:*\n```py\nprint('Hello, world!')\n```\n*No stroke:*\n#codly(stroke: none)\n```py\nprint('Hello, world!')\n```\n*Overriden stroke:*\n#codly(stroke: 1pt + blue)\n```py\nprint('Hello, world!')\n```"
+ },
+ "lang-inset": {
+ "title": "Language box inset",
+ "description": "The inset of the language block. This only applies if you're using the default language block formatter. It can also be a dictionary with the keys same keys as in the Tyspt built-in #link(\"https://typst.app/docs/reference/layout/block/#parameters-inset\")[`block.inset`]",
+ "default": "0.32em",
+ "ty": [ "length", "dictionary" ],
+ "function": true,
+ "example": "#codly(lang-inset: 5pt)\n```py\nprint('Hello, world!')\nprint('Goodbye, world!')\n```"
+ },
+ "lang-outset": {
+ "title": "Language box outset",
+ "description": "The X and Y outset of the language block, applied as a `dx` and `dy` during the `place` operation. This applies in every case, whether or not you're using the default language block formatter. The default value is chosen to get rid of the `inset` applied to each line.",
+ "default": "(x: 0.32em, y: 0em)",
+ "ty": [ "dictionary" ],
+ "function": true,
+ "example": "#codly(lang-outset: (x: -10pt, y: 5pt))\n```py\nprint('Hello, world!')\nprint('Goodbye, world!')\n```"
+ },
+ "lang-radius": {
+ "title": "Language box radius",
+ "description": "The radius of the border of the language block.",
+ "default": "0.32em",
+ "ty": [ "length" ],
+ "function": true,
+ "example": "#codly(lang-radius: 10pt)\n```py\nprint('Hello, world!')\nprint('Goodbye, world!')\n```"
+ },
+ "lang-stroke": {
+ "title": "Language box stroke",
+ "description": "The stroke of the language block. Can be a function that takes in the language `dictionary` or `none` (see argument #link()[`languages`]) and returns a stroke.",
+ "default": "(lang) => lang.color + 0.5pt",
+ "ty": [ "type(none)", "stroke", "function" ],
+ "function": false,
+ "example": "*Fixed stroke:*\n#codly(lang-stroke: 1pt + red)\n```py\nprint('Hello, world!')\nprint('Goodbye, world!')\n```\n*Function mapping:*\n#codly(lang-stroke: (lang) => 2pt + lang.color)\n```py\nprint('Hello, world!')\nprint('Goodbye, world!')\n```"
+ },
+ "lang-fill": {
+ "title": "Language box fill",
+ "description": "The background color of the language block. Can be a function that takes in the language `dictionary` or `none` (see argument #link()[`languages`]) and returns a stroke.",
+ "default": "(lang) => lang.color.lighten(80%)",
+ "ty": [ "type(none)", "color", "gradient", "tiling", "function" ],
+ "function": false,
+ "example": "*Fixed fill:*\n#codly(lang-fill: red)\n```py\nprint('Hello, world!')\nprint('Goodbye, world!')\n```\n*Function mapping:*\n#codly(lang-fill: (lang) => lang.color.lighten(40%))\n```py\nprint('Hello, world!')\nprint('Goodbye, world!')\n```"
+ },
+ "lang-format": {
+ "title": "Language box formatter",
+ "description": "The formatter for the language block. A value of `none` will not display the language block. To use the default formatter, set to `auto`. The function takes three arguments:\n - `lang`: the language key (e.g. `py`)\n - `icon`: the language icon, can be none or empty content\n - `color`: the language color\n\n The function should return a content/showable value.\n#info[The language formatter should avoid using #link(\"https://typst.app/docs/reference/introspection/state/\")[`state`] as this can lead to quadratic execution time, see #link(\"https://github.com/typst/typst/issues/5220\")[typst/typst \\#5220] for more information. Internally, when set to `auto`, codly uses an inlined function to avoid using states.]",
+ "default": "auto",
+ "ty": [ "type(auto)", "type(none)", "function" ],
+ "function": false,
+ "example": "*Default formatter:*\n```py\nprint('Hello, world!')\n```\n*Function mapping:*\n#codly(lang-format: (_, _, _) => [No!]))\n```py\nprint('Hello, world!')\n```"
+ },
+ "display-name": {
+ "title": "Display language name",
+ "description": "Whether to display the name of the language in the language block. This only applies if you're using the default language block formatter.",
+ "default": "true",
+ "ty": [ "bool" ],
+ "function": true,
+ "example": "#codly(\n display-name: false,\n languages: (\n py: (\n name: [Python], color: green,\n icon: \"🐍\"\n ),\n ),\n)\n```py\nprint('Hello, world!')\nprint('Goodbye, world!')\n```"
+ },
+ "display-icon": {
+ "title": "Display language icon",
+ "description": "Whether to display the icon of the language in the language block. This only applies if you're using the default language block formatter.",
+ "default": "true",
+ "ty": [ "bool" ],
+ "function": true,
+ "example": "#codly(\n display-icon: false,\n languages: (\n py: (\n name: [Python], color: green,\n icon: \"🐍\"\n ),\n ),\n)\n```py\nprint('Hello, world!')\nprint('Goodbye, world!')\n```"
+ },
+ "number-format": {
+ "title": "Line number format",
+ "description": "The format of the line numbers, a function that takes in number and returns a content. If set to none, disables line numbers.",
+ "default": "numbering.with(\"1\")",
+ "ty": [ "function", "type(none)" ],
+ "function": false,
+ "example": "#codly(number-format: numbering.with(\"I.\"))\n```py\nprint('Hello, world!')\nprint('Goodbye, world!')\n```"
+ },
+ "number-align": {
+ "title": "Line number alignment",
+ "description": "The alignment of the numbers.",
+ "default": "left + horizon",
+ "ty": [ "alignment" ],
+ "function": true,
+ "example": "#codly(number-align: right + top)\n```py\n# Iterative Fibonacci\n# As opposed to the recursive\n# version\ndef fib(n):\n if n <= 1:\n return n\n last, current = 0, 1\n for _ in range(2, n + 1):\n last, current = current, last + current\n return current\nfib(25)\n```"
+ },
+ "number-placement": {
+ "title": "Line number placement",
+ "description": "Sets the placement of the numbers, either `\"inside\"` (the default) or `\"outside\"` of the code block.",
+ "default": "\"inside\"",
+ "ty": [ "str" ],
+ "function": false,
+ "example": "*Line number outside of the block:*\n#codly(number-placement: \"outside\")\n```py\nprint('Hello, world!')\nprint('Goodbye, world!')\n```\n*Line number inside of the block:*\n#codly(number-placement: \"inside\")\n```py\nprint('Hello, world!')\nprint('Goodbye, world!')\n```",
+ "upcoming": false
+ },
+ "smart-indent": {
+ "title": "Smart indentation",
+ "description": "Whether to use smart indentation, which will check for indentation on a line and use a bigger left side inset instead of spaces. This allows for linebreaks to continue at the same level of indentation. This is on by default, but disabling it can improve performance.",
+ "default": "true",
+ "ty": [ "bool" ],
+ "function": false,
+ "example": "*Enabled (default):*\n```py\ndef quicksort(L):\n qsort = lambda L: [] if L==[] else qsort([x for x in L[1:] if x< L[0]]) + L[0:1] + qsort([x for x in L[1:] if x>=L[0]])\n qsort(L)\n```\n*Disabled:*\n#codly(smart-indent: false)\n```py\ndef quicksort(L):\n qsort = lambda L: [] if L==[] else qsort([x for x in L[1:] if x< L[0]]) + L[0:1] + qsort([x for x in L[1:] if x>=L[0]])\n qsort(L))\n```"
+ },
+ "skip-last-empty": {
+ "title": "Skip last line if empty",
+ "description": "Whether to automatically skip the last line of the code block if it is empty. This avoids having an unnecessary empty line at the end of the code block.",
+ "default": "true",
+ "ty": [ "bool" ],
+ "function": true,
+ "example": "*Enabled (default):*\n```py\ndef fib(n):\n if n <= 1:\n return n\n return fib(n - 1) + fib(n - 2)\n\n```\n*Disabled:*\n#codly(skip-last-empty: false)\n```py\ndef fib(n):\n if n <= 1:\n return n\n return fib(n - 1) + fib(n - 2)\n\n```",
+ "upcoming": false
+ },
+ "breakable": {
+ "title": "Breakable",
+ "description": "Whether the codeblocks are breakable across page and column breaks.",
+ "default": "true",
+ "ty": [ "bool" ],
+ "function": false
+ },
+ "skips": {
+ "title": "Skips",
+ "description": "Insert a skip at the specified line numbers, setting its offset to the length of the skip. The skip is formatted using the #link()[`skip-number`] argument. Each skip is an array with two values: the line where the skip is inserted (zero indexed) and the number of lines of the skip. The same behavior can be achieved using the #link()[`codly-skip`] function.",
+ "default": "none",
+ "ty": [ "array", "type(none)" ],
+ "function": true,
+ "reset": true,
+ "example": "#codly(skips: ((4, 32), ))\n```py\ndef fib(n):\n if n <= 1:\n return n\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```"
+ },
+ "skip-line": {
+ "title": "Skip line",
+ "description": "Sets the content with which the line code is filled when a skip is encountered.",
+ "default": "align(center)[ ... ]",
+ "ty": [ "content", "type(none)" ],
+ "function": true,
+ "example": "#codly(\n skips: ((4, 32), ),\n skip-line: align(center, emoji.face.shock)\n)\n```py\ndef fib(n):\n if n <= 1:\n return n\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```"
+ },
+ "skip-number": {
+ "title": "Skip number",
+ "description": "Sets the content with which the line number columns is filled when a skip is encountered. If line numbers are disabled, this has no effect.",
+ "default": "[ ... ]",
+ "ty": [ "content", "type(none)" ],
+ "function": true,
+ "example": "#codly(\n skips: ((4, 32), ),\n skip-number: align(center, emoji.face.shock)\n)\n```py\ndef fib(n):\n if n <= 1:\n return n\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```"
+ },
+ "annotations": {
+ "title": "Annotations",
+ "description": "The annotations to display on the code block. A list of annotations that are automatically numbered and displayed on the right side of the code block.\n\nEach entry is a dictionary with the following keys:\n - `start`: the line number to start the annotation\n- `end`: the line number to end the annotation, if missing or `none` the annotation will only contain the start line\n - `content`: the content of the annotation as a showable value, if missing or `none` the annotation will only contain the number\n - `label`: *if and only if* the code block is in a `figure`, sets the label by which the annotation can be referenced.\n\nGenerally you probably want the `content` to be contained within a `rotate(90deg)`.\n\n*Note*: Annotations cannot overlap.\n*Known issues*:\n - Annotations that spread over a page break will not work correctly\n - Annotations on the first line of a code block will not work correctly.\n - Annotations that span lines that overflow (one line of code <-> two lines of text) will not work correctly.\n#one-indexed",
+ "default": "none",
+ "ty": [ "array", "type(none)" ],
+ "function": true,
+ "reset": true,
+ "experimental": true,
+ "example": "#codly(\n annotations: (\n (\n start: 2, end: 5,\n content: block(\n width: 2em,\n rotate(-90deg, reflow: true, \n align(center)[Function body])\n )\n ),\n ),\n)\n```py\ndef fib(n):\n if n <= 1:\n return n\n else:\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```"
+ },
+ "annotation-format": {
+ "title": "Annotation formatter",
+ "description": "The format of the annotation number. Can be `none` or a function that formats the annotation number.",
+ "default": "numbering.with(\"(1)\")",
+ "ty": [ "type(none)", "function" ],
+ "function": false
+ },
+ "highlighted-lines": {
+ "title": "Highlight lines",
+ "description": "Changes the background fill of certain lines to give them the appearance of being hihglighted. This is done by specifying an array of a combination of:\n - *line numbers*: the line number (one-indexed) as it is shown in the document (including offsets, skips, etc.)\n - *array of line number & fill*: an array of strictly two elements containing the line number (same as before) and a color, gradient, or pattern fill. \n\n The default highlighting color is controlled by the #link()[`highlighted-default-color`] argument. \n#one-indexed",
+ "default": "()",
+ "ty": [ "array", "type(none)" ],
+ "function": true,
+ "reset": true,
+ "example": "*Using default highlight color:*\n#codly(\n highlighted-lines: (1, 3)\n)\n```py\ndef fib(n):\n if n <= 1:\n return n\n else:\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```\n*Per-line color selection:*\n#codly(\n highlighted-lines: ((1, red.lighten(60%)), 2, 3)\n)```py\ndef fib(n):\n if n <= 1:\n return n\n else:\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```",
+ "upcoming": false
+ },
+ "highlighted-default-color": {
+ "title": "Highlight lines default color",
+ "description": "Changes the default color of highlighted lines.",
+ "default": "orange.lighten(60%)",
+ "ty": [ "color", "tiling", "gradient" ],
+ "function": true,
+ "example": "*Using default highlight color:*\n#codly(\n highlighted-default-color: green.lighten(60%),\n highlighted-lines: (1, 3)\n)\n```py\ndef fib(n):\n if n <= 1:\n return n\n else:\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```\n*Per-line color selection still works:*\n#codly(\n highlighted-lines: ((1, red.lighten(60%)), 2, 3)\n)```py\ndef fib(n):\n if n <= 1:\n return n\n else:\n return fib(n - 1) + fib(n - 2)\nfib(25)\n```",
+ "upcoming": false
+ },
+ "highlights": {
+ "title": "Highlights",
+ "description": "You can apply highlights to the code block using the `highlights` argument. It consists of a list of dictionaries, each with the following keys:\n- `line`: the line number to start highlighting\n- `start`: the character position to start highlighting, zero if omitted or `none`\n- `end`: the character position to end highlighting, the end of the line if omitted or `none`\n- `fill`: the fill of the highlight, defaults to the default color\n- `tag`: an optional tag to be displayed alongside the highlight.\n- `inset`: overrides the global @arg-highlight-inset.\n- `baseline`: overrides the baseline which is set by default to the `bottom` component of @arg-highlight-inset.\n- `clip`: overrides the global @arg-highlight-clip.\n- `outset`: overrides the global @arg-highlight-outset.\n- `radius`: overrides the global @arg-highlight-radius.\n- `label`: *if and only if* the code block is in a `figure`, sets the label by which the highlight can be referenced.\n\nAs with other code block settings, annotations are reset after each code block. #one-indexed #zero-indexed",
+ "default": "none",
+ "ty": [ "array", "type(none)" ],
+ "function": true,
+ "example": "#codly(highlights: (\n (line: 4, start: 2, end: none, fill: red),\n (line: 5, start: 13, end: 19, fill: green, tag: \"(a)\"),\n (line: 5, start: 26, fill: blue, tag: \"(b)\"),\n))\n```py\ndef fib(n):\n if n <= 1:\n return n\n else:\n return fib(n - 1) + fib(n - 2)\nprint(fib(25))\n```",
+ "examples": [{
+ "title": "Local override of highlight parameters",
+ "desc": "You can override the global highlight parameters for a specific highlight by using the `inset`, `radius`, `clip`, and `outset` keys in the highlight dictionary.",
+ "code": "#codly(\n highlights: (\n (line: 4, start: 2, end: none, fill: red),\n (line: 5, start: 13, end: 19, fill: green, tag: \"(a)\", inset: 0.1em, radius: 0.5em),\n (line: 5, start: 26, fill: blue, tag: \"(b)\", clip: false)\n )\n)\n```py\ndef fib(n):\n if n <= 1:\n return n\n else:\n return fib(n - 1) + fib(n - 2)\nprint(fib(25))\n```"
+ }]
+ },
+ "highlight-radius": {
+ "title": "Highlight radius",
+ "description": "The radius of the highlights.",
+ "default": "0.32em",
+ "ty": [ "length" ],
+ "function": true,
+ "reset": true
+ },
+ "highlight-fill": {
+ "title": "Highlight fill",
+ "description": "The fill transformer of the highlights, is a function that takes in the highlight color and returns a fill.",
+ "default": "(color) => color.lighten(80%)",
+ "ty": [ "function" ],
+ "function": false
+ },
+ "highlight-stroke": {
+ "title": "Highlight stroke",
+ "description": "The stroke transformer of the highlights, is a function that takes in the highlight color and returns a stroke.",
+ "default": "(color) => 0.5pt + color",
+ "ty": [ "stroke", "function" ],
+ "function": false
+ },
+ "highlight-inset": {
+ "title": "Highlight inset",
+ "description": "The inset of the highlight blocks. It can also be a dictionary with the keys same keys as in the Tyspt built-in #link(\"https://typst.app/docs/reference/layout/block/#parameters-inset\")[`block.inset`].",
+ "default": "0.32em",
+ "ty": [ "length", "dictionary" ],
+ "function": true,
+ "examples": [{
+ "title": "Alignment of lines",
+ "desc": "If alignment between highlighted and non-highlighted lines is critical for your use case, as could be the case in presentation, you can set the horizontal inset to be zero, or close to zero, to maintain alignment between the lines.",
+ "code": "#codly(\n highlight-inset: (x: 0pt, y: 0.32em),\n highlights: ((line: 2, start: 0), )\n)\n```kotlin\nprivate val n = 0\nprivate val n = 0\n```"
+ }],
+ "upcoming": false
+ },
+ "highlight-outset": {
+ "title": "Highlight outset",
+ "description": "The outset of the highlight blocks. It can also be a dictionary with the keys same keys as in the Tyspt built-in #link(\"https://typst.app/docs/reference/layout/block/#parameters-outset\")[`block.outset`].",
+ "default": "0em",
+ "ty": [ "length", "dictionary" ],
+ "function": true,
+ "upcoming": false
+ },
+ "highlight-clip": {
+ "title": "Highlight clip",
+ "description": "Whether highlight box clips code. See the documentation of the Tyspt built-in #link(\"https://typst.app/docs/reference/layout/block/#parameters-clip\")[`block.clip`].",
+ "default": "true",
+ "ty": [ "bool" ],
+ "function": true,
+ "upcoming": false
+ },
+ "reference-by": {
+ "title": "Reference by",
+ "description": "The mode by which references are displayed. Two modes are available:\n- `line`: references are displayed as line numbers\n- `item`: references are displayed as items, i.e by the `tag` for highlights and `content` for annotations.",
+ "default": "\"line\"",
+ "ty": [ "str" ],
+ "function": true
+ },
+ "reference-sep": {
+ "title": "Reference separator",
+ "description": "The separator to use when referencing highlights and annotations.",
+ "default": "\"-\"",
+ "ty": [ "str", "content" ],
+ "function": true
+ },
+ "reference-number-format": {
+ "title": "Reference number format",
+ "description": "The format of the reference number line number, only used if `reference-by` is set to `\"line\"`.",
+ "default": "numbering.with(\"1\")",
+ "ty": [ "function" ],
+ "function": false
+ },
+ "aliases": {
+ "title": "Language name aliases",
+ "description": "Creates aliases between languages, allowing them to have separate `languages` but the same syntax highlighting. An example of that could be `cuda` and `c++` having the same highlighting but different icons.",
+ "default": "(:)",
+ "ty": [ "dictionary" ],
+ "function": false,
+ "upcoming": false,
+ "example": "#import \"@preview/codly-languages:0.1.7\": *\n\n#codly(\n languages: codly-languages,\n aliases: (\"cuda\": \"c++\")\n)\n```cuda\nint main() {\n std::cout << \"Hello, World!\" << std::endl;\n return 0;\n}```"
+ }
+}
diff --git a/packages/preview/codly/1.3.1/src/args.typ b/packages/preview/codly/1.3.1/src/args.typ
new file mode 100644
index 0000000000..a2e5b524c0
--- /dev/null
+++ b/packages/preview/codly/1.3.1/src/args.typ
@@ -0,0 +1,129 @@
+#let type_check(
+ tys,
+ function,
+) = {
+ assert(
+ type(tys) == array,
+ message: "codly: the type of a codly argument must be an array"
+ )
+
+ (value_ty, use) => {
+ value_ty in tys or (function and (not use) and value_ty == function)
+ }
+}
+
+#let type_check_str(
+ tys,
+ function,
+) = {
+ assert(
+ type(tys) == array,
+ message: "codly: the type of a codly argument must be an array"
+ )
+
+ let out = "either a " + tys.map(str).join(", a ", last: ", or a ")
+ let out_function = if function {
+ "either a " + tys.map(str).join(", a ") + ", or a function that returns one of the previous types"
+ } else {
+ out
+ }
+
+ (use) => {
+ if function and (not use) {
+ out_function
+ } else {
+ out
+ }
+ }
+}
+
+#let state_with_type(
+ name,
+ type_check,
+ type_check_str,
+ default,
+ allow-fn,
+) = {
+ let state = state("codly-" + name, default)
+
+ (
+ set-raw: (value) => {
+ state.update((_) => value)
+ },
+ update: (value) => {
+ assert(
+ type_check(type(value), false),
+ message: "codly: `" + name + "` must be " + type_check_str(false) + ", found: " + str(type(value)),
+ )
+ state.update((_) => value)
+ },
+ type_check: (value) => {
+ if allow-fn and type(value) == function {
+ value = value()
+ }
+
+ assert(
+ type_check(type(value), false),
+ message: "codly: `" + name + "` must be " + type_check_str(false) + ", found: " + str(type(value)),
+ )
+
+ value
+ },
+ reset: () => {
+ state.update((_) => default)
+ },
+ default: default,
+ )
+}
+
+#let __codly-args = {
+ let out = (:)
+ let args = json("args.json")
+ for (key, arg) in args {
+ let tys = arg.ty.map(t => {
+ let t = eval(t, mode: "code");
+ if t == none {
+ type(none)
+ } else {
+ t
+ }
+ })
+ if arg.function and function in tys {
+ panic("codly: `function` is not a valid type for an argument")
+ }
+
+ out.insert(
+ key,
+ state_with_type(
+ key,
+ type_check(tys, arg.function),
+ type_check_str(tys, arg.function),
+ eval(arg.default, mode: "code"),
+ arg.function,
+ )
+ )
+ }
+
+ out
+}
+
+#let __codly-defaults = {
+ let out = (:)
+ __codly-args.pairs().map(((key, value)) =>(key, value.default)).to-dict()
+}
+
+// Must be called in a context!
+#let __codly-save() = {
+ let out = (:)
+ for (key, value) in __codly-args {
+ out.insert(key, (value.get-raw)())
+ }
+ return out
+}
+
+// Must be called after `__codly_save`!
+#let __codly-load(stored) = {
+ for (key, value) in __codly-args {
+ (value.set-raw)(stored.at(key))
+ }
+}
\ No newline at end of file
diff --git a/packages/preview/codly/1.3.1/src/lib.typ b/packages/preview/codly/1.3.1/src/lib.typ
new file mode 100644
index 0000000000..30f072ed5c
--- /dev/null
+++ b/packages/preview/codly/1.3.1/src/lib.typ
@@ -0,0 +1,2161 @@
+#import "args.typ": __codly-args, __codly-save, __codly-load
+
+#let __codly-trim(body) = {
+ if type(body) == str {
+ return body.trim()
+ }
+
+ if body.has("children") {
+ let out = ()
+ let start = true
+ for child in body.children {
+ if start and child.has("text") and child.text.trim().len() == 0 {
+ continue
+ } else if start and child == [ ] {
+ continue
+ }
+
+ start = false
+ out.push(child)
+ }
+
+ (body.func())(out)
+ } else {
+ body
+ }
+}
+
+#let __codly-inset(inset) = {
+ if type(inset) == dictionary {
+ let other = inset.at("rest", default: 0.32em)
+ (
+ top: inset.at("top", default: inset.at("y", default: other)),
+ right: inset.at("right", default: inset.at("x", default: other)),
+ bottom: inset.at("bottom", default: inset.at("y", default: other)),
+ left: inset.at("left", default: inset.at("x", default: other)),
+ )
+ } else {
+ (top: inset, right: inset, bottom: inset, left: inset)
+ }
+}
+
+/// Lets you set a line number offset.
+///
+/// #codly-offset(offset: 25)
+/// ```py
+/// def fib(n):
+/// if n <= 1:
+/// return n
+/// return fib(n - 1) + fib(n - 2)
+/// fib(25)
+/// ```
+///
+/// - offset (int): the offset to apply to the code block
+///
+/// -> content
+#let codly-offset(offset: 0) = {
+ (__codly-args.offset.update)(offset)
+}
+
+/// Lets you set a range of line numbers to highlight.
+/// Similar to `codly(range: (start, end))`.
+///
+/// #codly-range(start: 2)
+/// ```py
+/// def fib(n):
+/// if n <= 1:
+/// return n
+/// return fib(n - 1) + fib(n - 2)
+/// fib(25)
+/// ```
+///
+/// - start (int): the start of the displayed range
+/// - end (none, int): the end of the displayed range, none for the rest of the block
+///
+/// -> content
+#let codly-range(
+ start,
+ end: none,
+ ..rest,
+) = {
+ let pos = rest.pos();
+ if pos.len() > 0 {
+ (__codly-args.ranges.update)(((start, end), ..pos))
+ } else {
+ (__codly-args.range.update)((start, end))
+ }
+}
+
+/// Disables codly.
+///
+/// *Enabled:*
+/// ```
+/// Hello, world!
+/// ```
+///
+/// #codly-disable()
+/// *Disabled:*
+/// ```
+/// Hello, world!
+/// ```
+///
+/// -> content
+#let codly-disable() = {
+ (__codly-args.enabled.update)(false)
+}
+
+/// Enables codly.
+///
+/// *Enabled:*
+/// ```
+/// Hello, world!
+/// ```
+/// #codly-disable()
+/// *Disabled:*
+/// ```
+/// Hello, world!
+/// ```
+///
+/// #codly-enable()
+/// *Enabled:*
+/// ```
+/// Hello, world!
+/// ```
+///
+/// -> content
+#let codly-enable() = {
+ (__codly-args.enabled.update)(true)
+}
+
+/// Disabled codly locally.
+///
+/// ````
+/// *Enabled:*
+/// ```
+/// Hello, world!
+/// ```
+///
+/// *Disabled:*
+/// #no-codly(```
+/// Hello, world!
+/// ```)
+///
+/// -> content
+#let no-codly(body) = {
+ (__codly-args.enabled.update)(false)
+ body
+ (__codly-args.enabled.update)(true)
+}
+
+/// Enable codly locally.
+///
+/// ````
+/// *Disabled:*
+/// #codly(enabled: false)
+/// ```
+/// Hello, world!
+/// ```
+///
+/// *Enabled:*
+/// #yes-codly(```
+/// Hello, world!
+/// ```)
+///
+/// -> content
+#let yes-codly(body) = {
+ (__codly-args.enabled.update)(true)
+ body
+ (__codly-args.enabled.update)(false)
+}
+
+/// Appends a skip to the list of skips.
+///
+/// #codly-skip(4, 32)
+/// ```
+/// Hello, world!
+/// Goodbye, world!
+/// ```
+///
+/// - position (int): the line at which to insert the skip
+/// - length (int): the number of lines the skip is long
+///
+/// -> content
+#let codly-skip(
+ position,
+ length,
+) = {
+ state("codly-skips", ()).update(skips => {
+ if skips == none {
+ skips = ()
+ }
+
+ skips.push((position, length))
+ skips
+ })
+}
+
+
+/// Configures codly.
+/// Is used in a similar way as `set` rules. You can imagine the following:
+/// ```typc
+/// // This is a representation of the actual code.
+/// // The actual code behave like a set rule that uses `state`.
+/// let codly(
+/// enabled: true,
+/// offset: 0,
+/// range: none,
+/// languages: (:),
+/// display-name: true,
+/// display-icon: true,
+/// default-color: rgb("#283593"),
+/// radius: 0.32em,
+/// inset: 0.32em,
+/// fill: none,
+/// zebra-fill: luma(240),
+/// stroke: 1pt + luma(240),
+/// lang-inset: 0.32em,
+/// lang-outset: (x: 0.32em, y: 0pt),
+/// lang-radius: 0.32em,
+/// lang-stroke: (lang) => lang.color + 0.5pt,
+/// lang-fill: (lang) => lang.color.lighten(80%),
+/// lang-format: auto,
+/// number-format: (number) => [ #number ],
+/// number-align: left + horizon,
+/// number-placement: false,
+/// smart-indent: false,
+/// annotations: none,
+/// annotation-format: numbering.with("(1)"),
+/// highlights: none,
+/// highlight-radius: 0.32em,
+/// highlight-fill: (color) => color.lighten(80%),
+/// highlight-stroke: (color) => 0.5pt + color,
+/// highlight-inset: 0.32em,
+/// highlight-outset: 0em,
+/// highlight-clip: true,
+/// reference-by: line,
+/// reference-sep: "-",
+/// reference-number-format: numbering.with("1"),
+/// header: none,
+/// header-repeat: false,
+/// header-transform: (x) => x,
+/// header-cell-args: (),
+/// footer: none,
+/// footer-repeat: false,
+/// footer-transform: (x) => x,
+/// footer-cell-args: (),
+/// breakable: false,
+/// ) = {}
+/// ```
+///
+/// Each argument is explained in the docs: https://github.com/Dherse/codly/blob/main/docs.pdf
+#let __codly-inner(
+ ..args,
+) = {
+ let pos = args.named()
+
+ if args.pos().len() > 0 {
+ panic("codly: no positional arguments are allowed")
+ }
+
+ for (key, arg) in __codly-args {
+ if key in pos {
+ // Remove the argument from the list.
+ let i = pos.remove(key)
+
+ // Update the state.
+ (arg.update)(i)
+ }
+ }
+
+ if pos.len() > 0 {
+ panic("codly: unknown arguments: " + pos.keys().join(", "))
+ }
+}
+
+/// Resets the codly state to its default values.
+/// This is useful if you want to reset the state of codly to its default values.
+///
+/// Note that this is mostly intended for testing purposes.
+///
+/// -> content
+#let codly-reset() = {
+ for (_, arg) in __codly-args {
+ (arg.reset)()
+ }
+}
+
+#let get-len(child) = {
+ if child.has("label") and child.label == {
+ 0
+ } else if child.has("text") {
+ child.text.len()
+ } else if child.has("children") {
+ child.children.map(get-len).sum()
+ } else if child.has("child") {
+ get-len(child.child)
+ } else if child.has("body") {
+ get-len(child.body)
+ } else {
+ 0
+ }
+}
+
+#let ws_regex = regex("\s")
+#let indent-regex = regex("^\\s*")
+#let get-flattened-body(elem) = {
+ if elem.has("label") and elem.label == {
+ return (elem,)
+ }
+
+ if elem.has("children") {
+ elem.children.map(get-flattened-body).flatten()
+ } else if elem.has("child") and elem.has("styles") {
+ get-flattened-body(elem.child)
+ .map(x => (elem.func())(x, elem.styles))
+ .flatten()
+ } else if elem.has("text") {
+ // Separate the whitespaces at the start of text
+ let out = ()
+ let ws = none
+ for cluster in elem.text.clusters() {
+ let m = cluster.match(ws_regex)
+ if m != none and ws != none {
+ ws += cluster
+ } else if m != none and ws == none {
+ ws = cluster
+ } else if ws != none {
+ out.push(text(ws))
+ ws = none
+ out.push(text(cluster))
+ } else {
+ out.push(text(cluster))
+ }
+ }
+
+ if ws != none {
+ out.push(text(ws))
+ }
+ out
+ } else {
+ (elem,)
+ }
+}
+
+#let __sort-highlights(highlights) = {
+ // Check if highlight 'a' contains highlight 'b'
+ let contains(a, b) = {
+ // a contains b if b is fully within a's range
+ // but they're not the same highlight
+ (a.start <= b.start and a.end >= b.end and
+ not (a.start == b.start and a.end == b.end))
+ }
+
+ // Calculate nesting depth for a highlight
+ // Depth = number of highlights that contain this one
+ let get-depth(h, all-highlights) = {
+ let depth = 0
+ for other in all-highlights {
+ if contains(other, h) {
+ depth += 1
+ }
+ }
+ depth
+ }
+
+ // Add depth information to each highlight
+ let with-depths = highlights.enumerate().map(((i, h)) => {
+ (
+ original-index: i,
+ highlight: h,
+ depth: get-depth(h, highlights)
+ )
+ })
+
+ // Sort by:
+ // 1. Depth (descending - deepest/most nested first)
+ // 2. Start position (ascending)
+ // 3. End position (ascending)
+ // 4. Original index (to maintain stable sort)
+ let sorted = with-depths.sorted(key: item => (
+ -item.depth, // Negative for descending order
+ item.highlight.start,
+ item.highlight.end,
+ item.original-index
+ ))
+
+ // Return just the highlights without the extra metadata
+ sorted.map(item => item.highlight)
+}
+
+#let codly-line(
+ highlight-stroke,
+ highlight-fill,
+ highlight-radius,
+ highlight-inset,
+ highlight-baseline,
+ highlight-outset,
+ highlight-clip,
+ default-color,
+ highlights,
+ smart-indent,
+ reference-by,
+ reference-sep,
+ reference-number-format,
+ it,
+ block-label: none
+) = {
+ let highlighted = it.body
+ let highlights = {
+ if highlights == none {
+ ()
+ } else {
+ __sort-highlights(highlights
+ .filter(x => x.line == it.number)
+ .map(x => {
+ if not "start" in x or x.start == none {
+ x.insert("start", 0)
+ }
+
+ if not "end" in x or x.end == none {
+ x.insert("end", 999999999)
+ }
+
+ if not "fill" in x {
+ x.insert("fill", default-color)
+ }
+ x
+ }))
+ }
+ }
+
+ // Smart indentation code.
+ let body = if it.body.has("children") {
+ it.body.children
+ } else {
+ (body, )
+ }
+
+ let width = none
+ if smart-indent {
+ // Check the indentation of the line by taking l,
+ // and checking for the first element in the sequence.
+ let first = for child in body {
+ if child.has("children") {
+ for c in child.children {
+ if c.has("text") {
+ c
+ break
+ }
+ }
+ } else if child.has("child") {
+ if child.child.has("text") {
+ child.child
+ break
+ }
+ } else if child.has("body") {
+ if child.body.has("text") {
+ child.body
+ break
+ }
+ } else if child.has("text") {
+ child
+ break
+ }
+ }
+
+ if first != none and first.has("text") {
+ let match = first.text.match(indent-regex)
+
+ // Ensure there is a match and it starts at the beginning of the line.
+ if match != none and match.start == 0 {
+ // Calculate the indentation.
+ let indent = match.end - match.start
+
+ // Then measure the necessary indent.
+ let indent = first.text.slice(match.start, match.end)
+ width = measure([#indent]).width
+ }
+ }
+ }
+
+ // If there are no highlights, return the line as is.
+ if highlights.len() == 0 {
+ if width != none {
+ // We add the indentation to the line.
+ highlighted = {
+ set par(hanging-indent: width)
+ highlighted
+ }
+ }
+
+ let l = raw.line(it.number, it.count, it.text, highlighted)
+
+ // Build a label if the code block has one.
+ if block-label != none {
+ let lab = label(str(block-label) + ":" + str(it.number))
+
+ return [#figure(
+ kind: "codly-line",
+ supplement: none,
+ caption: none,
+ outlined: false,
+ numbering: (..) => {
+ ref(block-label)
+ reference-sep
+ reference-number-format(it.number)
+ },
+ l
+ )#lab]
+ } else {
+ return l
+ }
+ }
+
+ // Apply highlights
+ let l = it.body
+ for hl in highlights {
+ let fill = highlight-fill(hl.fill)
+ let stroke = highlight-stroke(hl.fill)
+ let hl-inset = if "inset" in hl {
+ hl.inset
+ } else {
+ highlight-inset
+ }
+
+ let hl-baseline = if "baseline" in hl {
+ hl.baseline
+ } else {
+ highlight-baseline
+ }
+
+ let hl-outset = if "outset" in hl {
+ hl.outset
+ } else {
+ highlight-outset
+ }
+
+ let hl-clip = if "clip" in hl {
+ hl.clip
+ } else {
+ highlight-clip
+ }
+
+ let hl-radius = if "radius" in hl {
+ hl.radius
+ } else {
+ highlight-radius
+ }
+
+ let tag = if "tag" in hl {
+ hl.tag
+ } else {
+ none
+ }
+
+ let children = ()
+ let collection = none
+ let i = 0
+
+ let body = get-flattened-body(highlighted)
+
+ let len = body.len()
+ for (index, child) in body.enumerate() {
+ let last = index == len - 1
+ if child.has("label") and child.label == {
+ if collection != none {
+ collection.push(child)
+ continue
+ } else {
+ children.push(child)
+ continue
+ }
+ }
+
+ let len = get-len(child)
+
+ if collection != none {
+ collection.push(child)
+ } else if collection == none and (
+ i >= hl.start or i + len >= hl.start
+ ) and (i < hl.end) {
+ collection = (child,)
+ } else {
+ children.push(child)
+ }
+
+ let label = if "label" in hl and hl.label != none {
+ assert.ne(block-label, none, message: {
+ "codly: for labels on highlights to work, "
+ "you must have the code block contained within a figure "
+ "and that figure must have a label."
+ })
+
+ let referenced = if reference-by == "line" {
+ reference-number-format(it.number)
+ } else {
+ assert("tag" in hl, message: "codly: tag is required for item reference")
+ hl.tag
+ }
+
+ place(hide[#figure(
+ kind: "codly-referencer",
+ supplement: none,
+ numbering: (..) => {
+ ref(block-label)
+ reference-sep
+ __codly-trim(referenced)
+ },
+ []
+ )#hl.label])
+ } else {
+ none
+ }
+
+ if collection != none and (i + len >= hl.end or last) {
+ if tag == none {
+ let content = box(
+ radius: hl-radius,
+ clip: hl-clip,
+ fill: fill,
+ stroke: stroke,
+ inset: hl-inset,
+ outset: hl-outset,
+ baseline: hl-baseline,
+ collection.join() + label,
+ )
+ children.push(content)
+ } else {
+ let col = collection.join()
+ let height-col = measure(col).height
+ let height-tag = measure(tag).height
+ let highlight-inset-sep = __codly-inset(hl-inset)
+ let max-height = calc.max(
+ height-col,
+ height-tag,
+ ) + highlight-inset-sep.top + highlight-inset-sep.bottom
+ let hl-box = box(
+ radius: (
+ top-right: 0pt,
+ bottom-right: 0pt,
+ rest: hl-radius,
+ ),
+ height: max-height,
+ clip: hl-clip,
+ fill: fill,
+ stroke: stroke,
+ inset: hl-inset,
+ outset: hl-outset,
+ baseline: hl-baseline,
+ collection.join(),
+ )
+ let tag-box = box(
+ radius: (
+ top-left: 0pt,
+ bottom-left: 0pt,
+ rest: hl-radius,
+ ),
+ height: max-height,
+ clip: hl-clip,
+ fill: fill,
+ stroke: stroke,
+ inset: hl-inset,
+ outset: hl-outset,
+ baseline: hl-baseline,
+ tag + label,
+ )
+
+ children.push([#hl-box#h(
+ 0pt,
+ weak: true,
+ )#tag-box])
+ }
+ collection = none
+ }
+
+ i += len
+ }
+
+ highlighted = children.join()
+ }
+
+ if width != none {
+ // We add the indentation to the line.
+ highlighted = {
+ set par(hanging-indent: width)
+ highlighted
+ }
+ }
+
+ let l = raw.line(it.number, it.count, it.text, highlighted)
+
+ // Build a label if the code block has one.
+ if block-label != none {
+ let lab = label(str(block-label) + ":" + str(it.number))
+ return [#figure(
+ kind: "codly-line",
+ supplement: none,
+ numbering: (..) => {
+ ref(block-label)
+ reference-sep
+ reference-number-format(it.number)
+ },
+ l
+ )#lab]
+ } else {
+ return l
+ }
+}
+
+#let in_range(ranges, line) = {
+ if ranges == none or ranges.len() == 0 {
+ return true
+ }
+
+ // Return true if the line is contained in any of the ranges.
+ for r in ranges {
+ if r.at(0) == none {
+ if line <= r.at(1) {
+ return true
+ }
+ } else if r.at(1) == none {
+ if r.at(0) <= line {
+ return true
+ }
+ } else if r.at(0) <= line and line <= r.at(1) {
+ return true
+ }
+ }
+
+ return false
+}
+
+#let __codly-raw(
+ it,
+ block-label: none,
+ alias: none,
+ extra: (:),
+) = context {
+ let enabled = (__codly-args.enabled.type_check)(if "enabled" in extra {
+ extra.enabled
+ } else {
+ state("codly-enabled", __codly-args.enabled.default).get()
+ })
+
+ if not enabled {
+ return it
+ }
+
+ if type(it) != content or it.func() != raw {
+ panic("codly-raw: body must be a raw content")
+ }
+
+ // Optimization: skip alises checking if `alias` already set
+ let aliases = if alias == none {
+ (__codly-args.aliases.type_check)(if "aliases" in extra {
+ extra.aliases
+ } else {
+ state("codly-aliases", __codly-args.aliases.default).get()
+ })
+ } else {
+ (:)
+ }
+
+ let alias = if it.lang != none and it.lang in aliases {
+ let real = aliases.at(it.lang)
+ return {
+ show raw.where(block: true): __codly-raw.with(alias: it.lang, extra: extra, block-label: block-label)
+
+ raw(
+ it.text,
+ block: true,
+ align: it.align,
+ lang: real,
+ theme: it.theme,
+ syntaxes: it.syntaxes,
+ tab-size: it.tab-size,
+ )
+ }
+ } else if alias == none {
+ it.lang
+ } else {
+ alias
+ }
+
+ let highlight-inset = (
+ __codly-args.highlight-inset.type_check
+ )(if "highlight-inset" in extra {
+ extra.highlight-inset
+ } else {
+ state("codly-highlight-inset", __codly-args.highlight-inset.default).get()
+ });
+
+ show raw.line.where(label: ): codly-line.with(
+ (__codly-args.highlight-stroke.type_check)(if "highlight-stroke" in extra {
+ extra.highlight-stroke
+ } else {
+ state("codly-highlight-stroke", __codly-args.highlight-stroke.default).get()
+ }),
+ (__codly-args.highlight-fill.type_check)(if "highlight-fill" in extra {
+ extra.highlight-fill
+ } else {
+ state("codly-highlight-fill", __codly-args.highlight-fill.default).get()
+ }),
+ (__codly-args.highlight-radius.type_check)(if "highlight-radius" in extra {
+ extra.highlight-radius
+ } else {
+ state("codly-highlight-radius", __codly-args.highlight-radius.default).get()
+ }),
+ highlight-inset,
+ __codly-inset(highlight-inset).bottom,
+ (__codly-args.highlight-outset.type_check)(if "highlight-outset" in extra {
+ extra.highlight-outset
+ } else {
+ state("codly-highlight-outset", __codly-args.highlight-outset.default).get()
+ }),
+ (__codly-args.highlight-clip.type_check)(if "highlight-clip" in extra {
+ extra.highlight-clip
+ } else {
+ state("codly-highlight-clip", __codly-args.highlight-clip.default).get()
+ }),
+ (__codly-args.default-color.type_check)(if "default-color" in extra {
+ extra.default-color
+ } else {
+ state("codly-default-color", __codly-args.default-color.default).get()
+ }),
+ (__codly-args.highlights.type_check)(if "highlights" in extra {
+ extra.highlights
+ } else {
+ state("codly-highlights", __codly-args.highlights.default).get()
+ }),
+ (__codly-args.smart-indent.type_check)(if "smart-indent" in extra {
+ extra.smart-indent
+ } else {
+ state("codly-smart-indent", __codly-args.smart-indent.default).get()
+ }),
+ (__codly-args.reference-by.type_check)(if "reference-by" in extra {
+ extra.reference-by
+ } else {
+ state("codly-reference-by", __codly-args.reference-by.default).get()
+ }),
+ (__codly-args.reference-sep.type_check)(if "reference-sep" in extra {
+ extra.reference-sep
+ } else {
+ state("codly-reference-sep", __codly-args.reference-sep.default).get()
+ }),
+ (__codly-args.reference-number-format.type_check)(if "reference-number-format" in extra {
+ extra.reference-number-format
+ } else {
+ state("codly-reference-number-format", __codly-args.reference-number-format.default).get()
+ }),
+ block-label: block-label,
+ )
+
+ show figure.where(kind: "codly-line"): it => {
+ set align(left + horizon)
+ it.body
+ }
+
+ show figure.where(kind: "__codly-raw-line"): it => {
+ set align(left + horizon)
+ it.body
+ }
+
+ show figure.where(kind: "__codly-end-block"): it => none
+
+ set par(justify: false, first-line-indent: 0pt)
+
+ let range = (__codly-args.range.type_check)(if "range" in extra {
+ extra.range
+ } else {
+ state("codly-range", __codly-args.range.default).get()
+ })
+
+ let ranges = (__codly-args.ranges.type_check)(if "ranges" in extra {
+ extra.ranges
+ } else {
+ state("codly-ranges", __codly-args.ranges.default).get()
+ })
+
+ if ranges == none and range != none {
+ ranges = (range, )
+ }
+
+ // Pre-check ranges (skips range check in every iter)
+ if ranges != none {
+ for r in ranges {
+ assert(type(r) == array, message: "codly: ranges must be an array of arrays, found " + str(type(r)))
+ assert(r.len() == 2, message: "codly: ranges must be an array of arrays with two elements")
+ }
+ }
+
+ let display-names = (
+ __codly-args.display-name.type_check
+ )(if "display-name" in extra {
+ extra.display-name
+ } else {
+ state("codly-display-name", __codly-args.display-name.default).get()
+ })
+
+ let display-icons = (
+ __codly-args.display-icon.type_check
+ )(if "display-icon" in extra {
+ extra.display-icon
+ } else {
+ state("codly-display-icon", __codly-args.display-icon.default).get()
+ })
+
+ let lang-outset = (
+ __codly-args.lang-outset.type_check
+ )(if "lang-outset" in extra {
+ extra.lang-outset
+ } else {
+ state("codly-lang-outset", __codly-args.lang-outset.default).get()
+ })
+
+ let language-block = {
+ let fn = (__codly-args.lang-format.type_check)(if "lang-format" in extra {
+ extra.lang-format
+ } else {
+ state("codly-lang-format", __codly-args.lang-format.default).get()
+ })
+
+ if fn == none {
+ (..) => none
+ } else if fn == auto {
+ auto
+ } else {
+ fn
+ }
+ }
+ let default-color = (
+ __codly-args.default-color.type_check
+ )(if "default-color" in extra {
+ extra.default-color
+ } else {
+ state("codly-default-color", __codly-args.default-color.default).get()
+ })
+
+ let radius = (__codly-args.radius.type_check)(if "radius" in extra {
+ extra.radius
+ } else {
+ state("codly-radius", __codly-args.radius.default).get()
+ })
+
+ let offset = (__codly-args.offset.type_check)(if "offset" in extra {
+ extra.offset
+ } else {
+ state("codly-offset", __codly-args.offset.default).get()
+ })
+
+ let offset-from = (
+ __codly-args.offset-from.type_check
+ )(if "offset-from" in extra {
+ extra.offset-from
+ } else {
+ state("codly-offset-from", __codly-args.offset-from.default).get()
+ })
+
+ if offset-from != none {
+ let origin = query(offset-from)
+ if origin.len() == 0 {
+ panic("codly: offset-from must be used with a valid label, could not find: " + str(offset-from))
+ } else if origin.len() > 1 {
+ panic("codly: offset-from must be used with a unique label, found multiple: " + str(offset-from))
+ }
+
+ let origin = origin.first()
+ let end = query(figure.where(kind: "__codly-end-block").after(origin.location())).first()
+ let lines = query(figure.where(kind: "__codly-raw-line").after(origin.location()).before(end.location()))
+ if lines.len() > 0 {
+ offset += lines.last().body.children.at(0).number
+ }
+ }
+
+ let stroke = (__codly-args.stroke.type_check)(if "stroke" in extra {
+ extra.stroke
+ } else {
+ state("codly-stroke", __codly-args.stroke.default).get()
+ })
+
+ let zebra-color = (
+ __codly-args.zebra-fill.type_check
+ )(if "zebra-fill" in extra {
+ extra.zebra-fill
+ } else {
+ state("codly-zebra-fill", __codly-args.zebra-fill.default).get()
+ })
+
+ let numbers-format = (
+ __codly-args.number-format.type_check
+ )(if "number-format" in extra {
+ extra.number-format
+ } else {
+ state("codly-number-format", __codly-args.number-format.default).get()
+ })
+
+ let numbers-alignment = (
+ __codly-args.number-align.type_check
+ )(if "number-align" in extra {
+ extra.number-align
+ } else {
+ state("codly-number-align", __codly-args.number-align.default).get()
+ })
+
+ let number-placement = (
+ __codly-args.number-placement.type_check
+ )(if "number-placement" in extra {
+ extra.number-placement
+ } else {
+ state("codly-number-placement", __codly-args.number-placement.default).get()
+ })
+
+ if number-placement != "inside" and number-placement != "outside" {
+ panic("codly: number-placement must be either `\"inside\"` or `\"outside\"`")
+ }
+
+ let numbers-outside = (number-placement == "outside") and numbers-format != none
+
+ let padding = __codly-inset(
+ (__codly-args.inset.type_check)(if "inset" in extra {
+ extra.inset
+ } else {
+ state("codly-inset", __codly-args.inset.default).get()
+ }),
+ )
+
+ let breakable = (
+ __codly-args.breakable.type_check
+ )(if "breakable" in extra {
+ extra.breakable
+ } else {
+ state("codly-breakable", __codly-args.breakable.default).get()
+ })
+
+ let fill = (__codly-args.fill.type_check)(if "fill" in extra {
+ extra.fill
+ } else {
+ state("codly-fill", __codly-args.fill.default).get()
+ })
+
+ let skips = {
+ let skips = (__codly-args.skips.type_check)(if "skips" in extra {
+ extra.skips
+ } else {
+ state("codly-skips", __codly-args.skips.default).get()
+ })
+ if skips == none {
+ ()
+ } else {
+ skips.sorted(key: x => x.at(0)).dedup()
+ }
+ }
+
+ let highlighted-lines = (
+ __codly-args.highlighted-lines.type_check
+ )(if "highlighted-lines" in extra {
+ extra.highlighted-lines
+ } else {
+ state("codly-highlighted-lines", __codly-args.highlighted-lines.default).get()
+ });
+
+ let highlighted-default-color = (
+ __codly-args.highlighted-default-color.type_check
+ )(if "highlighted-default-color" in extra {
+ extra.highlighted-default-color
+ } else {
+ state("codly-highlighted-default-color", __codly-args.highlighted-default-color.default).get()
+ });
+
+ let highlighted-by-line = ()
+ if highlighted-lines == none {
+
+ } else if type(highlighted-lines) == array {
+ if highlighted-lines.len() > 0 {
+ let ix = 1
+ for l in highlighted-lines.sorted(key: (x) => if type(x) == int { x } else { x.at(0) }) {
+ let (ln, col) = if type(l) == int {
+ (l, highlighted-default-color)
+ } else if type(l) == array {
+ assert(l.len() == 2, message: "codly: a highlighted line definition must be an integer or an array of two elements: the line, and the highlight color (array length mismatch)")
+ let ln = l.at(0)
+ assert(type(ln) == int, message: "codly: the type of a `highlighted-lines` line must be either an integer, found: " + str(type(ln)));
+
+ let col = l.at(1)
+ assert(
+ type(col) == color or type(col) == gradient or type(col) == pattern,
+ message: "codly: the type of a `highlighted-lines` color must be either a color, a gradient, or a pattern, found: " + str(type(col))
+ )
+
+ (ln, col)
+ }
+
+ while ix < ln {
+ ix += 1
+ highlighted-by-line.push(none)
+ }
+
+ highlighted-by-line.push(col)
+ ix += 1
+ }
+ }
+ } else {
+ panic("codly: incorrect type for highlighted-lines, this shouldn't happen")
+ }
+
+ let has-skips = skips != none and skips != ()
+ let skip-line = (
+ __codly-args.skip-line.type_check
+ )(if "skip-line" in extra {
+ extra.skip-line
+ } else {
+ state("codly-skip-line", __codly-args.skip-line.default).get()
+ })
+
+ let skip-number = (
+ __codly-args.skip-number.type_check
+ )(if "skip-number" in extra {
+ extra.skip-number
+ } else {
+ state("codly-skip-number", __codly-args.skip-number.default).get()
+ })
+
+ let skip-last-empty = (
+ __codly-args.skip-last-empty.type_check
+ )(if "skip-last-empty" in extra {
+ extra.skip-last-empty
+ } else {
+ state("codly-skip-last-empty", __codly-args.skip-last-empty.default).get()
+ })
+
+ let reference-by = (
+ __codly-args.reference-by.type_check
+ )(if "reference-by" in extra {
+ extra.reference-by
+ } else {
+ state("codly-reference-by", __codly-args.reference-by.default).get()
+ })
+
+ if not reference-by in ("line", "item") {
+ panic("codly: reference-by must be either 'line' or 'item'")
+ }
+
+ let reference-sep = (
+ __codly-args.reference-sep.type_check
+ )(if "reference-sep" in extra {
+ extra.reference-sep
+ } else {
+ state("codly-reference-sep", __codly-args.reference-sep.default).get()
+ })
+
+ let reference-number-format = (
+ __codly-args.reference-number-format.type_check
+ )(if "reference-number-format" in extra {
+ extra.reference-number-format
+ } else {
+ state("codly-reference-number-format", __codly-args
+ .reference-number-format
+ .default).get()
+ })
+
+ let annotation-format = {
+ let fn = (
+ __codly-args.annotation-format.type_check
+ )(if "annotation-format" in extra {
+ extra.annotation-format
+ } else {
+ state("codly-annotation-format", __codly-args
+ .annotation-format
+ .default).get()
+ })
+ if fn == none {
+ _ => none
+ } else {
+ fn
+ }
+ }
+
+ let annotations = {
+ let annotations = (
+ __codly-args.annotations.type_check
+ )(if "annotations" in extra {
+ extra.annotations
+ } else {
+ state("codly-annotations", __codly-args.annotations.default).get()
+ })
+ annotations = if annotations == none or annotations.len() == 0 {
+ ()
+ } else {
+ annotations.sorted(key: x => x.start).map(x => {
+ if (not "end" in x) or x.end == none {
+ x.insert("end", x.start)
+ }
+
+ if (not "content" in x) {
+ x.insert("content", none)
+ }
+
+ if "label" in x {
+ if block-label == none {
+ panic("codly: label " + str(x.label) + " is only allowed in a figure block")
+ }
+ }
+
+ x
+ })
+ }
+
+ // Check for overlapping annotations.
+ let current = none
+ for a in annotations {
+ if current != none and a.start <= current.end {
+ panic("codly: overlapping annotations")
+ }
+ current = a
+ }
+
+ annotations
+ }
+ let has-annotations = annotations != none and annotations.len() > 0
+
+ // Get the widest annotation.
+ let annot-bodies-width = annotations
+ .map(x => x.content)
+ .map(measure)
+ .map(x => x.width)
+ let num = annotation-format(annotations.len())
+ let lr-width = if annotations.len() > 0 {
+ measure(math.lr("}", size: 5em) + num).width
+ } else {
+ 0pt
+ }
+
+ let annot-width = annot-bodies-width.fold(
+ 0pt,
+ (a, b) => calc.max(a, b),
+ ) + padding.left + padding.right + lr-width
+
+ // Get the height of an individual line.
+ let line-height = measure(
+ raw.line(1, 1, "X", [X])
+ ).height + padding.top + padding.bottom
+
+ let items = ()
+ let lines_to_number = ()
+ let height = measure[1].height
+ let current-annot = none
+ let first-annot = false
+ let annots = 0
+
+ // prepare the header
+ let header = (__codly-args.header.type_check)(if "header" in extra {
+ extra.header
+ } else {
+ state("codly-header", __codly-args.header.default).get()
+ })
+ let header-repeat = (
+ __codly-args.header-repeat.type_check
+ )(if "header-repeat" in extra {
+ extra.header-repeat
+ } else {
+ state("codly-header-repeat", __codly-args.header-repeat.default).get()
+ })
+
+ // The language block (icon + name)
+ let languages = (
+ __codly-args.languages.type_check
+ )(if "languages" in extra {
+ extra.languages
+ } else {
+ state("codly-languages", __codly-args.languages.default).get()
+ })
+
+ let language-block = if alias == none {
+ none
+ } else if alias in languages {
+ let lang = languages.at(alias)
+ let name = if type(lang) == str {
+ lang
+ } else if display-names and "name" in lang {
+ lang.name
+ } else {
+ []
+ }
+ let icon = if display-icons and "icon" in lang {
+ lang.icon
+ } else {
+ []
+ }
+ let color = if "color" in lang {
+ lang.color
+ } else {
+ default-color
+ }
+
+ if language-block == auto {
+ let radius = (
+ __codly-args.lang-radius.type_check
+ )(if "lang-radius" in extra {
+ extra.lang-radius
+ } else {
+ state("codly-lang-radius", __codly-args.lang-radius.default).get()
+ })
+ let padding = __codly-inset(
+ (__codly-args.lang-inset.type_check)(if "lang-inset" in extra {
+ extra.lang-inset
+ } else {
+ state("codly-lang-inset", __codly-args.lang-inset.default).get()
+ }),
+ )
+
+ let lang-stroke = (
+ __codly-args.lang-stroke.type_check
+ )(if "lang-stroke" in extra {
+ extra.lang-stroke
+ } else {
+ state("codly-lang-stroke", __codly-args.lang-stroke.default).get()
+ })
+ let lang-fill = (
+ __codly-args.lang-fill.type_check
+ )(if "lang-fill" in extra {
+ extra.lang-fill
+ } else {
+ state("codly-lang-fill", __codly-args.lang-fill.default).get()
+ })
+
+ let fill = if type(lang-fill) == function {
+ (lang-fill)((name: name, icon: icon, color: color))
+ } else if type(lang-fill) == color or type(lang-fill) == gradient or type(lang-fill) == pattern {
+ lang-fill
+ } else {
+ color
+ }
+
+ let stroke = if type(lang-stroke) == function {
+ (lang-stroke)((name: name, icon: icon, color: color))
+ } else {
+ lang-stroke
+ }
+
+ let sep = if icon != [] and name != [] {
+ h(0.2em)
+ } else {
+ []
+ }
+ let b = measure(icon + sep + name)
+ box(
+ radius: radius,
+ fill: fill,
+ inset: padding,
+ stroke: stroke,
+ outset: 0pt,
+ height: b.height + padding.top + padding.bottom,
+ icon + sep + name,
+ )
+ } else {
+ (language-block)(name, icon, color)
+ }
+ } else if display-names {
+ if language-block == auto {
+ let radius = (
+ __codly-args.lang-radius.type_check
+ )(if "lang-radius" in extra {
+ extra.lang-radius
+ } else {
+ state("codly-lang-radius", __codly-args.lang-radius.default).get()
+ })
+
+ let padding = __codly-inset(
+ (__codly-args.lang-inset.type_check)(if "lang-inset" in extra {
+ extra.lang-inset
+ } else {
+ state("codly-lang-inset", __codly-args.lang-inset.default).get()
+ }),
+ )
+
+ let lang-stroke = (
+ __codly-args.lang-stroke.type_check
+ )(if "lang-stroke" in extra {
+ extra.lang-stroke
+ } else {
+ state("codly-lang-stroke", __codly-args.lang-stroke.default).get()
+ })
+
+ let lang-fill = (
+ __codly-args.lang-fill.type_check
+ )(if "lang-fill" in extra {
+ extra.lang-fill
+ } else {
+ state("codly-lang-fill", __codly-args.lang-fill.default).get()
+ })
+
+ let fill = if type(lang-fill) == function {
+ (lang-fill)((name: alias, icon: [], color: default-color))
+ } else if type(lang-fill) == color or type(lang-fill) == gradient or type(lang-fill) == pattern {
+ lang-fill
+ } else {
+ default-color
+ }
+
+ let stroke = if type(lang-stroke) == function {
+ (lang-stroke)((name: alias, icon: [], color: default-color))
+ } else {
+ lang-stroke
+ }
+
+ let b = measure(alias)
+ box(
+ radius: radius,
+ fill: fill,
+ inset: padding,
+ stroke: stroke,
+ outset: 0pt,
+ height: b.height + padding.top + padding.bottom,
+ alias,
+ )
+ } else {
+ (language-block)(alias, [], default-color)
+ }
+ }
+
+ // Push the line and the language block in a grid for alignment
+ let language-block-final = place(
+ right + horizon,
+ dx: lang-outset.x,
+ dy: lang-outset.y,
+ language-block,
+ )
+ let lb = measure(language-block)
+
+ let header = if header != none {
+ let header-args = (
+ __codly-args.header-cell-args.type_check
+ )(if "header-cell-args" in extra {
+ extra.header-cell-args
+ } else {
+ state("codly-header-cell-args", __codly-args
+ .header-cell-args
+ .default).get()
+ })
+
+ let transform = (
+ __codly-args.header-transform.type_check
+ )(if "header-transform" in extra {
+ extra.header-transform
+ } else {
+ state("codly-header-transform", __codly-args
+ .header-transform
+ .default).get()
+ })
+
+ lines_to_number.push(-999999999)
+ (
+ grid.header(
+ repeat: header-repeat,
+ grid.cell(
+ transform(header) + language-block-final,
+ colspan: if numbers-format == none { 1 } else { 2 },
+ align: auto,
+ breakable: auto,
+ fill: auto,
+ inset: auto,
+ rowspan: 1,
+ stroke: none,
+ x: auto,
+ y: auto,
+ ..header-args,
+ ),
+ ),
+ )
+ } else {
+ none
+ }
+
+ let smart-skip = (
+ __codly-args.smart-skip.type_check
+ )(if "smart-skip" in extra {
+ extra.smart-skip
+ } else {
+ state("codly-smart-skip", __codly-args
+ .smart-skip
+ .default).get()
+ })
+
+ let smart-skip-top = if type(smart-skip) == bool {
+ smart-skip
+ } else if "first" in smart-skip {
+ smart-skip.first
+ } else if "rest" in smart-skip {
+ smart-skip.rest
+ } else {
+ false
+ }
+
+ let smart-skip-bot = if type(smart-skip) == bool {
+ smart-skip
+ } else if "last" in smart-skip {
+ smart-skip.last
+ } else if "rest" in smart-skip {
+ smart-skip.rest
+ } else {
+ false
+ }
+
+ let smart-skip-rest = if type(smart-skip) == bool {
+ smart-skip
+ } else if "rest" in smart-skip {
+ smart-skip.rest
+ } else {
+ false
+ }
+
+ let smart-skip-enabled = if type(smart-skip) == bool {
+ smart-skip
+ } else {
+ smart-skip.values().any((v) => v)
+ }
+
+ let in-skip = false
+ let in-first = true
+ let had-first = false
+ for line in it.lines {
+ first-annot = false
+
+ // Check for annotations
+ let annot = annotations.at(0, default: none)
+ if annot != none and line.number == annot.start {
+ current-annot = annot
+ first-annot = true
+ annots += 1
+ }
+
+ // Cleanup annotations
+ if current-annot != none and line.number > current-annot.end {
+ current-annot = none
+ _ = annotations.remove(0)
+
+ // Check for annotations again
+ let annot = annotations.at(0, default: none)
+ if annot != none and line.number == annot.start {
+ current-annot = annot
+ first-annot = true
+ annots += 1
+ }
+ }
+
+ // Try and look for a skip
+ let skip = skips.at(0, default: none)
+ if skip != none and line.number == skip.at(0) {
+ if numbers-format != none {
+ items.push(skip-number)
+ }
+
+ items.push(skip-line)
+ lines_to_number.push(-99999999);
+ // Advance the offset.
+ offset += skip.at(1)
+ _ = skips.remove(0)
+ } else if smart-skip-enabled and not in_range(ranges, line.number) and not in-skip {
+ if in-first {
+ if smart-skip-top {
+ if numbers-format != none {
+ items.push(skip-number)
+ }
+ items.push(skip-line)
+ lines_to_number.push(-99999999);
+ }
+ } else if array.range(line.number, line.count).any((i) => in_range(ranges, i)) {
+ if smart-skip-rest {
+ if numbers-format != none {
+ items.push(skip-number)
+ }
+ items.push(skip-line)
+ lines_to_number.push(-99999999);
+ }
+ } else {
+ if smart-skip-bot {
+ if numbers-format != none {
+ items.push(skip-number)
+ }
+ items.push(skip-line)
+ lines_to_number.push(-99999999);
+ }
+ }
+ }
+
+ // Don't include if not in range
+ if not in_range(ranges, line.number) {
+ in-skip = true
+ continue
+ } else {
+ in-skip = false
+ }
+
+ in-first = false
+
+ // Remove the last line if it's empty
+ if skip-last-empty and line.text.trim().len() == 0 and line.number == line.count {
+ continue
+ }
+
+ // Always push the formatted line number
+ let l = figure(
+ kind: "__codly-raw-line",
+ numbering: none,
+ placement: none,
+ outlined: false,
+ gap: 0pt,
+ caption: none,
+ [#raw.line(
+ line.number + offset,
+ line.count,
+ line.text,
+ box(height: height, width: 0pt) + line.body,
+ ) ]
+ )
+
+ lines_to_number.push(line.number + offset)
+
+ // Must be done before the smart indentation code.
+ // Otherwise it results in two paragraphs.
+ if numbers-format != none {
+ items.push(numbers-format(line.number + offset))
+ }
+
+ let annot = none
+ if current-annot != none and first-annot {
+ let height = line-height * (current-annot.end - current-annot.start + 1)
+ let num = annotation-format(annots)
+ let label = if "label" in current-annot and current-annot.label != none {
+ let referenced = if reference-by == "line" {
+ reference-number-format(line.number + offset)
+ } else {
+ num
+ }
+
+ place(hide[#figure(
+ kind: "codly-referencer",
+ supplement: none,
+ numbering: (..) => {
+ ref(block-label)
+ reference-sep
+ referenced
+ },
+ []
+ )#current-annot.label])
+ } else {
+ none
+ }
+
+ let annot-content = {
+ $lr(}, size: #height) #num #current-annot.content #label$
+ }
+
+ annot = grid.cell(
+ rowspan: current-annot.end - current-annot.start + 1,
+ align: left + horizon,
+ breakable: auto,
+ fill: auto,
+ inset: auto,
+ colspan: 1,
+ stroke: none,
+ x: auto,
+ y: auto,
+ annot-content,
+ )
+ }
+
+ if had-first or (
+ display-names != true and display-icons != true
+ ) {
+ if annot == none and has-annotations {
+ items.push(grid.cell(
+ l,
+ colspan: if current-annot == none { 2 } else { 1 },
+ align: auto,
+ breakable: auto,
+ fill: auto,
+ inset: auto,
+ rowspan: 1,
+ stroke: none,
+ x: auto,
+ y: auto,
+ ))
+ } else if annot == none {
+ items.push(l)
+ } else {
+ items.push(l)
+ items.push(annot)
+ }
+ continue
+ } else if alias == none {
+ if annot == none and has-annotations {
+ items.push(grid.cell(
+ l,
+ colspan: if current-annot == none { 2 } else { 1 },
+ align: auto,
+ breakable: auto,
+ fill: auto,
+ inset: auto,
+ rowspan: 1,
+ stroke: none,
+ x: auto,
+ y: auto,
+ ))
+ } else if annot == none {
+ items.push(l)
+ } else {
+ items.push(l)
+ items.push(annot)
+ }
+ continue
+ }
+
+ had-first = true
+ if has-annotations {
+ if header != none {
+ if annot == none and has-annotations {
+ items.push(grid.cell(
+ l,
+ colspan: if current-annot == none { 2 } else { 1 },
+ align: auto,
+ breakable: auto,
+ fill: auto,
+ inset: auto,
+ rowspan: 1,
+ stroke: none,
+ x: auto,
+ y: auto,
+ ))
+ } else if annot == none {
+ items.push(annot)
+ } else {
+ items.push(l)
+ items.push(annot)
+ }
+ } else {
+ // Annotation printing
+ if annot == none and has-annotations {
+ items.push(grid.cell(
+ grid(
+ columns: (1fr, lb.width + padding.left + padding.right),
+ align: left,
+ column-gutter: 0pt,
+ fill: none,
+ gutter: 0pt,
+ inset: 0pt,
+ row-gutter: 0pt,
+ stroke: none,
+ l, language-block-final,
+ ),
+ colspan: if current-annot == none { 2 } else { 1 },
+ align: auto,
+ breakable: auto,
+ fill: auto,
+ inset: auto,
+ rowspan: 1,
+ stroke: none,
+ x: auto,
+ y: auto,
+ ))
+ } else if annot == none {
+ items.push(grid(
+ columns: (1fr, lb.width + padding.left + padding.right),
+ align: left,
+ column-gutter: 0pt,
+ fill: none,
+ gutter: 0pt,
+ inset: 0pt,
+ row-gutter: 0pt,
+ stroke: none,
+ l, language-block-final,
+ ))
+ items = (..items, grid(
+ columns: (1fr, lb.width + padding.left + padding.right),
+ align: left,
+ column-gutter: 0pt,
+ fill: none,
+ gutter: 0pt,
+ inset: 0pt,
+ row-gutter: 0pt,
+ stroke: none,
+ l, language-block-final,
+ ))
+ } else {
+ items.push(grid(
+ columns: (1fr, lb.width + padding.left + padding.right),
+ align: left,
+ column-gutter: 0pt,
+ fill: none,
+ gutter: 0pt,
+ inset: 0pt,
+ row-gutter: 0pt,
+ stroke: none,
+ l, language-block-final,
+ ))
+ items.push(annot)
+ }
+ }
+ } else {
+ if header != none {
+ items.push(l)
+ } else {
+ items.push(
+ grid(
+ columns: (1fr, lb.width + padding.left + padding.right),
+ align: left,
+ column-gutter: 0pt,
+ fill: none,
+ gutter: 0pt,
+ inset: 0pt,
+ row-gutter: 0pt,
+ stroke: none,
+ l, language-block-final,
+ ),
+ )
+ }
+ }
+ }
+
+ // prepare the footer
+ let footer = (__codly-args.footer.type_check)(if "footer" in extra {
+ extra.footer
+ } else {
+ state("codly-footer", __codly-args.footer.default).get()
+ })
+ let footer-repeat = (
+ __codly-args.footer-repeat.type_check
+ )(if "footer-repeat" in extra {
+ extra.footer-repeat
+ } else {
+ state("codly-footer-repeat", __codly-args.footer-repeat.default).get()
+ })
+ let footer = if footer != none {
+ let footer-args = (
+ __codly-args.footer-cell-args.type_check
+ )(if "footer-cell-args" in extra {
+ extra.footer-cell-args
+ } else {
+ state("codly-footer-cell-args", __codly-args
+ .footer-cell-args
+ .default).get()
+ })
+ let transform = (
+ __codly-args.footer-transform.type_check
+ )(if "footer-transform" in extra {
+ extra.footer-transform
+ } else {
+ state("codly-footer-transform", __codly-args
+ .footer-transform
+ .default).get()
+ })
+
+ (
+ grid.footer(
+ repeat: footer-repeat,
+ grid.cell(
+ transform(footer),
+ colspan: if numbers-format == none { 1 } else { 2 },
+ align: auto,
+ breakable: auto,
+ fill: auto,
+ inset: auto,
+ rowspan: 1,
+ stroke: none,
+ x: auto,
+ y: auto,
+ ..footer-args
+ ),
+ ),
+ )
+ } else {
+ ()
+ }
+
+ // If the fill or zebra color is a gradient, we will draw it on a separate layer.
+ let is-complex-fill = (
+ (type(fill) != color and fill != none) or (
+ type(zebra-color) != color and zebra-color != none
+ )
+ )
+
+ let width_lines_number = calc.max(2, (calc.ceil(calc.log(it.lines.len())) + 1)) * 1em
+
+ let line_colors = ()
+ for (i, line) in lines_to_number.enumerate() {
+ let highlighted = highlighted-by-line.at(line - 1, default: none)
+ if highlighted != none {
+ line_colors.push(highlighted)
+ } else if zebra-color != none and calc.rem(i, 2) == 0 {
+ line_colors.push(zebra-color)
+ } else {
+ line_colors.push(fill)
+ }
+ }
+
+ let block_content = block(
+ breakable: breakable,
+ clip: true,
+ width: 100%,
+ radius: radius,
+ stroke: if numbers-outside { none } else { stroke },
+ {
+ if is-complex-fill {
+ // We use place to draw the fill on a separate layer.
+ place(
+ grid(
+ columns: if has-annotations {
+ (1fr, annot-width)
+ } else {
+ (1fr,)
+ },
+ stroke: none,
+ inset: padding.pairs().map(((k, x)) => (k, x * 1.5)).to-dict(),
+ fill: (x, y) => if zebra-color != none and calc.rem(y, 2) == 0 {
+ zebra-color
+ } else {
+ fill
+ },
+ ..header,
+ ..it.lines.map(line => hide(line)),
+ ..footer,
+ ),
+ )
+ }
+
+ if numbers-format != none {
+ grid(
+ columns: if has-annotations {
+ (auto, 1fr, annot-width)
+ } else {
+ (auto, 1fr)
+ },
+ inset: padding.pairs().map(((k, x)) => (k, x * 1.5)).to-dict(),
+ stroke: (x,y) =>
+ if numbers-outside {
+ let idx_end = if has-annotations {
+ 2
+ } else {
+ 1
+ }
+
+ (
+ left: if x == 1 { stroke } else { none },
+ right: if x == idx_end { stroke } else { none },
+ top: if x != 0 and y == 0 { stroke } else { none },
+ bottom: if x != 0 and y == it.lines.len() - 1 { stroke } else { none },
+ )
+ } else {
+ none
+ },
+ align: (numbers-alignment, left + horizon),
+ fill: if is-complex-fill {
+ none
+ } else {
+ (x, y) => if numbers-outside and x == 0 {
+ none
+ } else {
+ line_colors.at(y, default: fill)
+ }
+ },
+ column-gutter: 0pt,
+ gutter: 0pt,
+ row-gutter: 0pt,
+ ..header,
+ ..items,
+ ..footer,
+ )
+ } else {
+ grid(
+ columns: if has-annotations {
+ (1fr, annot-width)
+ } else {
+ (1fr)
+ },
+ inset: padding.pairs().map(((k, x)) => (k, x * 1.5)).to-dict(),
+ stroke: none,
+ align: (numbers-alignment, left + horizon),
+ fill: (x, y) => line_colors.at(y, default: if zebra-color != none and calc.rem(y, 2) == 0 {
+ zebra-color
+ } else {
+ fill
+ }),
+ column-gutter: 0pt,
+ gutter: 0pt,
+ row-gutter: 0pt,
+ ..header,
+ ..items,
+ ..footer,
+ )
+ }
+ },
+ )
+
+ block_content
+
+ figure(
+ kind: "__codly-end-block",
+ supplement: none,
+ numbering: none,
+ placement: none,
+ outlined: false,
+ gap: 0pt,
+ caption: none,
+ )[]
+
+ if offset != 0 {
+ state("codly-offset").update(0)
+ }
+
+ if offset-from != none {
+ state("codly-offset-from").update(none)
+ }
+
+ if range != none {
+ state("codly-range").update(none)
+ }
+
+ if ranges != none {
+ state("codly-ranges").update(none)
+ }
+
+ if has-skips {
+ state("codly-skips").update(())
+ }
+
+ if has-annotations {
+ state("codly-annotations").update(())
+ }
+
+ if header != () {
+ state("codly-header").update(none)
+ }
+
+ if footer != () {
+ state("codly-footer").update(none)
+ }
+
+ let highlights = state("codly-highlights").get()
+ if highlights != none and highlights != () {
+ state("codly-highlights").update(())
+ }
+
+ let highlighted = state("codly-highlighted-lines").get()
+ if highlighted != none and highlighted != () {
+ state("codly-highlighted-lines").update(())
+ }
+}
+
+#let __codly-get-parts(fig) = {
+ let num = fig.body.children.at(0)
+ let lbl = fig.body.children.at(1)
+ (num.body, label(lbl.body.text))
+}
+
+/// Initializes the codly show rule.
+///
+/// ```typ
+/// #show: codly-init
+/// ```
+///
+/// - body (content): the body of the document
+///
+/// -> content
+#let codly-init(
+ body,
+) = {
+ show figure.where(kind: raw): fig => {
+ if fig.has("label") {
+ show raw.where(block: true): it => __codly-raw(it, block-label: fig.label)
+ fig
+ } else {
+ fig
+ }
+ }
+
+ show raw.where(block: true): it => __codly-raw(it)
+
+ body
+}
+
+/// Allows setting codly setting locally.
+/// Anything that happens inside the block will have the settings applied only to it.
+/// The pre-existing settings will be restored after the block. This is useful
+/// if you want to apply settings to a specific block only.
+///
+/// #pre-example()
+/// #example(`````
+/// *Special:*
+/// #local(default-color: red)[
+/// ```
+/// Hello, world!
+/// ```
+/// ]
+///
+/// *Normal:*
+/// ```
+/// Hello, world!
+/// ```
+/// `````, mode: "markup", scale-preview: 100%)
+#let __local-inner(body, nested: false, ..args) = {
+ assert(type(nested) == bool, message: "local: nested must be a boolean, found: " + str(type(nested)))
+ if nested {
+ let extra = args.named()
+ context {
+ let current = state("codly-extra-args", (:)).get()
+ state("codly-extra-args", (:)).update(old => {
+ old + extra
+ })
+
+ let args = current + extra
+ show raw.where(block: true): it => __codly-raw(it, extra: args)
+ show figure.where(kind: raw): fig => {
+ if fig.has("label") {
+ show raw.where(block: true, extra: args): it => __codly-raw(it, block-label: fig.label)
+ fig
+ } else {
+ fig
+ }
+ }
+
+ body
+ state("codly-extra-args").update(current)
+ }
+ } else {
+ let extra = args.named()
+ state("codly-extra-args").update(extra)
+ show raw.where(block: true): it => __codly-raw(it, extra: extra)
+ show figure.where(kind: raw): fig => {
+ if fig.has("label") {
+ show raw.where(block: true, extra: args): it => __codly-raw(it, block-label: fig.label)
+ fig
+ } else {
+ fig
+ }
+ }
+ body
+ state("codly-extra-args").update((:))
+ }
+}
+
+#let typst-icon = (
+ typ: (
+ name: "Typst",
+ icon: box(
+ image("typst-small.png", height: 0.8em),
+ baseline: 0.1em,
+ inset: 0pt,
+ outset: 0pt,
+ ),
+ color: rgb("#239DAD"),
+ ),
+ typc: (
+ name: "Typst code",
+ icon: box(
+ image("typst-small.png", height: 0.8em),
+ baseline: 0.1em,
+ inset: 0pt,
+ outset: 0pt,
+ ),
+ color: rgb("#239DAD"),
+ ),
+)
diff --git a/packages/preview/codly/1.3.1/src/typst-small.png b/packages/preview/codly/1.3.1/src/typst-small.png
new file mode 100644
index 0000000000..24edfbab19
Binary files /dev/null and b/packages/preview/codly/1.3.1/src/typst-small.png differ
diff --git a/packages/preview/codly/1.3.1/typst.toml b/packages/preview/codly/1.3.1/typst.toml
new file mode 100644
index 0000000000..46131f54d2
--- /dev/null
+++ b/packages/preview/codly/1.3.1/typst.toml
@@ -0,0 +1,13 @@
+[package]
+name = "codly"
+version = "1.3.1"
+entrypoint = "codly.typ"
+authors = [
+ "Dherse "
+]
+license = "MIT"
+description = "Codly is a beautiful code presentation template with many features like smart indentation, line numbering, highlighting, etc."
+repository = "https://github.com/Dherse/codly"
+exclude = [ "assets/*", "docs.pdf" ]
+compiler = "0.12.0"
+keywords = [ "code", "pretty", "template", "raw" ]
\ No newline at end of file