From 3be22c5654ced6cd04ef81d08ebe499588d7e6e5 Mon Sep 17 00:00:00 2001 From: Jean-Marie 'Histausse' Mineau Date: Sun, 7 Jun 2026 15:57:09 +0200 Subject: [PATCH] add support for pygame scripts --- lib/html_head.typ | 19 +- lib/html_utils.typ | 6 + lib/main.typ | 9 +- lib/pyscript.typ | 206 ++++++++++++------ .../isn_s_cube-0.1.0-py3-none-any.whl | Bin 0 -> 28332 bytes test_template/main.typ | 29 ++- 6 files changed, 180 insertions(+), 89 deletions(-) create mode 100644 test_template/isn_s_cube-0.1.0-py3-none-any.whl diff --git a/lib/html_head.typ b/lib/html_head.typ index 1c5fe31..0cbb3b3 100644 --- a/lib/html_head.typ +++ b/lib/html_head.typ @@ -1,6 +1,6 @@ #import "./custom_html.typ" as chtml -#import "./html_utils.typ": get-css, get-js -#import "./pyscript.typ": state-use-pyscript, state-pyscript-headers, state-pyscript-version +#import "./html_utils.typ": get-css, get-js, additionnal-head-tags +#import "./pyscript.typ": state-use-pyscript, state-pyscript-data-list, state-pyscript-version /// Generate the html element for the page. #let html-head( @@ -95,20 +95,25 @@ } }} + html.style(get-css()) + html.script(get-js()) + context for tag in additionnal-head-tags.final() { + tag + } + context if state-use-pyscript.final() { assert( state-pyscript-version.final() != none, message: "Cannot run python script: pyscript-version is not set" ) assert( - state-pyscript-version.final() in state-pyscript-headers.final(), + state-pyscript-version.final() in state-pyscript-data-list.final(), message: "Cannot run python script: urls for " + state-pyscript-version.final() + " not set in pyscript-urls" ) - state-pyscript-headers.final().at(state-pyscript-version.final()) + let pyscript-data = state-pyscript-data-list.final().at(state-pyscript-version.final()) + html.script(type: "module", src: pyscript-data.core-js-url) + pyscript-data.additionnal-head-tags } - html.style(get-css()) - html.script(get-js()) - }) } diff --git a/lib/html_utils.typ b/lib/html_utils.typ index 6531b25..e90ead5 100644 --- a/lib/html_utils.typ +++ b/lib/html_utils.typ @@ -3,6 +3,7 @@ #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 { @@ -14,6 +15,11 @@ js-list.update(x => if js in x { x } else { x + (js,) }) } +/// Add additionnal html tag to insert in +#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 { css-list.final().join("\n\n") diff --git a/lib/main.typ b/lib/main.typ index 8d79a72..6de6b54 100644 --- a/lib/main.typ +++ b/lib/main.typ @@ -2,7 +2,7 @@ #import "./html_body.typ": html-body #import "./html_utils.typ": html-show #import "./summary.typ": summary, card-list -#import "./pyscript.typ": state-use-pyscript, state-pyscript-headers, state-pyscript-version, state-pyscript-interpreters, state-pyscript-default-interpreter, pyscript-show +#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 "./icons.typ" @@ -32,8 +32,9 @@ stylesheets: (), /// List of related sites for metadata me-links: (), - /// Dictionnary of available tags to add in header for each versions of pyscript - pyscript-headers: (:), + /// Dictionnary of available pyscript-data for each versions of pyscript + /// expected in the form of ("": pyscript-data-list("", { html.link(...) }), ...) + pyscript-data-list: (:), /// Dictionnary of available python version to add in header for each pyscript interpreter pyscript-interpreters: (:), /// Pyscript version to use @@ -57,7 +58,7 @@ ) = { assert(type(url) == str, message: "A page must have an url") context { - state-pyscript-headers.update(x => pyscript-headers) + state-pyscript-data-list.update(x => pyscript-data-list) state-pyscript-interpreters.update(x => pyscript-interpreters) state-pyscript-version.update(x => pyscript-version) state-pyscript-default-interpreter.update(x => pyscript-default-interpreter) diff --git a/lib/pyscript.typ b/lib/pyscript.typ index 0788d6e..9c6967b 100644 --- a/lib/pyscript.typ +++ b/lib/pyscript.typ @@ -1,8 +1,22 @@ +#import "./html_utils.typ": add-tag-in-head + #let state-use-pyscript = state("state-use-pyscript", false) #let state-pyscript-version = state("state-pyscript-version", none) -#let state-pyscript-headers = state("state-pyscript-headers", (:)) +#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", ()) + +/// Define data needed to load a version of pyscript +#let pyscript-data( + /// Url to `core.js` + core-js-url, + /// Additionnal tags to add to , like mini-coi of core.css + additionnal-head-tags: {}, +) = ( + core-js-url: core-js-url, + additionnal-head-tags: additionnal-head-tags +) #let get-pep723(script) = { script.find(regex( @@ -31,81 +45,131 @@ metadata = (:) } state-use-pyscript.update(x => true) - context { - let config = metadata.at("tool", default: (:)).at("pyscript", default: (:)) + let config = metadata.at("tool", default: (:)).at("pyscript", default: (:)) - let pyscript-config = (:) - // Package dependencies - if "dependencies" in metadata { - pyscript-config.insert("packages", metadata.at("dependencies")) - } - // Files stetup - if "files" in config { - pyscript-config.insert("files", config.at("files")) - } - // Interpreteur selection - if "interpreter" in config { - pyscript-config.insert("interpreter", config.at("interpreter")) - } else if state-pyscript-default-interpreter.final() != none { - assert( - state-pyscript-default-interpreter.final() in state-pyscript-interpreters.final(), - message: state-pyscript-default-interpreter.final() + " is not in pyscript-interpreters", - ) - pyscript-config.insert("interpreter", state-pyscript-interpreters.final().at(state-pyscript-default-interpreter.final())) - } - - let attrs = ( - type: "py" + let pyscript-config = (:) + // Package dependencies + if "dependencies" in metadata { + pyscript-config.insert("packages", metadata.at("dependencies")) + } + // Files stetup + if "files" in config { + pyscript-config.insert("files", config.at("files")) + } + // Interpreteur selection + if "interpreter" in config { + pyscript-config.insert("interpreter", config.at("interpreter")) + } else if state-pyscript-default-interpreter.final() != none { + assert( + state-pyscript-default-interpreter.final() in state-pyscript-interpreters.final(), + message: state-pyscript-default-interpreter.final() + " is not in pyscript-interpreters", ) - let default_val_terminal = true - let default_val_worker = true - let default_val_canvas = none - // TODO: canvas: can only be used once by page, need inserting canvas html tag and pyscript hook to link it to pyodide - if config.at("pygame", default: false) { - default_val_terminal = false - default_val_worker = false - default_val_canvas = "canvas" - } - if config.at("terminal", default: true) { - attrs.insert("terminal", "") - } - if config.at("worker", default: true) { - attrs.insert("worker", "") - } - if config.at("canvas", default: default_val_canvas) not in (none, false) { - attrs.insert("canvas", config.at("canvas", default: default_val_canvas)) - } + pyscript-config.insert("interpreter", state-pyscript-interpreters.final().at(state-pyscript-default-interpreter.final())) + } - if pyscript-config != (:) { - attrs.insert("config", json.encode(pyscript-config)) - } - - let script = it.text; - if config.at("repl", default: false) { - script = "import code\n" + script + "\ncode.interact(banner='', local=globals())" - } - let displayed-code = it.text; - if config.at("hide-meta", default: false) { - displayed-code = displayed-code.replace(get-pep723(displayed-code), "").trim("\n") - } + let attrs = ( + type: "py" + ) + let default-val-terminal = true + let default-val-worker = true + let default-val-canvas = none + if config.at("pygame", default: false) { + default-val-terminal = false + default-val-worker = false + default-val-canvas = "canvas" + } + if config.at("terminal", default: default-val-terminal) { + attrs.insert("terminal", "") + } + if config.at("worker", default: default-val-worker) { + attrs.insert("worker", "") + } + let canvas-attr = config.at("canvas", default: default-val-canvas) + if canvas-attr == false { + canvas-attr = none + } + if canvas-attr == true { + canvas-attr = "canvas" + } + if canvas-attr != none { + attrs.insert("canvas", canvas-attr) - if not config.at("hide-code", default: false) { - raw( - displayed-code, - block: true, - lang: "python", - align: it.align, - syntaxes: it.syntaxes, - theme: it.theme, - tab-size: it.tab-size, - ) - } + assert( + canvas-attr not in state-pyscript-canvas-ids.at(here()), + message: ( + "Can not have multiple canvas with the same name. Note that SDL2 has issues working with canvas", + "with an id different than 'canvas', meaning that it is unadvised to have more than one script", + "using SDL2 by page.", + "(tool.pyscript.pygame = true set the default value for the SDL2 canvas to 'canvas' instead of none)" + ).join(" ") + ) + state-pyscript-canvas-ids.update(canvas-ids => canvas-ids + (canvas-attr,)) + } - html.elem( - "script", - attrs: attrs, - script + if pyscript-config != (:) { + attrs.insert("config", json.encode(pyscript-config)) + } + + let script = it.text; + if config.at("repl", default: false) { + script = "import code\n" + script + "\ncode.interact(banner='', local=globals())" + } + let displayed-code = it.text; + if config.at("hide-meta", default: false) { + displayed-code = displayed-code.replace(get-pep723(displayed-code), "").trim("\n") + } + + if not config.at("hide-code", default: false) { + raw( + displayed-code, + block: true, + lang: "python", + align: it.align, + syntaxes: it.syntaxes, + theme: it.theme, + tab-size: it.tab-size, + ) + } + + html.elem( + "script", + attrs: attrs, + script + ) + if canvas-attr != none { + html.elem("canvas", attrs: ("id": canvas-attr)) + + let core-js-url = state-pyscript-data-list.final().at(state-pyscript-version.final()).core-js-url + add-tag-in-head( + html.script( + type: "module", + ``` + import { hooks } from ""; + hooks.main.onReady.add((wrap, script) => { + if (script.hasAttribute("canvas")) { + const target = script.getAttribute("canvas"); + const canvas = document.getElementById(target); + wrap.interpreter.canvas.setCanvas2D(canvas); + } + }); + ```.text.replace("", core-js-url) + ) ) } - //raw(block: true, lang: "json", json.encode(metadata)) } + // also need pyscript hook to link canvas to pyodide: + // +// + //raw(block: true, lang: "json", json.encode(metadata)) diff --git a/test_template/isn_s_cube-0.1.0-py3-none-any.whl b/test_template/isn_s_cube-0.1.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..53c14cc9e71416cb17dd058f5e01966af2afbfaf GIT binary patch literal 28332 zcmWIWW@Zs#VBlb2U|>)L5pc}Sz`&4MoEKjlpIn-hst;2LQmM#bAgaH&gpGkAMwEd; z1XZD9a!F=>o?b!a+K9J)$~%Sr_8+mH!)re&!>J)M^L?z|nr(>^Ct^7?5@)0eI{B5W zevI3G_jP~EnagkD=PB!#J$-jB{PF}@mzIEA-z-DdtqV>~f88#d{>88VwCp5h6=Ca; zPsh*p>b7V-D%!xG{`>a-dv|W!SrN8+w)F8?GnR6ACB0Wa;v-#i=kD+6^XLzz6NHN-VSX}zP{M& z@@1w7$uHf%XK}cFeLExb)^Qih79XeJBfVGmJlAQAHjsR)(Hr5q$NuDu9j%iEGjAv! zoYdEA{z~5Oc6@=?BopBsMYac>=X{>9MA4x6NdF8)*B4KH`H$#WiAE);BM{R{8punAhXt*Sl&jy?yjKm7Q^2)49^u zKTcO(ICypXxvQz)mvqiq?!86t-`_8)Sxzp~3KO3R?hNHiTQR4@AVv9;w6fhI4xWWx zLD%D!pUapLP&DCPx#{1AF!`XHY&?Q|ZP(O(TU$T=xWjV7x|LrN+*3@_o=?BBJbvb8 z=cV5Z1(s{x|1)_;&apU_{d`HsT#Z9M>d%($3}Zg~XvTu&{yqi9wM~UmenM4e1eDs3 z&dT^@qNEsNRQt*3prxiKlemt|Cc(pv9`hf+DLl$oG~HP=@OGB!&&Bf{^`7kuGc=hz zvFq>Y&|69}T5``4Kh8hf8(@$u&|+u({jM+j-AaFp3b_%`5jBvK0jkwZN_0F)+De?Zt1i+Pn)EhRupCG zOjgw8`O%-A^X$gqIXewbKXDM?+h{G#x=mn7^DLX?N5ACr^G>}SBYrVcgom%Ne80o< z=F9sX|Jt=)3luz_>e8=mkh4%#S#FY}%dz~4!Cn)OZ#a{CInYd3U-ROb+({KV36~fm zYoe1f#57pUrxnGopJKRYI@_alMQxAQuk~V{YRvL-JL`w5{!cU5Y<~GYST6bDaygTK z`hm;uS=h{tQwl!sy>L$E*2x9m<6d>Q^o1z6%sShuV^_K4Lb=lFIjWOaB~5b>bTnA9 zKE<^4xbuDf>63FiFV|mM+1@^Z^W%qr)$9HAR_nwEKDvJN%Spaa7Y~!^ivBY+EDrwG zPWqVj@vO7f?^)^5r;B~3si-WDQnj$>)M7O^ja;s}ZI{fVS&Ttzf7hLP`lYi*@Bh

or!?IiCOV>~`sQ)~_a&onEnt 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 M!^*%Al?mbj00s-7wg3PC literal 0 HcmV?d00001 diff --git a/test_template/main.typ b/test_template/main.typ index e42bc0a..6d14821 100644 --- a/test_template/main.typ +++ b/test_template/main.typ @@ -61,12 +61,14 @@ icon: "https://jean-marie.mineau.eu/website_assets/platypus.png", // Pyscript: - pyscript-headers: ( - "remote-2026.3.1": { - html.elem("script", attrs: (src: "./mini-coi.js")) - html.elem("script", attrs: (type: "module", src: "https://pyscript.net/releases/2026.3.1/core.js")) - html.elem("link", attrs: (rel: "stylesheet", href: "https://pyscript.net/releases/2026.3.1/core.css")) - }, + 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")) + html.elem("link", attrs: (rel: "stylesheet", href: "https://pyscript.net/releases/2026.3.1/core.css")) + }, + ) ), pyscript-version: "remote-2026.3.1", ) @@ -125,7 +127,7 @@ pprint([(k, v["title"]) for k, v in data.items()][:10]) # [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 @@ -136,6 +138,19 @@ pprint([(k, v["title"]) for k, v in data.items()][:10]) # Inline script metadata ``` +```python-run +# /// script +# dependencies = [ +# "pygame-ce", +# "./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.