fix(presentation): finishing touches
add progress bar, tweak animations, adjust spacing, add conclusion
This commit is contained in:
10 files changed
+260
-55
No files matched your search
@@ -0,0 +1,2 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<svg width="41mm" height="41mm" version="1.1" viewBox="0 0 41 41" xmlns="http://www.w3.org/2000/svg"><path d="M4,4H5V5H4zM5,4H6V5H5zM6,4H7V5H6zM7,4H8V5H7zM8,4H9V5H8zM9,4H10V5H9zM10,4H11V5H10zM14,4H15V5H14zM17,4H18V5H17zM19,4H20V5H19zM22,4H23V5H22zM24,4H25V5H24zM25,4H26V5H25zM27,4H28V5H27zM30,4H31V5H30zM31,4H32V5H31zM32,4H33V5H32zM33,4H34V5H33zM34,4H35V5H34zM35,4H36V5H35zM36,4H37V5H36zM4,5H5V6H4zM10,5H11V6H10zM12,5H13V6H12zM14,5H15V6H14zM15,5H16V6H15zM16,5H17V6H16zM18,5H19V6H18zM21,5H22V6H21zM24,5H25V6H24zM27,5H28V6H27zM28,5H29V6H28zM30,5H31V6H30zM36,5H37V6H36zM4,6H5V7H4zM6,6H7V7H6zM7,6H8V7H7zM8,6H9V7H8zM10,6H11V7H10zM12,6H13V7H12zM13,6H14V7H13zM22,6H23V7H22zM23,6H24V7H23zM27,6H28V7H27zM30,6H31V7H30zM32,6H33V7H32zM33,6H34V7H33zM34,6H35V7H34zM36,6H37V7H36zM4,7H5V8H4zM6,7H7V8H6zM7,7H8V8H7zM8,7H9V8H8zM10,7H11V8H10zM13,7H14V8H13zM14,7H15V8H14zM15,7H16V8H15zM17,7H18V8H17zM18,7H19V8H18zM19,7H20V8H19zM20,7H21V8H20zM22,7H23V8H22zM23,7H24V8H23zM25,7H26V8H25zM27,7H28V8H27zM28,7H29V8H28zM30,7H31V8H30zM32,7H33V8H32zM33,7H34V8H33zM34,7H35V8H34zM36,7H37V8H36zM4,8H5V9H4zM6,8H7V9H6zM7,8H8V9H7zM8,8H9V9H8zM10,8H11V9H10zM14,8H15V9H14zM17,8H18V9H17zM19,8H20V9H19zM21,8H22V9H21zM22,8H23V9H22zM26,8H27V9H26zM27,8H28V9H27zM28,8H29V9H28zM30,8H31V9H30zM32,8H33V9H32zM33,8H34V9H33zM34,8H35V9H34zM36,8H37V9H36zM4,9H5V10H4zM10,9H11V10H10zM13,9H14V10H13zM14,9H15V10H14zM16,9H17V10H16zM17,9H18V10H17zM18,9H19V10H18zM19,9H20V10H19zM22,9H23V10H22zM25,9H26V10H25zM26,9H27V10H26zM27,9H28V10H27zM30,9H31V10H30zM36,9H37V10H36zM4,10H5V11H4zM5,10H6V11H5zM6,10H7V11H6zM7,10H8V11H7zM8,10H9V11H8zM9,10H10V11H9zM10,10H11V11H10zM12,10H13V11H12zM14,10H15V11H14zM16,10H17V11H16zM18,10H19V11H18zM20,10H21V11H20zM22,10H23V11H22zM24,10H25V11H24zM26,10H27V11H26zM28,10H29V11H28zM30,10H31V11H30zM31,10H32V11H31zM32,10H33V11H32zM33,10H34V11H33zM34,10H35V11H34zM35,10H36V11H35zM36,10H37V11H36zM14,11H15V12H14zM15,11H16V12H15zM16,11H17V12H16zM17,11H18V12H17zM18,11H19V12H18zM19,11H20V12H19zM21,11H22V12H21zM22,11H23V12H22zM23,11H24V12H23zM28,11H29V12H28zM5,12H6V13H5zM6,12H7V13H6zM7,12H8V13H7zM9,12H10V13H9zM10,12H11V13H10zM15,12H16V13H15zM16,12H17V13H16zM17,12H18V13H17zM22,12H23V13H22zM25,12H26V13H25zM26,12H27V13H26zM27,12H28V13H27zM34,12H35V13H34zM35,12H36V13H35zM4,13H5V14H4zM8,13H9V14H8zM11,13H12V14H11zM12,13H13V14H12zM13,13H14V14H13zM14,13H15V14H14zM15,13H16V14H15zM19,13H20V14H19zM20,13H21V14H20zM21,13H22V14H21zM22,13H23V14H22zM24,13H25V14H24zM26,13H27V14H26zM27,13H28V14H27zM30,13H31V14H30zM31,13H32V14H31zM33,13H34V14H33zM34,13H35V14H34zM4,14H5V15H4zM6,14H7V15H6zM7,14H8V15H7zM8,14H9V15H8zM10,14H11V15H10zM11,14H12V15H11zM12,14H13V15H12zM15,14H16V15H15zM19,14H20V15H19zM20,14H21V15H20zM22,14H23V15H22zM25,14H26V15H25zM26,14H27V15H26zM27,14H28V15H27zM30,14H31V15H30zM31,14H32V15H31zM32,14H33V15H32zM33,14H34V15H33zM35,14H36V15H35zM36,14H37V15H36zM4,15H5V16H4zM5,15H6V16H5zM6,15H7V16H6zM7,15H8V16H7zM12,15H13V16H12zM16,15H17V16H16zM18,15H19V16H18zM19,15H20V16H19zM20,15H21V16H20zM21,15H22V16H21zM24,15H25V16H24zM25,15H26V16H25zM26,15H27V16H26zM27,15H28V16H27zM28,15H29V16H28zM30,15H31V16H30zM31,15H32V16H31zM33,15H34V16H33zM35,15H36V16H35zM7,16H8V17H7zM9,16H10V17H9zM10,16H11V17H10zM11,16H12V17H11zM12,16H13V17H12zM15,16H16V17H15zM16,16H17V17H16zM17,16H18V17H17zM18,16H19V17H18zM20,16H21V17H20zM23,16H24V17H23zM24,16H25V17H24zM25,16H26V17H25zM26,16H27V17H26zM27,16H28V17H27zM32,16H33V17H32zM33,16H34V17H33zM35,16H36V17H35zM6,17H7V18H6zM7,17H8V18H7zM8,17H9V18H8zM9,17H10V18H9zM16,17H17V18H16zM18,17H19V18H18zM21,17H22V18H21zM24,17H25V18H24zM27,17H28V18H27zM30,17H31V18H30zM31,17H32V18H31zM33,17H34V18H33zM35,17H36V18H35zM5,18H6V19H5zM7,18H8V19H7zM10,18H11V19H10zM11,18H12V19H11zM12,18H13V19H12zM14,18H15V19H14zM15,18H16V19H15zM16,18H17V19H16zM18,18H19V19H18zM23,18H24V19H23zM24,18H25V19H24zM25,18H26V19H25zM26,18H27V19H26zM28,18H29V19H28zM30,18H31V19H30zM31,18H32V19H31zM34,18H35V19H34zM4,19H5V20H4zM5,19H6V20H5zM6,19H7V20H6zM7,19H8V20H7zM9,19H10V20H9zM12,19H13V20H12zM13,19H14V20H13zM15,19H16V20H15zM18,19H19V20H18zM19,19H20V20H19zM22,19H23V20H22zM25,19H26V20H25zM28,19H29V20H28zM29,19H30V20H29zM30,19H31V20H30zM31,19H32V20H31zM34,19H35V20H34zM4,20H5V21H4zM5,20H6V21H5zM9,20H10V21H9zM10,20H11V21H10zM12,20H13V21H12zM14,20H15V21H14zM15,20H16V21H15zM16,20H17V21H16zM17,20H18V21H17zM18,20H19V21H18zM24,20H25V21H24zM25,20H26V21H25zM27,20H28V21H27zM29,20H30V21H29zM30,20H31V21H30zM32,20H33V21H32zM33,20H34V21H33zM34,20H35V21H34zM4,21H5V22H4zM7,21H8V22H7zM8,21H9V22H8zM13,21H14V22H13zM15,21H16V22H15zM17,21H18V22H17zM18,21H19V22H18zM20,21H21V22H20zM21,21H22V22H21zM27,21H28V22H27zM29,21H30V22H29zM30,21H31V22H30zM32,21H33V22H32zM33,21H34V22H33zM8,22H9V23H8zM10,22H11V23H10zM12,22H13V23H12zM13,22H14V23H13zM16,22H17V23H16zM17,22H18V23H17zM18,22H19V23H18zM19,22H20V23H19zM20,22H21V23H20zM25,22H26V23H25zM28,22H29V23H28zM29,22H30V23H29zM30,22H31V23H30zM31,22H32V23H31zM32,22H33V23H32zM34,22H35V23H34zM35,22H36V23H35zM5,23H6V24H5zM6,23H7V24H6zM9,23H10V24H9zM11,23H12V24H11zM12,23H13V24H12zM14,23H15V24H14zM15,23H16V24H15zM16,23H17V24H1Line truncated
|
||||
|
After Width: | Height: | Size: 8.5 KiB |
+6
-2
@@ -4,14 +4,18 @@ from qrcode.image.svg import SvgPathImage
|
||||
|
||||
|
||||
def main():
|
||||
urls: dict[str, str] = {
|
||||
urls: dict[str, str | tuple[str, int | None]] = {
|
||||
"gitea": "https://git.kb28.ch/HEL/midas",
|
||||
"github": "https://github.com/LordBaryhobal/midas",
|
||||
"docs": "https://git.kb28.ch/HEL/TB-Docs",
|
||||
}
|
||||
|
||||
factory = SvgPathImage
|
||||
for name, url in urls.items():
|
||||
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_Q, version=4)
|
||||
version: int | None = 4
|
||||
if isinstance(url, tuple):
|
||||
url, version = url
|
||||
qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_Q, version=version)
|
||||
qr.add_data(url)
|
||||
qr.make()
|
||||
img = qr.make_image(image_factory=factory)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#import "../meta.typ"
|
||||
#import codly: codly-init, codly
|
||||
#import "@preview/codly-languages:0.1.10": codly-languages
|
||||
#import "@preview/tiaoma:0.3.0"
|
||||
|
||||
#pdfpc.config(
|
||||
start-time: datetime(hour: 13, minute: 15, second: 0),
|
||||
@@ -43,14 +44,38 @@
|
||||
//subtitle: meta.subtitle,
|
||||
//short-title: [Midas],
|
||||
//short-subtitle: [Hybrid Type Checking for Python],
|
||||
logo: image("figs/isc_logo.svg"),
|
||||
//logo: image("figs/isc_logo.svg"),
|
||||
logo: {
|
||||
image("figs/isc_logo.svg")
|
||||
place(
|
||||
left + horizon,
|
||||
dx: 100% + 1em,
|
||||
clearance: 0pt,
|
||||
//scale(80%, tiaoma.qrcode("https://url.kb28.ch/nws0"))
|
||||
scale(80%, tiaoma.qrcode("https://git.kb28.ch/HEL/TB-Docs/releases/download/official/presentation_handout.pdf"))
|
||||
)
|
||||
},
|
||||
),
|
||||
config-common(
|
||||
enable-pdfpc: true,
|
||||
handout: sys.inputs.at("handout", default: "false") == "true",
|
||||
slide-fn: slide,
|
||||
),
|
||||
config-store(
|
||||
font: "Source Sans Pro"
|
||||
)
|
||||
footer-appendix-label: "A",
|
||||
show-header: {
|
||||
place(
|
||||
top + left,
|
||||
utils.touying-progress(
|
||||
ratio => box(
|
||||
width: ratio * 100%,
|
||||
height: 6pt,
|
||||
fill: orange.C
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
#title-slide(
|
||||
@@ -64,3 +89,5 @@
|
||||
#include "sections/02_state_of_the_art.typ"
|
||||
#include "sections/03_midas.typ"
|
||||
#include "sections/04_status_and_future.typ"
|
||||
#include "sections/05_conclusion.typ"
|
||||
#include "sections/06_appendix.typ"
|
||||
@@ -1,3 +1,114 @@
|
||||
#import "@preview/touying:0.7.4": *
|
||||
#import "@preview/touying-unistra-pristine:1.4.3": *
|
||||
#import "@local/codly:1.3.1"
|
||||
#import "@local/codly:1.3.1"
|
||||
|
||||
// Adapted from:
|
||||
// https://github.com/spidersouris/touying-unistra-pristine/blob/67084e00529991bfbd12152c0f074c582cba0093/src/unistra.typ#L22-L126
|
||||
#let slide(
|
||||
config: (:),
|
||||
repeat: auto,
|
||||
setting: body => body,
|
||||
composer: auto,
|
||||
..bodies,
|
||||
) = touying-slide-wrapper(self => {
|
||||
let footer(self) = {
|
||||
let cell(body) = rect(
|
||||
width: 100%,
|
||||
height: 100%,
|
||||
inset: 0mm,
|
||||
outset: 0mm,
|
||||
fill: none,
|
||||
stroke: none,
|
||||
text(size: 0.5em, fill: self.colors.black, body),
|
||||
)
|
||||
|
||||
let author = self.info.author
|
||||
let date = self.info.date
|
||||
if self.store.footer-hide.contains("author") {
|
||||
author = none
|
||||
}
|
||||
|
||||
if self.store.footer-hide.contains("date") {
|
||||
date = none
|
||||
}
|
||||
|
||||
set align(center + horizon)
|
||||
block(width: 101%, height: -25%, stroke: (top: 0.5pt + self.colors.black), {
|
||||
set text(size: 1.5em)
|
||||
|
||||
// give priority to short, since long is also used in title slide
|
||||
let title = self.info.title
|
||||
if (self.info.short-title != auto) {
|
||||
title = self.info.short-title
|
||||
}
|
||||
|
||||
let first-col-width = 15%
|
||||
let second-col-width = 75%
|
||||
|
||||
grid(
|
||||
columns: (first-col-width, second-col-width, 10%),
|
||||
rows: 0.5em,
|
||||
stroke: (x: 1pt + self.colors.black),
|
||||
cell(box(self.info.logo, height: 100%)),
|
||||
cell(box(
|
||||
width: 100%,
|
||||
text(
|
||||
title, // either title or short-title
|
||||
weight: "bold",
|
||||
)
|
||||
+ self.store.footer-first-sep
|
||||
+ if _is(author) { author }
|
||||
+ if _is(date) and _is(author) {
|
||||
self.store.footer-second-sep
|
||||
} else { "" }
|
||||
+ if _is(date) { date },
|
||||
)),
|
||||
cell(utils.call-or-display(self, context {
|
||||
let current = int(utils.slide-counter.display())
|
||||
let last = int(utils.last-slide-counter.display())
|
||||
if current > last {
|
||||
(
|
||||
text(self.store.footer-appendix-label, style: "italic")
|
||||
+ str(current)
|
||||
)
|
||||
} else {
|
||||
str(current)
|
||||
}
|
||||
}
|
||||
+ " / "
|
||||
+ utils.last-slide-number)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
let store-header = self.store.show-header
|
||||
let store-footer = self.store.show-footer
|
||||
|
||||
let self = utils.merge-dicts(self, config-page(
|
||||
header: if store-header == true {
|
||||
unistra-nav-bar(self)
|
||||
} else if store-header == false {
|
||||
none
|
||||
} else {
|
||||
store-header
|
||||
},
|
||||
footer: if store-footer == true {
|
||||
footer
|
||||
} else if store-footer == false {
|
||||
none
|
||||
} else {
|
||||
store-footer
|
||||
},
|
||||
// todo: change if no footer/header, etc.
|
||||
margin: (x: 3em, y: 2em),
|
||||
))
|
||||
|
||||
touying-slide(
|
||||
self: self,
|
||||
config: config,
|
||||
repeat: repeat,
|
||||
setting: setting,
|
||||
composer: composer,
|
||||
..bodies,
|
||||
)
|
||||
})
|
||||
@@ -1,16 +1,21 @@
|
||||
#import "../requirements.typ": *
|
||||
|
||||
#focus-slide(theme: "berry")[The Issue]
|
||||
// = The issue
|
||||
= The issue
|
||||
|
||||
== A popular language
|
||||
|
||||
#align(center, image("../figs/python-logo.svg"))
|
||||
|
||||
Python is a widely used language, especially in data science:
|
||||
- Easy to learn
|
||||
- Simple syntax
|
||||
- Vast and thriving ecosystem
|
||||
#item-by-item[
|
||||
|
||||
- Easy to learn
|
||||
|
||||
- Simple syntax
|
||||
|
||||
- Vast and thriving ecosystem
|
||||
]
|
||||
|
||||
== It can bite
|
||||
|
||||
@@ -21,11 +26,13 @@ transactions = load_transactions()
|
||||
mean_amount = np.mean(transactions)
|
||||
```
|
||||
|
||||
// Il y a comme un couac -> duck-typing
|
||||
#speaker-note[
|
||||
Il y a comme un couac -> duck-typing
|
||||
]
|
||||
|
||||
#pause
|
||||
|
||||
Until its not
|
||||
Until it's not
|
||||
|
||||
```python
|
||||
def load_transactions():
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
#import "../requirements.typ": *
|
||||
|
||||
#focus-slide(theme: "mandarine")[What's Out There?]
|
||||
// = What's out there?
|
||||
= What's out there?
|
||||
|
||||
#gallery(
|
||||
== Existing Type Checkers
|
||||
|
||||
#v(1fr)
|
||||
#grid(
|
||||
columns: (1fr, 1fr),
|
||||
column-gutter: 3em,
|
||||
align: center + horizon,
|
||||
image("../figs/mypy_logo.svg"),
|
||||
image("../figs/pyright-logo.png"),
|
||||
title: [Existing Type Checkers],
|
||||
columns: 2,
|
||||
gutter: 3em,
|
||||
)
|
||||
#v(1fr)
|
||||
|
||||
== Community Packages for Pandas
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#import "@preview/conch:0.1.0"
|
||||
|
||||
#focus-slide(theme: "forest")[Making Python Better]
|
||||
// = Making Python Better
|
||||
= Making Python Better
|
||||
|
||||
== Strict Typing Rules
|
||||
|
||||
@@ -428,15 +428,15 @@ Steps:
|
||||
unknown = fetch_data()
|
||||
speed = cast(Meter, unknown)
|
||||
```,
|
||||
rotate(-90deg, reflow: true)[*Generated*],
|
||||
```python
|
||||
uncover("2-", rotate(-90deg, reflow: true)[*Generated*]),
|
||||
uncover("2-", ```python
|
||||
from lib import fetch_data
|
||||
unknown = fetch_data()
|
||||
__midas_a0__ = unknown
|
||||
assert isinstance(__midas_a0__, float), f'script.py:L3:9: CastError: Cannot cast {type(__midas_a0__).__name__} to float'
|
||||
speed = __midas_a0__
|
||||
del __midas_a0__
|
||||
```
|
||||
```)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -519,10 +519,7 @@ Generates runtime checks
|
||||
#figure(
|
||||
align(
|
||||
left + top,
|
||||
alternatives(
|
||||
make-cast-error(python-outputs.data1),
|
||||
make-cast-error(python-outputs.data2),
|
||||
)
|
||||
make-cast-error(python-outputs.data2),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -2,49 +2,60 @@
|
||||
#import codly: codly
|
||||
|
||||
#focus-slide(theme: "neon")[Current Status and Future]
|
||||
// = Current Status and Future
|
||||
= Current Status and Future
|
||||
|
||||
== What Midas offers
|
||||
|
||||
- Working *static type checking* of a good subset of Python (including dataframes)
|
||||
#v(-0.5em)
|
||||
#item-by-item[
|
||||
|
||||
- *Custom type* definition language for domain-specific usages (supports generics, method overloads, dependent types, frame schemas, named predicates and more)
|
||||
- Working *static type checking* of a good subset of Python (including dataframes)
|
||||
|
||||
- Toolbox *CLI* with useful debug features
|
||||
- *Custom type* definition language for domain-specific usages (supports generics, method overloads, dependent types, frame schemas, named predicates and more)
|
||||
|
||||
- Generation of *runtime assertions*
|
||||
- Toolbox *CLI* with useful debug features
|
||||
|
||||
- Stubs for *seamless integration* with other type checkers
|
||||
- Generation of *runtime assertions*
|
||||
|
||||
- Stubs for *seamless integration* with other type checkers
|
||||
]
|
||||
|
||||
== What needs work
|
||||
|
||||
- Reference types (side effects)
|
||||
```python
|
||||
def modify_column(df: pd.DataFrame):
|
||||
df["col"] = df["col"].astype(float)
|
||||
```
|
||||
- Reverse operators
|
||||
```python
|
||||
a = 1.0 + 1 # float.__add__(int)
|
||||
b = 1 + 1.0 # float.__radd__(int)
|
||||
```
|
||||
- Logical short-circuit bypass
|
||||
#codly(
|
||||
highlights: (
|
||||
(
|
||||
line: 1,
|
||||
start: 15,
|
||||
tag: box(inset: (x: 0.5em))[_Always evaluated_ ],
|
||||
fill: std.red.lighten(20%)
|
||||
),
|
||||
)
|
||||
)
|
||||
```python
|
||||
value = v1 or cast(Type, v2)
|
||||
```
|
||||
#{
|
||||
show raw: set text(size: 0.85em)
|
||||
item-by-item[
|
||||
- Reference types (side effects)
|
||||
```python
|
||||
def modify_column(df: pd.DataFrame):
|
||||
df["col"] = df["col"].astype(float)
|
||||
```
|
||||
- Reverse operators
|
||||
```python
|
||||
a = 1.0 + 1 # float.__add__(int)
|
||||
b = 1 + 1.0 # float.__radd__(int)
|
||||
```
|
||||
- Logical short-circuit bypass
|
||||
#codly(
|
||||
highlights: (
|
||||
(
|
||||
line: 1,
|
||||
start: 15,
|
||||
tag: box(inset: (x: 0.5em))[_Always evaluated_ ],
|
||||
fill: std.red.lighten(20%)
|
||||
),
|
||||
)
|
||||
)
|
||||
```python
|
||||
value = v1 or cast(Type, v2)
|
||||
```
|
||||
]
|
||||
}
|
||||
|
||||
== Possible extensions
|
||||
|
||||
#item-by-item[
|
||||
|
||||
- Visitor pattern $->$ easy to extend supported Python syntax
|
||||
|
||||
- Multi-file projects (```python import``` statements)
|
||||
@@ -53,3 +64,4 @@
|
||||
$
|
||||
v in [1; 6] => v > 0
|
||||
$
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
#import "../requirements.typ": *
|
||||
|
||||
#focus-slide(theme: "smoke")[Thanks for listening !]
|
||||
= Thanks for listening !
|
||||
|
||||
== Repositories
|
||||
|
||||
#let repos = (
|
||||
([Code], path("../../poster/figs/qr_gitea.svg"), "https://git.kb28.ch/HEL/midas"),
|
||||
([Code (mirror)], path("../../poster/figs/qr_github.svg"), "https://github.com/LordBaryhobal/midas"),
|
||||
([Docs], path("../../poster/figs/qr_docs.svg"), "https://git.kb28.ch/HEL/TB-Docs"),
|
||||
)
|
||||
|
||||
#let repo(name, code, url) = {
|
||||
stack(
|
||||
dir: ttb,
|
||||
spacing: 0.2em,
|
||||
strong(text(size: 0.8em, name)),
|
||||
image(code, width: 3cm),
|
||||
{
|
||||
set text(size: 0.6em)
|
||||
link(url)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#v(1fr)
|
||||
|
||||
#grid(
|
||||
columns: (1fr, 1fr),
|
||||
align: center + horizon,
|
||||
row-gutter: 2em,
|
||||
repo(..repos.at(0)),
|
||||
repo(..repos.at(1)),
|
||||
grid.cell(colspan: 2, repo(..repos.at(2)))
|
||||
)
|
||||
|
||||
#v(1fr)
|
||||
@@ -0,0 +1,3 @@
|
||||
#import "../requirements.typ": *
|
||||
|
||||
#show: appendix
|
||||
Reference in new issue
Block a user