start migration to bundle format

This commit is contained in:
Jean-Marie 'Histausse' Mineau 2026-06-17 12:40:20 +02:00
parent 64e270a868
commit 7fe9b99535
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
13 changed files with 378 additions and 470 deletions

View file

@ -4,10 +4,14 @@
/// Generate the html <head> element for the page. /// Generate the html <head> element for the page.
#let html-head( #let html-head(
/// Page url, /// The base url of the site (eg: "http://test.example.com")
url, base-url,
/// The url path of the page (full url of the page is base-url + path), eg: "/index.html"
path,
/// Title of the page /// Title of the page
title, title,
/// Label of the page
page-label,
/// Path to the icon (TODO: /!\ must be an url, typst image not yet supported) /// Path to the icon (TODO: /!\ must be an url, typst image not yet supported)
icon: none, icon: none,
/// Type of the page for open-graph data /// Type of the page for open-graph data
@ -25,6 +29,7 @@
/// List of related sites for metadata /// List of related sites for metadata
me-links: (), me-links: (),
) = { ) = {
let url = base-url + path
html.head({ html.head({
html.meta(charset: "utf-8") html.meta(charset: "utf-8")
if title != none { if title != none {
@ -95,9 +100,9 @@
} }
}} }}
get-css() get-css(page-label)
get-js() get-js(page-label)
context for tag in additionnal-head-tags.final() { context for tag in additionnal-head-tags.at(label(str(page-label) + "-end")) {
tag tag
} }

View file

@ -1,34 +1,7 @@
#import "./states.typ": *
#import "./figures.typ": show-rule-figure #import "./figures.typ": show-rule-figure
#import "./code.typ": show-rule-code-block #import "./code.typ": show-rule-code-block
#let css-list = state("css-list", ())
#let js-list = state("js-list", ())
#let additionnal-head-tags = state("additionnal-head-tags", ())
/// Add string `css` to `css-list` if not already present
#let add-css(css) = context {
css-list.update(x => if css in x { x } else { x + (css,) })
}
/// Add string `js` to `js-list` if not already present
#let add-js(js) = context {
js-list.update(x => if js in x { x } else { x + (js,) })
}
/// Add additionnal html tag to insert in <head>
#let add-tag-in-head(tag) = context {
additionnal-head-tags.update(x => if tag in x { x } else { x + (tag, ) })
}
/// Concatenate all css found in css-list at the end of the document
#let get-css() = context {
html.style(css-list.final().join("\n\n"))
}
/// Concatenate all js found in js-list at the end of the document
#let get-js() = context {
html.script(js-list.final().join("\n\n"))
}
#let html-show(body) = { #let html-show(body) = {
show raw: it => { show raw: it => {

View file

@ -1,7 +1,8 @@
#import "./html_head.typ": html-head #import "./html_head.typ": html-head
#import "./html_body.typ": html-body #import "./html_body.typ": html-body
#import "./html_utils.typ": html-show #import "./html_utils.typ": html-show, reset-page-states
#import "./summary.typ": summary, card-list #import "./summary.typ": summary, card-list
#import "./states.typ": current-page-label
#import "./pyscript.typ": state-use-pyscript, state-pyscript-data-list, state-pyscript-version, state-pyscript-interpreters, state-pyscript-default-interpreter, pyscript-show, pyscript-data #import "./pyscript.typ": state-use-pyscript, state-pyscript-data-list, state-pyscript-version, state-pyscript-interpreters, state-pyscript-default-interpreter, pyscript-show, pyscript-data
#import "./rss.typ": rss #import "./rss.typ": rss
#import "./icons.typ" #import "./icons.typ"
@ -9,8 +10,10 @@
/// Mail template function /// Mail template function
#let webpage( #let webpage(
/// Page url /// The base url of the site (eg: "http://test.example.com")
url: none, base-url: none,
/// The url path of the page (full url of the page is base-url + path), eg: "/index.html"
path: none,
/// Title of the page, default to document.title /// Title of the page, default to document.title
title: none, title: none,
/// Use only for html 'lang' attribute. /// Use only for html 'lang' attribute.
@ -32,6 +35,8 @@
stylesheets: (), stylesheets: (),
/// List of related sites for metadata /// List of related sites for metadata
me-links: (), me-links: (),
/// Label of the page (for links)
page-label: none,
/// Dictionnary of available pyscript-data for each versions of pyscript /// Dictionnary of available pyscript-data for each versions of pyscript
/// expected in the form of ("<version>": pyscript-data-list("<url>", { html.link(...) }), ...) /// expected in the form of ("<version>": pyscript-data-list("<url>", { html.link(...) }), ...)
pyscript-data-list: (:), pyscript-data-list: (:),
@ -56,7 +61,9 @@
/// Body of the page /// Body of the page
body body
) = { ) = {
assert(type(url) == str, message: "A page must have an url") assert(type(base-url) == str, message: "A page must have a base url")
assert(type(path) == str, message: "A page must have a path")
context { context {
state-pyscript-data-list.update(x => pyscript-data-list) state-pyscript-data-list.update(x => pyscript-data-list)
state-pyscript-interpreters.update(x => pyscript-interpreters) state-pyscript-interpreters.update(x => pyscript-interpreters)
@ -65,10 +72,13 @@
} }
show: html-show show: html-show
show raw.where(block: true, lang: "python-run"): pyscript-show show raw.where(block: true, lang: "python-run"): pyscript-show
current-page-label.update(x => page-label)
html.html(lang: lang, { html.html(lang: lang, {
html-head( html-head(
url, base-url,
path,
title, title,
page-label,
icon: icon, icon: icon,
og-type: og-type, og-type: og-type,
site-name: site-name, site-name: site-name,
@ -84,7 +94,11 @@
footer: footer, footer: footer,
nav-elements: nav-elements, nav-elements: nav-elements,
menu: menu, menu: menu,
body [
#body
#metadata("end of page " + str(page-label)) #label(str(page-label) + "-end")
]
) )
reset-page-states()
}) })
} }

View file

@ -1,11 +1,13 @@
#import "./html_utils.typ": add-tag-in-head #import "./html_utils.typ": add-tag-in-head
#import "./states.typ": (
#let state-use-pyscript = state("state-use-pyscript", false) state-use-pyscript,
#let state-pyscript-version = state("state-pyscript-version", none) state-pyscript-version,
#let state-pyscript-data-list = state("state-pyscript-data-list", (:)) state-pyscript-data-list,
#let state-pyscript-interpreters = state("state-pyscript-interpreters", (:)) state-pyscript-interpreters,
#let state-pyscript-default-interpreter = state("state-pyscript-default-interpreter", none) state-pyscript-default-interpreter,
#let state-pyscript-canvas-ids = state("state-pyscript-canvas-ids", ()) state-pyscript-canvas-ids,
current-page-label,
)
/// Define data needed to load a version of pyscript /// Define data needed to load a version of pyscript
#let pyscript-data( #let pyscript-data(
@ -139,7 +141,9 @@
if canvas-attr != none { if canvas-attr != none {
html.elem("canvas", attrs: ("id": canvas-attr)) html.elem("canvas", attrs: ("id": canvas-attr))
let core-js-url = state-pyscript-data-list.final().at(state-pyscript-version.final()).core-js-url let page-label = current-page-label.at(here())
let end-page-label = label(str(page-label) + "-end")
let core-js-url = state-pyscript-data-list.at(end-page-label).at(state-pyscript-version.at(end-page-label)).core-js-url
add-tag-in-head( add-tag-in-head(
html.script( html.script(
type: "module", type: "module",

View file

@ -64,8 +64,8 @@
"item", "item",
children: ( children: (
xml-tag("title", body: it.document-args.title), xml-tag("title", body: it.document-args.title),
xml-tag("link", body: it.template-args.url), xml-tag("link", body: it.template-args.base-url + it.template-args.path),
xml-tag("guid", body: it.template-args.url), xml-tag("guid", body: it.template-args.base-url + it.template-args.path),
xml-tag("description", body: it.document-args.description), xml-tag("description", body: it.document-args.description),
xml-tag("pubDate", body: it.document-args.date.display(date-format-rfc822)), xml-tag("pubDate", body: it.document-args.date.display(date-format-rfc822)),
) + it.template-args.tags.map(tag => xml-tag("category", body: tag)) ) + it.template-args.tags.map(tag => xml-tag("category", body: tag))

57
lib/states.typ Normal file
View file

@ -0,0 +1,57 @@
// Pyscript
#let state-use-pyscript = state("state-use-pyscript", false)
#let state-pyscript-version = state("state-pyscript-version", none)
#let state-pyscript-data-list = state("state-pyscript-data-list", (:))
#let state-pyscript-interpreters = state("state-pyscript-interpreters", (:))
#let state-pyscript-default-interpreter = state("state-pyscript-default-interpreter", none)
#let state-pyscript-canvas-ids = state("state-pyscript-canvas-ids", ())
#let reset-pyscript-page-states() = {
state-use-pyscript.update(x => false)
state-pyscript-version.update(x => state-pyscript-version)
state-pyscript-data-list.update(x => (:))
state-pyscript-interpreters.update(x => (:))
state-pyscript-default-interpreter.update(x => none)
state-pyscript-canvas-ids.update(x => ())
}
// CSS / JS / misc tags
#let current-page-label = state("current-page-label", none)
#let css-list = state("css-list", ())
#let js-list = state("js-list", ())
#let additionnal-head-tags = state("additionnal-head-tags", ())
/// Add string `css` to `css-list` if not already present
#let add-css(css) = context {
css-list.update(x => if css in x { x } else { x + (css,) })
}
/// Add string `js` to `js-list` if not already present
#let add-js(js) = context {
js-list.update(x => if js in x { x } else { x + (js,) })
}
/// Add additionnal html tag to insert in <head>
#let add-tag-in-head(tag) = context {
additionnal-head-tags.update(x => if tag in x { x } else { x + (tag, ) })
}
/// Concatenate all css found in css-list at the end of the document
#let get-css(page-label) = context {
html.style(css-list.at(label(str(page-label) + "-end")).join("\n\n"))
}
/// Concatenate all js found in js-list at the end of the document
#let get-js(page-label) = context {
html.script(js-list.at(label(str(page-label) + "-end")).join("\n\n"))
}
#let reset-page-states() = {
current-page-label.update(x => none)
css-list.update(x => ())
js-list.update(x => ())
additionnal-head-tags.update(x => ())
reset-pyscript-page-states()
}

View file

@ -81,15 +81,15 @@
``` ```
chtml.summary-card({ chtml.summary-card({
chtml.summary-card-preview({ chtml.summary-card-preview({
html.a(href: summ.template-args.url, summ.preview-image) link(summ.template-args.page-label, summ.preview-image)
if summ.img-copyright != none { if summ.img-copyright != none {
html.small[Image: #sym.copyright #summ.img-copyright] html.small[Image: #sym.copyright #summ.img-copyright]
} }
}) })
chtml.summary-card-description({ chtml.summary-card-description({
heading(level: 2, html.a(href: summ.template-args.url, summ.document-args.title)) heading(level: 2, link(summ.template-args.page-label, summ.document-args.title))
html.a(href: summ.template-args.url, summ.document-args.description) link(summ.template-args.page-label, summ.document-args.description)
chtml.summary-card-details(summ.document-args.date.display()) chtml.summary-card-details(summ.document-args.date.display())
chtml.tag-box(for tag in summ.template-args.tags { html.span(class: ("tag",), "#" + tag) }) chtml.tag-box(for tag in summ.template-args.tags { html.span(class: ("tag",), "#" + tag) })
@ -101,8 +101,10 @@
/// Store data form `set document(...)` and `show: webpage.with(...)` in `summ.document-args` and `summ.template-args`, /// Store data form `set document(...)` and `show: webpage.with(...)` in `summ.document-args` and `summ.template-args`,
/// and generate a summary card that can be used as a link for the page. /// and generate a summary card that can be used as a link for the page.
#let summary( #let summary(
/// The url of the page /// The base url of the site (eg: "http://test.example.com")
url: none, base-url: none,
/// The url path of the page (full url of the page is base-url + path), eg: "/index.html"
path: none,
/// The title of the page /// The title of the page
title: none, title: none,
/// The image preview of the page. /// The image preview of the page.
@ -119,14 +121,21 @@
date: none, date: none,
/// Tags associated to the content of the page /// Tags associated to the content of the page
tags: (), tags: (),
/// Label of the page (for links)
page-label: none,
) = { ) = {
assert(type(url) == str, message: "summary() must have an url") assert(type(base-url) == str, message: "summary() must have a base url")
assert(type(path) == str, message: "summary() must have a path")
assert(type(title) == str or type(title) == content, message: "summary() must have title") assert(type(title) == str or type(title) == content, message: "summary() must have title")
assert(type(preview-image) != none, message: "summary() must have a preview-image") assert(type(preview-image) != none, message: "summary() must have a preview-image")
assert(type(author) != none, message: "summary() must have at least one author") assert(type(author) != none, message: "summary() must have at least one author")
assert(type(description) != none, message: "summary() must have a description") assert(type(description) != none, message: "summary() must have a description")
assert(type(date) != none, message: "summary() must have a date") assert(type(date) != none, message: "summary() must have a date")
if page-label == none {
page-label = label(path)
}
let summ = ( let summ = (
document-args: ( document-args: (
title: title, title: title,
@ -135,9 +144,12 @@
date: date, date: date,
), ),
template-args: ( template-args: (
url: url, base-url: base-url,
path: path,
tags: tags, tags: tags,
page-label: page-label,
), ),
path: path,
preview-image: preview-image, preview-image: preview-image,
img-copyright: img-copyright, img-copyright: img-copyright,
card: [] card: []

2
test_template/build.sh Normal file → Executable file
View file

@ -1,4 +1,4 @@
#/usr/bin/env bash
MAIN="$(dirname $(realpath "${0}"))/main.typ" MAIN="$(dirname $(realpath "${0}"))/main.typ"
if [ "${1}" = "watch" ]; then if [ "${1}" = "watch" ]; then

View file

@ -1,11 +1,27 @@
#import "index.typ": summ, summaries
#import "@local/template-web:0.0.1": rss #import "@local/template-web:0.0.1": rss
#import "pages/index.typ": summ as summ-index
#document( #document(
"index.html", "index.html",
..summ.document-args, ..summ-index.document-args,
include "index.typ" include "/pages/index.typ"
) ) <index-page>
#import "pages/pyscript.typ": summ as summ-pyscript
#document(
"pycript.html",
..summ-pyscript.document-args,
include "/pages/pyscript.typ"
) <pyscript-page>
#import "pages/cards.typ": summ as summ-cards, summaries
#document(
"cards.html",
..summ-cards.document-args,
include "/pages/cards.typ"
) <cards-page>
#asset( #asset(
"/img/platypus.png", "/img/platypus.png",
read("assets/platypus.png", encoding: none), read("assets/platypus.png", encoding: none),

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,89 @@
#import "@local/template-web:0.0.1": *
#let summ = summary(
base-url: "http://test.example.com",
path: "/cards.html",
title: "Summary cards",
page-label: <cards-page>,
preview-image: image(
"/assets/platypus.png",
alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.",
),
author: "Me!",
tags: ("test", "html/css", "typst"),
description: "Showcase the summary card system",
date: datetime(year: 1942, month: 4, day: 1),
)
#show: webpage.with(
..summ.template-args,
logo: image(
"/assets/platypus.png",
alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.",
height: 100pt,
width: 100pt, // TODO 0.15 regression? in 0.14 setting the height was enough
),
header: [
= Summary Cards
Showcase summary cards
],
footer: context [
#sym.copyright #document.date.display("[year]") Histausse \
Please don't train AI on my stuff without explicit permission
],
menu: [
- #link(<index-page>)[Home]
- #link(<pyscript-page>)[Demo Pyscript]
- #link(<cards-page>)[Demo the card system]
],
site-name: "TTT",
icon: "/img/platypus.png", // TODO: use <ico> somehow? probably not supported in 0.15
// Pyscript:
pyscript-data-list: (
"remote-2026.3.1": pyscript-data(
"https://pyscript.net/releases/2026.3.1/core.js",
additionnal-head-tags: {
html.elem("script", attrs: (src: "/mini-coi.js")) // TODO: use <mini-coi> somehow? probably not supported in 0.15
html.elem("link", attrs: (rel: "stylesheet", href: "https://pyscript.net/releases/2026.3.1/core.css"))
},
)
),
pyscript-version: "remote-2026.3.1",
)
#summ.card
#let perm = (17, 6, 20, 19, 15, 5, 13, 11, 14, 12, 16, 10, 2, 3, 1, 9, 7, 4, 18, 8)
#let summaries = range(20).map(i =>
summary(
base-url: "http://test.example.com",
path: "/tst/" + str(perm.at(i)),
title: "Card " + str(perm.at(i)),
tags: if perm.at(i) == 20 {
("tag2", "tag5","loooooonnnnnnnnnnnnnng-tag","some-tag","some-other-tag")
} else {
(2, 3, 5, 7).filter(j => calc.rem(perm.at(i), j) == 0).map(j => "tag" + str(j))
},
preview-image: summ.preview-image,
img-copyright: if calc.rem(i, 3) == 0 { [Histausse ] } else { none },
author: "Me!",
description: lorem(10 * calc.rem(i * 123, 10)),
date: datetime(year: 2000, month: 12, day: perm.at(i)),
page-label: label("dummy-label-" + str(perm.at(i))),
)
)
#card-list(
min-width: 200,
summaries,
)
== Card Link Targets
Cards need a label to link to. Usually it is the label of a page, but here we don't generate the pages, to we target elements of this list:
#for i in range(20) [
- Target of card #i #label("dummy-label-" + str(perm.at(i)))
]

View file

@ -1,10 +1,12 @@
#import "@local/template-web:0.0.1": * #import "@local/template-web:0.0.1": *
#let summ = summary( #let summ = summary(
url: "http://test.example.com", base-url: "http://test.example.com",
path: "/index.html",
page-label: <index-page>,
title: "TeTyTe", title: "TeTyTe",
preview-image: image( preview-image: image(
"assets/platypus.png", "/assets/platypus.png",
alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.", alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.",
), ),
author: "Me!", author: "Me!",
@ -16,7 +18,7 @@
#show: webpage.with( #show: webpage.with(
..summ.template-args, ..summ.template-args,
logo: image( logo: image(
"assets/platypus.png", "/assets/platypus.png",
alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.", alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.",
height: 100pt, height: 100pt,
width: 100pt, // TODO 0.15 regression? in 0.14 setting the height was enough width: 100pt, // TODO 0.15 regression? in 0.14 setting the height was enough
@ -35,7 +37,7 @@
link( link(
"http://test.example.com", "http://test.example.com",
image( image(
"assets/platypus.png", "/assets/platypus.png",
alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.", alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.",
height: 2em, height: 2em,
) )
@ -44,113 +46,17 @@
html.a(href: "/feed.rss", aria-label: "RSS feed", icons.rss-icon), html.a(href: "/feed.rss", aria-label: "RSS feed", icons.rss-icon),
), ),
menu: [ menu: [
- #link("example.com")[Hello Void!] - #link(<index-page>)[Home]
- #link("example.com")[Demons] - #link(<pyscript-page>)[Demo Pyscript]
- #link("example.com")[Run] - #link(<cards-page>)[Demo the summary card system]
- #link("example.com")[When]
- #link("example.com")[A]
- #link("example.com")[Good]
- #link("example.com")[Man]
- #link("example.com")[Goes]
- #link("example.com")[To]
- #link("example.com")[War]
], ],
site-name: "TTT", site-name: "TTT",
icon: "/img/platypus.png", // TODO: use <ico> somehow? probably not supported in 0.15 icon: "/img/platypus.png", // TODO: use <ico> somehow? probably not supported in 0.15
// Pyscript:
pyscript-data-list: (
"remote-2026.3.1": pyscript-data(
"https://pyscript.net/releases/2026.3.1/core.js",
additionnal-head-tags: {
html.elem("script", attrs: (src: "/mini-coi.js")) // TODO: use <mini-coi> somehow? probably not supported in 0.15
html.elem("link", attrs: (rel: "stylesheet", href: "https://pyscript.net/releases/2026.3.1/core.css"))
},
)
),
pyscript-version: "remote-2026.3.1",
) )
#lorem(400) #lorem(400)
```python-run
n = ""
while not n.isnumeric():
n = input("enter a valid number: ")
for i in range(1, int(n) + 1):
if i % 3 == 0 and i % 5 == 0:
print("plopliplop")
elif i % 3 == 0:
print("plop")
elif i % 5 == 0:
print("plip")
else:
print(i)
```
```python-run
# /// script
# # requires-python = ">=3.11" # not supported yet
# dependencies = [
# "rich",
# ]
#
# [tool.pyscript]
# repl = true
# [tool.pyscript.files]
# "https://peps.python.org/api/peps.json" = "./peps.json"
# ///
import json
from rich.pretty import pprint
with open("./peps.json") as fd:
data = json.load(fd)
pprint([(k, v["title"]) for k, v in data.items()][:10])
# >>> print(data["723"]["title"])
# Inline script metadata
```
```python-run
# /// script
# # requires-python = ">=3.11" # not supported yet
# dependencies = [
# "rich",
# ]
#
# [tool.pyscript]
# repl = true
# hide-meta = true
# [tool.pyscript.files]
# "https://peps.python.org/api/peps.json" = "./peps.json"
# ///
# setting tool.pyscript.hide-meta to true will hide the `/// script` section
import json
from rich.pretty import pprint
with open("./peps.json") as fd:
data = json.load(fd)
pprint([(k, v["title"]) for k, v in data.items()][:10])
# >>> print(data["723"]["title"])
# Inline script metadata
```
```python-run
# /// script
# dependencies = [
# "pygame-ce",
# "./python-packages/isn_s_cube-0.1.0-py3-none-any.whl"
# ]
# [tool.pyscript]
# pygame = true
# ///
from isn_s_cube import wasm
await wasm()
```
#summ.card
Test, `this is not a code block`, end test. Test, `this is not a code block`, end test.
```python ```python
@ -168,7 +74,6 @@ def plopliplop(n: int)
#raw(range(10).map(i => lorem(100)).join("\n"), block: true) #raw(range(10).map(i => lorem(100)).join("\n"), block: true)
/*
#figure({ #figure({
show table: set text(size: 0.80em) show table: set text(size: 0.80em)
table( table(
@ -199,7 +104,6 @@ def plopliplop(n: int)
caption: [Comparison of API methods between documentation and emulators], caption: [Comparison of API methods between documentation and emulators],
)<tab:cl-platform_apis> )<tab:cl-platform_apis>
*/
```raw-css ```raw-css
table { table {
@ -220,7 +124,6 @@ thead {
border-bottom: 1px solid #999999; border-bottom: 1px solid #999999;
} }
``` ```
/*
#table( #table(
columns: 5, columns: 5,
//inset: (x: 0% + 5pt, y: 0% + 2pt), //inset: (x: 0% + 5pt, y: 0% + 2pt),
@ -238,41 +141,19 @@ thead {
[34], [605106], [605098], [26], [18], [34], [605106], [605098], [26], [18],
table.hline(), table.hline(),
) )
*/
#figure( #figure(
image( image(
"assets/smol-platypus.png", "/assets/smol-platypus.png",
alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.", alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.",
), ),
caption: [A Platypus!] caption: [A Platypus!]
) )
#figure( #figure(
image( image(
"assets/big-platypus.png", "/assets/big-platypus.png",
alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.", alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.",
), ),
caption: [A Big Platypus!] caption: [A Big Platypus!]
) )
#let perm = (17, 6, 20, 19, 15, 5, 13, 11, 14, 12, 16, 10, 2, 3, 1, 9, 7, 4, 18, 8)
#let summaries = range(20).map(i =>
summary(
url: "http://test.example.com/tst/" + str(perm.at(i)),
title: "Card " + str(perm.at(i)),
tags: if perm.at(i) == 20 {
("tag2", "tag5","loooooonnnnnnnnnnnnnng-tag","some-tag","some-other-tag")
} else {
(2, 3, 5, 7).filter(j => calc.rem(perm.at(i), j) == 0).map(j => "tag" + str(j))
},
preview-image: summ.preview-image,
img-copyright: if calc.rem(i, 3) == 0 { [Histausse ] } else { none },
author: "Me!",
description: lorem(10 * calc.rem(i * 123, 10)),
date: datetime(year: 2000, month: 12, day: perm.at(i)),
)
)
#card-list(
min-width: 200,
summaries,
)

View file

@ -0,0 +1,129 @@
#import "@local/template-web:0.0.1": *
#let summ = summary(
base-url: "http://test.example.com",
path: "/pycript.html",
title: "TeTyTe",
page-label: <pyscript-label>,
preview-image: image(
"/assets/platypus.png",
alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.",
),
author: "Me!",
tags: ("test", "html/css", "typst", "pyscript"),
description: "Demonstrate how to use pyscript to run python in the browser",
date: datetime(year: 1942, month: 4, day: 1),
)
#show: webpage.with(
..summ.template-args,
logo: image(
"/assets/platypus.png",
alt: "A drawing of a blue-ish round-ish platypus with big eyes, holding a laptop. This platypus is quite cute, but I might be biased.",
height: 100pt,
width: 100pt, // TODO 0.15 regression? in 0.14 setting the height was enough
),
header: [
= Showcase Pyscript
Show how to run Python in the browser
],
footer: context [
#sym.copyright #document.date.display("[year]") Histausse \
Please don't train AI on my stuff without explicit permission
],
site-name: "TTT",
icon: "/img/platypus.png", // TODO: use <ico> somehow? probably not supported in 0.15
menu: [
- #link(<index-page>)[Home]
- #link(<pyscript-page>)[Demo Pyscript]
- #link(<cards-page>)[Demo the summary card system]
],
// Pyscript:
pyscript-data-list: (
"remote-2026.3.1": pyscript-data(
"https://pyscript.net/releases/2026.3.1/core.js",
additionnal-head-tags: {
html.elem("script", attrs: (src: "/mini-coi.js")) // TODO: use <mini-coi> somehow? probably not supported in 0.15
html.elem("link", attrs: (rel: "stylesheet", href: "https://pyscript.net/releases/2026.3.1/core.css"))
},
)
),
pyscript-version: "remote-2026.3.1",
)
```python-run
n = ""
while not n.isnumeric():
n = input("enter a valid number: ")
for i in range(1, int(n) + 1):
if i % 3 == 0 and i % 5 == 0:
print("plopliplop")
elif i % 3 == 0:
print("plop")
elif i % 5 == 0:
print("plip")
else:
print(i)
```
```python-run
# /// script
# # requires-python = ">=3.11" # not supported yet
# dependencies = [
# "rich",
# ]
#
# [tool.pyscript]
# repl = true
# [tool.pyscript.files]
# "https://peps.python.org/api/peps.json" = "./peps.json"
# ///
import json
from rich.pretty import pprint
with open("./peps.json") as fd:
data = json.load(fd)
pprint([(k, v["title"]) for k, v in data.items()][:10])
# >>> print(data["723"]["title"])
# Inline script metadata
```
```python-run
# /// script
# # requires-python = ">=3.11" # not supported yet
# dependencies = [
# "rich",
# ]
#
# [tool.pyscript]
# repl = true
# hide-meta = true
# [tool.pyscript.files]
# "https://peps.python.org/api/peps.json" = "./peps.json"
# ///
# setting tool.pyscript.hide-meta to true will hide the `/// script` section
import json
from rich.pretty import pprint
with open("./peps.json") as fd:
data = json.load(fd)
pprint([(k, v["title"]) for k, v in data.items()][:10])
# >>> print(data["723"]["title"])
# Inline script metadata
```
```python-run
# /// script
# dependencies = [
# "pygame-ce",
# "./python-packages/isn_s_cube-0.1.0-py3-none-any.whl"
# ]
# [tool.pyscript]
# pygame = true
# ///
from isn_s_cube import wasm
await wasm()
```