Compare commits
5 Commits
2f2f7c68f1
...
917c871763
| Author | SHA1 | Date | |
|---|---|---|---|
| 917c871763 | |||
| 59b890bfe9 | |||
| dfffe824d1 | |||
| 399fccb73b | |||
| 726bf3cd36 |
76
Cargo.lock
generated
76
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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 }
|
||||
|
||||
BIN
crates/service/service-webpage/assets/htwah/definitions.pdf
Normal file
BIN
crates/service/service-webpage/assets/htwah/definitions.pdf
Normal file
Binary file not shown.
BIN
crates/service/service-webpage/assets/htwah/numbering.pdf
Normal file
BIN
crates/service/service-webpage/assets/htwah/numbering.pdf
Normal file
Binary file not shown.
BIN
crates/service/service-webpage/assets/htwah/sols-a.pdf
Normal file
BIN
crates/service/service-webpage/assets/htwah/sols-a.pdf
Normal file
Binary file not shown.
BIN
crates/service/service-webpage/assets/htwah/sols-b.pdf
Normal file
BIN
crates/service/service-webpage/assets/htwah/sols-b.pdf
Normal file
Binary file not shown.
BIN
crates/service/service-webpage/assets/htwah/spacing-a.pdf
Normal file
BIN
crates/service/service-webpage/assets/htwah/spacing-a.pdf
Normal file
Binary file not shown.
BIN
crates/service/service-webpage/assets/htwah/spacing-b.pdf
Normal file
BIN
crates/service/service-webpage/assets/htwah/spacing-b.pdf
Normal file
Binary file not shown.
@@ -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);
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
--metaColor: #6199bb;
|
||||
--lightMetaColor: #638c86;
|
||||
--linkColor: #e4dab3;
|
||||
--codeBgColor: var(--lightBgColor);
|
||||
--codeBgColor: #292929;
|
||||
--codeFgColor: var(--fgColor);
|
||||
|
||||
// Main colors
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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/>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: Links
|
||||
author: Mark
|
||||
slug: links
|
||||
---
|
||||
+++
|
||||
title = "Links"
|
||||
author = "Mark"
|
||||
slug = "links"
|
||||
+++
|
||||
|
||||
|
||||
# Bookmarks
|
||||
|
||||
@@ -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()),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user