Compare commits

...

5 Commits

Author SHA1 Message Date
917c871763 HTWAH draft
Some checks failed
CI / Check typos (push) Successful in 8s
CI / Check links (push) Failing after 11s
CI / Clippy (push) Successful in 1m4s
CI / Build and test (push) Successful in 1m6s
CI / Build container (push) Successful in 45s
CI / Deploy on waypoint (push) Successful in 49s
2025-11-05 22:06:23 -08:00
59b890bfe9 Style tweaks 2025-11-05 22:05:52 -08:00
dfffe824d1 Smart links 2025-11-05 22:05:24 -08:00
399fccb73b Upgrade mdx parser 2025-11-05 21:46:49 -08:00
726bf3cd36 Toml frontmatter 2025-11-05 09:55:03 -08:00
19 changed files with 397 additions and 150 deletions

76
Cargo.lock generated
View File

@@ -2194,6 +2194,15 @@ dependencies = [
"serde_core",
]
[[package]]
name = "serde_spanned"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@@ -2206,19 +2215,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "service-webpage"
version = "0.0.1"
@@ -2237,9 +2233,10 @@ dependencies = [
"parking_lot",
"reqwest",
"serde",
"serde_yaml",
"strum",
"tokio",
"toml",
"toolbox",
"tower-http",
"tracing",
]
@@ -2587,6 +2584,45 @@ dependencies = [
"tokio",
]
[[package]]
name = "toml"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
[[package]]
name = "toolbox"
version = "0.0.1"
@@ -2802,12 +2838,6 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -3285,6 +3315,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winnow"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
[[package]]
name = "wit-bindgen"
version = "0.46.0"

View File

@@ -12,6 +12,7 @@ libservice = { workspace = true }
macro-assets = { workspace = true }
macro-sass = { workspace = true }
assetserver = { workspace = true }
toolbox = { workspace = true }
page = { workspace = true }
axum = { workspace = true }
@@ -23,7 +24,7 @@ strum = { workspace = true }
chrono = { workspace = true }
parking_lot = { workspace = true }
lazy_static = { workspace = true }
serde_yaml = { workspace = true }
toml = { workspace = true }
serde = { workspace = true }
reqwest = { workspace = true }
tower-http = { workspace = true }

Binary file not shown.

Binary file not shown.

View File

@@ -92,7 +92,8 @@ pre span:before {
p code,
li code,
div code {
padding: 0 .2rem 0 .2rem;
padding: .2rem .4rem .2rem .4rem;
font-size: 85%;
border-radius: .3rem;
color: var(--codeFgColor);
background-color: var(--codeBgColor);

View File

@@ -41,7 +41,7 @@
--metaColor: #6199bb;
--lightMetaColor: #638c86;
--linkColor: #e4dab3;
--codeBgColor: var(--lightBgColor);
--codeBgColor: #292929;
--codeFgColor: var(--fgColor);
// Main colors

View File

@@ -1,4 +1,5 @@
use lazy_static::lazy_static;
use markdown_it::generics::inline::full_link;
use markdown_it::parser::block::{BlockRule, BlockState};
use markdown_it::parser::core::Root;
use markdown_it::parser::inline::{InlineRule, InlineState};
@@ -14,12 +15,51 @@ use crate::components::misc::Backlinks;
lazy_static! {
static ref MdParser: MarkdownIt = {
let mut md = markdown_it::MarkdownIt::new();
markdown_it::plugins::cmark::add(&mut md);
{
use markdown_it::plugins::cmark::*;
inline::newline::add(&mut md);
inline::escape::add(&mut md);
inline::backticks::add(&mut md);
inline::emphasis::add(&mut md);
// Replaced with smart links
//inline::link::add(&mut md);
full_link::add::<false>(&mut md, |href, title| {
Node::new(SmartLink {
url: href.unwrap_or_default(),
title,
})
});
inline::image::add(&mut md);
inline::autolink::add(&mut md);
inline::entity::add(&mut md);
block::code::add(&mut md);
block::fence::add(&mut md);
block::blockquote::add(&mut md);
block::hr::add(&mut md);
block::list::add(&mut md);
block::reference::add(&mut md);
block::heading::add(&mut md);
block::lheading::add(&mut md);
block::paragraph::add(&mut md);
}
markdown_it::plugins::html::add(&mut md);
md.block.add_rule::<YamlFrontMatter>().before_all();
md.block.add_rule::<TomlFrontMatter>().before_all();
md.inline.add_rule::<InlineEmote>();
md.inline.add_rule::<InlineEmote>();
md.inline.add_rule::<InlineMdx>();
md.block.add_rule::<FrontMatter>().before_all();
md
};
}
@@ -47,12 +87,12 @@ impl Markdown<'_> {
/// Try to read page metadata from a markdown file's frontmatter.
/// - returns `none` if there is no frontmatter
/// - returns an error if we fail to parse frontmatter
pub fn meta_from_markdown(root_node: &Node) -> Result<Option<PageMetadata>, serde_yaml::Error> {
pub fn meta_from_markdown(root_node: &Node) -> Result<Option<PageMetadata>, toml::de::Error> {
root_node
.children
.first()
.and_then(|x| x.cast::<FrontMatter>())
.map(|x| serde_yaml::from_str::<PageMetadata>(&x.content))
.and_then(|x| x.cast::<TomlFrontMatter>())
.map(|x| toml::from_str::<PageMetadata>(&x.content))
.map_or(Ok(None), |v| v.map(Some))
}
@@ -96,6 +136,39 @@ pub fn page_from_markdown(md: impl Into<String>, default_image: Option<String>)
// MARK: extensions
//
//
// MARK: smart link
//
#[derive(Debug)]
pub struct SmartLink {
pub url: String,
pub title: Option<String>,
}
impl NodeValue for SmartLink {
fn render(&self, node: &Node, fmt: &mut dyn Renderer) {
let mut attrs = node.attrs.clone();
attrs.push(("href", self.url.clone()));
if let Some(title) = &self.title {
attrs.push(("title", title.clone()));
}
let external = !(self.url.starts_with(".") || self.url.starts_with("/"));
// Open external links in a new tab
if external {
attrs.push(("target", "_blank".into()));
attrs.push(("rel", "noopener noreferrer".into()));
}
fmt.open("a", &attrs);
fmt.contents(&node.children);
fmt.close("a");
}
}
//
// MARK: emote
//
@@ -196,78 +269,83 @@ impl InlineRule for InlineMdx {
}
fn mdx_style(mdx: &str, _node: &Node, fmt: &mut dyn Renderer) -> bool {
// Parse inside of mdx: `style(<style>) <content>`
let (style, content) = {
let mdx = mdx.trim();
if !mdx.starts_with("style(") {
return false;
// Parse inside of mdx: `color(value, "text")`
let mdx = mdx
.trim()
.trim_start_matches('{')
.trim_end_matches('}')
.trim();
// Find the function name (everything before the opening parenthesis)
let paren_pos = match mdx.find('(') {
Some(x) => x,
None => return false,
};
if mdx[..paren_pos].trim() != "color" {
return false;
};
// Find matching closing parenthesis
let skip = paren_pos + 1;
let mut balance = 1;
let mut end = skip;
for i in mdx[skip..].bytes() {
match i {
b')' => balance -= 1,
b'(' => balance += 1,
_ => {}
}
let skip = 6;
let mut balance = 1;
let mut end = skip;
for i in mdx[skip..].bytes() {
match i {
b')' => balance -= 1,
b'(' => balance += 1,
_ => {}
}
if balance == 0 {
break;
}
end += 1;
if balance == 0 {
break;
}
if balance != 0 {
return false;
}
end += 1;
}
let style = mdx[skip..end].trim();
let content = mdx[end + 1..].trim();
if balance != 0 {
return false;
}
(style, content)
let args = mdx[skip..end].trim();
// Parse arguments: should be "value, text" or "value, \"text\""
let comma_pos = match args.find(',') {
Some(x) => x,
None => return false,
};
let value = args[..comma_pos].trim();
let text = args[comma_pos + 1..].trim();
// Strip quotes from text if present
let text = if (text.starts_with('"') && text.ends_with('"'))
|| (text.starts_with('\'') && text.ends_with('\''))
{
&text[1..text.len() - 1]
} else {
text
};
let mut style_str = String::new();
for kv in style.split(";") {
let mut s = kv.split(":");
let k = s.next();
let v = s.next();
if k.is_none() || v.is_none() || s.next().is_some() {
return false;
}
#[expect(clippy::unwrap_used)] // Checked previously
let k = k.unwrap().trim();
#[expect(clippy::unwrap_used)] // Checked previously
let v = v.unwrap().trim();
match k {
"color" => {
style_str.push_str("color:");
style_str.push_str(v);
style_str.push(';');
}
"color_var" => {
style_str.push_str("color:var(--");
style_str.push_str(v);
style_str.push_str(");");
}
_ => continue,
}
if value.starts_with("#") {
style_str.push_str("color:");
style_str.push_str(value);
style_str.push(';');
} else if value.starts_with("--") {
style_str.push_str("color:var(");
style_str.push_str(value);
style_str.push_str(");");
} else {
style_str.push_str("color:");
style_str.push_str(value);
style_str.push(';');
}
// Only works with text, could be reworked to do basic md styling
// (italics, bold, tab, code)
fmt.open("span", &[("style", style_str)]);
fmt.text(content);
fmt.text(text);
fmt.close("span");
return true;
@@ -276,7 +354,12 @@ fn mdx_style(mdx: &str, _node: &Node, fmt: &mut dyn Renderer) -> bool {
fn mdx_include(mdx: &str, _node: &Node, fmt: &mut dyn Renderer) -> bool {
// Parse inside of mdx: `include(<args>)`
let args = {
let mdx = mdx.trim();
let mdx = mdx
.trim()
.trim_start_matches('{')
.trim_end_matches('}')
.trim();
if !mdx.starts_with("include(") {
return false;
}
@@ -323,20 +406,20 @@ fn mdx_include(mdx: &str, _node: &Node, fmt: &mut dyn Renderer) -> bool {
}
//
// MARK: frontmatter
// MARK: yaml frontmatter
//
#[derive(Debug)]
/// AST node for front-matter
pub struct FrontMatter {
pub struct YamlFrontMatter {
#[expect(dead_code)]
pub content: String,
}
impl NodeValue for FrontMatter {
impl NodeValue for YamlFrontMatter {
fn render(&self, _node: &Node, _fmt: &mut dyn Renderer) {}
}
impl BlockRule for FrontMatter {
impl BlockRule for YamlFrontMatter {
fn run(state: &mut BlockState<'_, '_>) -> Option<(Node, usize)> {
// check the parent is the document Root
if !state.node.is::<Root>() {
@@ -373,6 +456,56 @@ impl BlockRule for FrontMatter {
}
let (content, _) = state.get_lines(state.line + 1, next_line, 0, true);
Some((Node::new(FrontMatter { content }), next_line + 1))
Some((Node::new(YamlFrontMatter { content }), next_line + 1))
}
}
//
// MARK: toml frontmatter
//
#[derive(Debug)]
pub struct TomlFrontMatter {
pub content: String,
}
impl NodeValue for TomlFrontMatter {
fn render(&self, _node: &Node, _fmt: &mut dyn Renderer) {}
}
impl BlockRule for TomlFrontMatter {
fn run(state: &mut BlockState<'_, '_>) -> Option<(Node, usize)> {
if !state.node.is::<Root>() {
return None;
}
if state.line != 0 {
return None;
}
let opening = state
.get_line(state.line)
.chars()
.take_while(|c| *c == '+')
.collect::<String>();
if !opening.starts_with("+++") {
return None;
}
let mut next_line = state.line;
loop {
next_line += 1;
if next_line >= state.line_max {
return None;
}
let line = state.get_line(next_line);
if line.starts_with(&opening) {
break;
}
}
let (content, _) = state.get_lines(state.line + 1, next_line, 0, true);
Some((Node::new(TomlFrontMatter { content }), next_line + 1))
}
}

View File

@@ -1,8 +1,8 @@
---
title: "What's a \"betalupi?\""
author: "Mark"
slug: whats-a-betalupi
---
+++
title = "What's a \"betalupi?\""
author = "Mark"
slug = "whats-a-betalupi"
+++
[es]: https://github.com/endless-sky/endless-sky
[*Stellaris*]: https://www.paradoxinteractive.com/games/stellaris/about
@@ -31,4 +31,4 @@ A snippet of the [_Endless Sky_][es] map is below.
<br/>
<img alt="betalupi map" class="image" src="/assets/img/betalupi.png"></img>
<img alt="betalupi map" src="/assets/img/betalupi.png"></img>

View File

@@ -1,8 +1,8 @@
---
title: Mark's Handouts
author: Mark
slug: handouts
---
+++
title = "Mark's Handouts"
author = "Mark"
slug = "handouts"
+++
# Mark's Handouts
@@ -15,14 +15,14 @@ and the [BMC](https://mathcircle.berkeley.edu).
<br></br>
{style(color_var:pink) For my students: } \
{{color(--pink, "For my students:")}} \
Don't look at solutions we haven't discussed,
and don't start any handouts before class. That spoils all the fun!
{style(color_var:green) For everyone else:} \
{{color(--green, "For everyone else:")}} \
If you're using any of these, please let me know---especially \
if you find errors, mistakes, or a poorly designed section. \
Such things must be fixed! { include(email_beta) }
Such things must be fixed! {{ include(email_beta) }}
<br></br>

View File

@@ -1,23 +1,21 @@
+++
title = "HtWaH: Typesetting"
template = "page.html"
author = "Mark"
[extra]
show_title = false
back_links = [
{target = "/htwah", text = "htwah"}
]
# TODO: many slugs, htwah/typesetting
slug = "handouts"
+++
## Table of Contents
This page is part of my [how to write a handout](/htwah) series.
- [Part 1: Typesetting](.) {{ color(c="green", t="**<-- you are here**") }}
- [Part 1: Typesetting](.) **{{color(--green, "<-- you are here")}}**
- Part 2: Notation
- Part 3: Designing lessons
- Part 4: Designing puzzles
- Part 5: Leading a class
- Part 6: Resources
<hr style="margin-top: 5rem; margin-bottom: 5rem"/>
@@ -34,7 +32,7 @@ Lessons presented on a screen do not take advantage of this; lessons presented o
Double-sided handouts break this spacial intuition. The act of turning a page upside-down severs its connection to physical space. Do not entangle your students in the pages of a double-sided worksheet---print only on one side.
{{color(c="grey", t="This is not true of books, which are presented in a fundamentally different way.")}}
{{color(--grey, "This is not true of books, which are presented in a fundamentally different way.")}}
Double-sided handouts also clutter the page with the opposite side's nodes. Even when using a pencil, work on the front of a page makes its way to the back.
@@ -62,26 +60,29 @@ If you force your students to cram their work into the margin,
their understanding will have a similar quality. \
Give them space to work and space to think.
{{color(c="grey", t="This rule only applies to handouts. Lecture notes and textbooks do not need to worry about this, since students should work through them on a separate sheet of paper.")}}
{{color(--grey, "This rule only applies to handouts. Lecture notes and textbooks do not need to worry about this, since students should work through them on a separate sheet of paper")}}
Every problem should be followed by a `\vfill`. \
There are exceptions, but they are rare.
If your handouts include solutions
{{color(c="grey", t="(they should),")}}
{{color(--grey, "(they should)")}}
observe the following rule: each problem's typeset
solution should fit in the empty space after the problem.
This isn't perfectly accurate estimate of space
(your students will likely need more), but it is a very good minimum.
{{color(c="grey", t="If your students will be drawing pictures, make each gap twice as bit as it needs to be.")}}
{{color(--grey, "If your students will be drawing pictures, make each gap twice as bit as it needs to be.")}}
In a handout that is typeset well, the layout of each page should not
change when solutions are shown. This helps you give students enough
room to solve each problem, and keeps page numbers consistent between
instructors' and students' handouts:
{{ twopdf(left="/htwah/sols-a.pdf", right="/htwah/sols-b.pdf") }}
<div style="display: flex; flex-direction: row; gap: 1rem; height: 50rem">
<embed type="application/pdf" src="/assets/htwah/sols-a.pdf" width="100%" height="100%" />
<embed type="application/pdf" src="/assets/htwah/sols-b.pdf" width="100%" height="100%" />
</div>
<br>
@@ -93,7 +94,7 @@ A page should _never_ break between the first mention of a concept and the probl
New sections should _always_ start on a new page.
{{color(c="grey", t="Unlike the previous comments on spacing, the above rules apply to all formats. Whether you are writing a textbook, a handout, or lecture notes, do not allow your page breaks to break as student's train of thought.")}}
{{color(--grey, "Unlike the previous comments on spacing, the above rules apply to all formats. Whether you are writing a textbook, a handout, or lecture notes, do not allow your page breaks to break as student's train of thought.")}}
These rules are easy to follow if you leave space for work: \
Place your `\pagebreak`s well and let `\vfill` handle the rest.
@@ -111,11 +112,14 @@ clear that these are the definitions we will be using in the future.
It is also a good idea to mark the first use of a term. \
In the example below, this is done with italicized text.
{{ onepdf(src="/htwah/definitions.pdf") }}
<div style="display: flex; flex-direction: row; gap: 1rem; height: 50rem">
<embed type="application/pdf" src="/assets/htwah/definitions.pdf" width="100%" height="100%" />
</div>
Of course, not all texts need such aggressive grouping. I tend to avoid clear "boxing" in [my lessons](/handouts), opting for subtle grouping by whitespace instead. This is a matter of taste.
{{color(c="grey", t="Regardless of implementation, visual boundaries should match conceptual boundaries. It should be easy to see where each logical element ends.")}}
{{color(grey, "Regardless of implementation, visual boundaries should match conceptual boundaries. It should be easy to see where each logical element ends.")}}
Finally, remember that too much separation is just as distracting as too little.
Balance is key.
@@ -125,7 +129,11 @@ Balance is key.
Different elements should be far apart, and related elements should be close together[^4]. This rule is often violated by equations and diagrams.
Consider the two pages below:
{{ twopdf(left="/htwah/spacing-a.pdf", right="/htwah/spacing-b.pdf") }}
<div style="display: flex; flex-direction: row; gap: 1rem; height: 50rem">
<embed type="application/pdf" src="/assets/htwah/spacing-a.pdf" width="100%" height="100%" />
<embed type="application/pdf" src="/assets/htwah/spacing-b.pdf" width="100%" height="100%" />
</div>
The circuit diagram on the left is clearly a part of the setup at the top of the page. It is not connected to Problem 14.
@@ -151,7 +159,7 @@ Numbering should be simple: use a single counter that is set to 1 on the first p
Different numbering systems (`i, ii, iii...` for a preface; `A31, A32, A33...` for appendices) have little value and only make navigation more difficult.
{{color(c="grey", t="Roman numbering of frontmatter in large textbooks is acceptable, but anything more complicated is unnecessary.")}}
{{color(--grey, "Roman numbering of frontmatter in large textbooks is acceptable, but anything more complicated is unnecessary")}}
### Versions
@@ -159,11 +167,11 @@ The document itself should also be numbered. In most cases, a `\today` on the fr
This helps synchronize the handout you _think_ the class has with the handout that the class _really_ has.
Future instructors {{color(c="grey", t="(and future you)")}} will be thankful.
Future instructors {{color(--grey, "(and future you")}} will be thankful.
### Items
Propositions, definitions, and examples should all be numbered with the same counter. Problems should also use this counter, unless they are only listed at the end of each section {{color(c="grey", t="(as they often are in textbooks).")}}
Propositions, definitions, and examples should all be numbered with the same counter. Problems should also use this counter, unless they are only listed at the end of each section {{color(--grey, "(as they often are in textbooks).")}}
Do not use different counters for different objects. Theorem 1 should not follow Definition 2. This is the default behavior of LaTeX, and it is a serious mistake.
@@ -175,7 +183,9 @@ With a single counter, this is not an issue. Readers are aware of their absolute
[Open Logic](https://openlogicproject.org/) again provides an example of quality typesetting. Notice how the numbering of Propositions, Definitions, and Examples on the page below is consecutive:
{{ onepdf(src="/htwah/numbering.pdf") }}
<div style="display: flex; flex-direction: row; gap: 1rem; height: 50rem">
<embed type="application/pdf" src="/assets/htwah/numbering.pdf" width="100%" height="100%" />
</div>
In long textbooks, prefixing numbers with the chapter index (e.g, the `2` in `2.32` above) is wise. In any other case, a single counter should be enough.
@@ -197,7 +207,12 @@ who made it, when, and why.
If you're writing a document that is available in multiple variants (for example, a handout with solutions or an exam with multiple versions), the variant should be easily visible in the title. Note the box under the title in the handout on the left.
{{ twopdf(left="/htwah/sols-a.pdf", right="/htwah/sols-b.pdf") }}
<div style="display: flex; flex-direction: row; gap: 1rem; height: 50rem">
<embed type="application/pdf" src="/assets/htwah/sols-a.pdf" width="100%" height="100%" />
<embed type="application/pdf" src="/assets/htwah/sols-b.pdf" width="100%" height="100%" />
</div>
This lets us detect errors quickly: we only need to look at the first page of a lesson to know if we printed the wrong variant, handed the students solutions, or graded an exam using the wrong answer key.

View File

@@ -9,37 +9,37 @@ Also see [what's a "betalupi?"](/whats-a-betalupi)
## Projects
- **RedoxOS**, a general-purpose, microkernel-based operating system written in Rust. _{ style(color_var:grey) [enthusiast] }_
- **RedoxOS**, a general-purpose, microkernel-based operating system written in Rust. _{{color(--grey, "[enthusiast]")}}
- { style(color_var:grey) Status: } { style(color_var:yellow) Passive. }
- { style(color_var:grey) Website: } [:fa-link: redox-os.org](https://www.redox-os.org/)
- {{color(--grey, "Status: ")}} {{color(--yellow, "Passive.")}}
- {{color(--grey, "Website: ")}} [:fa-link: redox-os.org](https://www.redox-os.org/)
<br/>
- **Tectonic**, the LaTeX engine that is pleasant to use.
Experimental, but fully functional. _{ style(color_var:grey) [co-maintainer] }_
Experimental, but fully functional. _{{color(--grey, "[co-maintainer]")}}_
- { style(color_var:grey) Status: } { style(color_var:yellow) Passive. } [Typst](https://github.com/typst/typst) is better.
- { style(color_var:grey) Main repo: } [:fa-github: Tectonic](https://github.com/tectonic-typesetting/tectonic)
- { style(color_var:grey) Bundle tools: } [:fa-github: tectonic-texlive-bundles](https://github.com/tectonic-typesetting/tectonic-texlive-bundles)
- {{color(--grey, "Status: ")}} {{color(--yellow, "Abandoned. ")}} [Typst](https://github.com/typst/typst) is better.
- {{color(--grey, "Main repo: ")}} [:fa-github: Tectonic](https://github.com/tectonic-typesetting/tectonic)
- {{color(--grey, "Bundle tools: ")}} [:fa-github: tectonic-texlive-bundles](https://github.com/tectonic-typesetting/tectonic-texlive-bundles)
<br/>
- **Daisy**, a pretty TUI scientific calculator. _{style(color_var:grey) [author] }_
- **Daisy**, a pretty TUI scientific calculator. _{{color(--grey, "[author]")}}_
- {style(color_var:grey) Status: } {style(color_var:orange) Done. } Used this to learn Rust. [Numbat](https://numbat.dev) is better.
- {style(color_var:grey) Repository: } [:fa-github: rm-dr/daisy](https://github.com/rm-dr/daisy)
- {style(color_var:grey) Website: } [:fa-link: daisy.betalupi.com](https://daisy.betalupi.com) (WASM demo)
- {{color(--grey, "Status: ")}} {{color(--orange, "Done. ")}} Used this to learn Rust. [Numbat](https://numbat.dev) is better.
- {{color(--grey, "Repository: ")}} [:fa-github: rm-dr/daisy](https://github.com/rm-dr/daisy)
- {{color(--grey, "Website: ")}} [:fa-link: daisy.betalupi.com](https://daisy.betalupi.com) (WASM demo)
<br/>
- **Lamb**, a lambda calculus engine. _{style(color_var:grey) [author] }_
- {style(color_var:grey) Status: } {style(color_var:orange) Done. } Fun little project.
- {style(color_var:grey) Repository: } [:fa-github: rm-dr/lamb](https://github.com/rm-dr/lamb)
- {style(color_var:grey) PyPi: } [:fa-python: lamb-engine](https://pypi.org/project/lamb-engine)
- **Lamb**, a lambda calculus engine. _{{color(--grey, "[author] ")}}_
- {{color(--grey, "Status: ")}} {{color(--orange, "Done. ")}} Fun little project.
- {{color(--grey, "Repository: ")}} [:fa-github: rm-dr/lamb](https://github.com/rm-dr/lamb)
- {{color(--grey, "PyPi: ")}} [:fa-python: lamb-engine](https://pypi.org/project/lamb-engine)
<br/>

View File

@@ -1,8 +1,8 @@
---
title: Links
author: Mark
slug: links
---
+++
title = "Links"
author = "Mark"
slug = "links"
+++
# Bookmarks

View File

@@ -26,3 +26,10 @@ pub fn betalupi() -> Page {
Some(Image_Icon::URL.to_owned()),
)
}
pub fn htwah_typesetting() -> Page {
page_from_markdown(
include_str!("htwah-typesetting.md"),
Some(Image_Icon::URL.to_owned()),
)
}

View File

@@ -2,6 +2,7 @@ use assetserver::Asset;
use axum::http::header;
use macro_assets::assets;
use macro_sass::sass;
use toolbox::mime::MimeType;
assets! {
prefix: "/assets"
@@ -150,4 +151,56 @@ assets! {
(header::CONTENT_TYPE, "application/font-ttf")
]
}
//
// MARK: htwah
//
Htwah_Definitions {
source: include_bytes!("../../assets/htwah/definitions.pdf"),
target: "/htwah/definitions.pdf",
headers: [
(header::CONTENT_TYPE, MimeType::Pdf.to_string())
]
}
Htwah_Numbering {
source: include_bytes!("../../assets/htwah/numbering.pdf"),
target: "/htwah/numbering.pdf",
headers: [
(header::CONTENT_TYPE, MimeType::Pdf.to_string())
]
}
Htwah_SolsA {
source: include_bytes!("../../assets/htwah/sols-a.pdf"),
target: "/htwah/sols-a.pdf",
headers: [
(header::CONTENT_TYPE, MimeType::Pdf.to_string())
]
}
Htwah_SolsB {
source: include_bytes!("../../assets/htwah/sols-b.pdf"),
target: "/htwah/sols-b.pdf",
headers: [
(header::CONTENT_TYPE, MimeType::Pdf.to_string())
]
}
Htwah_SpacingA {
source: include_bytes!("../../assets/htwah/spacing-a.pdf"),
target: "/htwah/spacing-a.pdf",
headers: [
(header::CONTENT_TYPE, MimeType::Pdf.to_string())
]
}
Htwah_SpacingB {
source: include_bytes!("../../assets/htwah/spacing-b.pdf"),
target: "/htwah/spacing-b.pdf",
headers: [
(header::CONTENT_TYPE, MimeType::Pdf.to_string())
]
}
}

View File

@@ -25,7 +25,8 @@ fn build_server() -> Arc<PageServer> {
.add_page("/", pages::index())
.add_page("/links", pages::links())
.add_page("/whats-a-betalupi", pages::betalupi())
.add_page("/handouts", pages::handouts());
.add_page("/handouts", pages::handouts())
.add_page("/htwah", pages::htwah_typesetting());
server
}