make tairu work better with noscript
261
Cargo.lock
generated
|
@ -168,6 +168,12 @@ dependencies = [
|
|||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -189,6 +195,18 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.5.0"
|
||||
|
@ -261,6 +279,12 @@ dependencies = [
|
|||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
|
@ -285,6 +309,46 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
|
@ -305,6 +369,12 @@ dependencies = [
|
|||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.0"
|
||||
|
@ -345,6 +415,50 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.72.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"flume",
|
||||
"half",
|
||||
"lebe",
|
||||
"miniz_oxide",
|
||||
"rayon-core",
|
||||
"smallvec",
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -420,6 +534,16 @@ dependencies = [
|
|||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.27.3"
|
||||
|
@ -445,6 +569,16 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "handlebars"
|
||||
version = "4.3.7"
|
||||
|
@ -570,6 +704,24 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"jpeg-decoder",
|
||||
"num-traits",
|
||||
"png",
|
||||
"qoi",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.0.0"
|
||||
|
@ -597,6 +749,21 @@ version = "1.0.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
|
@ -660,6 +827,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -673,6 +841,15 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
|
@ -803,6 +980,19 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
|
@ -829,6 +1019,15 @@ dependencies = [
|
|||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
|
@ -868,6 +1067,26 @@ dependencies = [
|
|||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
|
@ -1034,6 +1253,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
|
@ -1059,6 +1284,15 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
|
@ -1111,6 +1345,17 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.36.0"
|
||||
|
@ -1277,6 +1522,7 @@ dependencies = [
|
|||
"env_logger",
|
||||
"handlebars",
|
||||
"http-body",
|
||||
"image",
|
||||
"log",
|
||||
"pulldown-cmark",
|
||||
"rand",
|
||||
|
@ -1368,6 +1614,12 @@ version = "0.11.0+wasi-snapshot-preview1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
@ -1473,3 +1725,12 @@ checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97"
|
|||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
|
|
@ -29,6 +29,13 @@ styles = ["post/tairu.css"]
|
|||
% id = "01HQ162WWAMCPC5M88QAXHX4BT"
|
||||
- so to help us learn, I made a little tile editor so that we can experiment with rendering tiles! have a look:
|
||||
|
||||
<noscript>(…though you will need to enable JavaScript to try it out.
|
||||
seriously, pinky promise I won't ever track you!
|
||||
inspect the source code if you wanna.
|
||||
if not, you will have to deal with static pictures.
|
||||
but just keep in mind this was supposed to be an <strong><em>interactive</em></strong> exploration of autotiling techniques.
|
||||
cheers!)</noscript>
|
||||
|
||||
```javascript tairu
|
||||
import { Tilemap } from "tairu/tilemap.js";
|
||||
import { TileEditor } from "tairu/editor.js";
|
||||
|
@ -47,7 +54,7 @@ styles = ["post/tairu.css"]
|
|||
});
|
||||
```
|
||||
|
||||
```output tairu
|
||||
```output tairu 01HQ47ZX7520PJNPJ75M793R5G
|
||||
```
|
||||
|
||||
% id = "01HQ162WWAC3FN565QE3JAB87D"
|
||||
|
@ -58,6 +65,8 @@ styles = ["post/tairu.css"]
|
|||
console.log(tilemapSquare.at(3, 1));
|
||||
```
|
||||
```output tairu
|
||||
0
|
||||
1
|
||||
```
|
||||
|
||||
[`Uint8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
|
||||
|
@ -189,7 +198,7 @@ styles = ["post/tairu.css"]
|
|||
borderWidth: 4,
|
||||
});
|
||||
```
|
||||
```output tairu
|
||||
```output tairu 01HQ49TJZFMK719KSE16SG3F7B
|
||||
```
|
||||
|
||||
% id = "01HQ162WWAAEKW1ECV5G3ZEY47"
|
||||
|
@ -407,7 +416,7 @@ styles = ["post/tairu.css"]
|
|||
tilesets: [heavyMetalTileset],
|
||||
});
|
||||
```
|
||||
```output tairu
|
||||
```output tairu 01HQ49X8Z57FNMN3E79FYF8CMG
|
||||
```
|
||||
|
||||
% id = "01HQ162WWA03JAGJYCT0DRZP24"
|
||||
|
@ -429,7 +438,7 @@ styles = ["post/tairu.css"]
|
|||
tilesets: [heavyMetalTileset],
|
||||
});
|
||||
```
|
||||
```output tairu
|
||||
```output tairu 01HQ49YDPQXYSAT5N6P241DG3C
|
||||
```
|
||||
|
||||
% id = "01HQ162WWAB0AYSPGB4AEVT03Z"
|
||||
|
@ -455,7 +464,7 @@ styles = ["post/tairu.css"]
|
|||
tilesets: [heavyMetalTileset],
|
||||
});
|
||||
```
|
||||
```output tairu
|
||||
```output tairu 01HQ49Z8JWR75D85DGHCB34K8E
|
||||
```
|
||||
|
||||
% id = "01HQ1K39AS4VDW7DVTAGQ03WFM"
|
||||
|
@ -550,7 +559,7 @@ styles = ["post/tairu.css"]
|
|||
tilesets: [heavyMetalTileset],
|
||||
});
|
||||
```
|
||||
```output tairu
|
||||
```output tairu 01HQ4A01MPE6JT5ZZFEN9S635W
|
||||
```
|
||||
|
||||
% id = "01HQ1K39AS7CRBZ67N1VVHCVME"
|
||||
|
@ -879,7 +888,7 @@ styles = ["post/tairu.css"]
|
|||
tilesets: [heavyMetalTileset47],
|
||||
});
|
||||
```
|
||||
```output tairu
|
||||
```output tairu 01HQ4A11RRXEQ850598GFBJN0B
|
||||
```
|
||||
|
||||
% id = "01HQ1M84GSCXTPGVPXY840WCQ6"
|
||||
|
@ -907,7 +916,7 @@ new TilesetTileEditor({
|
|||
tilesets: [heavyMetalTileset47],
|
||||
});
|
||||
```
|
||||
```output tairu
|
||||
```output tairu 01HQ4A45WNAEJGCT2WDMQJHK14
|
||||
```
|
||||
|
||||
:nap: <!--
|
||||
|
|
|
@ -13,9 +13,10 @@ clap = { version = "4.3.22", features = ["derive"] }
|
|||
codespan-reporting = "0.11.1"
|
||||
copy_dir = "0.1.3"
|
||||
env_logger = "0.10.0"
|
||||
log = { workspace = true }
|
||||
handlebars = "4.3.7"
|
||||
http-body = "1.0.0"
|
||||
image = "0.24.8"
|
||||
log = { workspace = true }
|
||||
pulldown-cmark = { version = "0.9.3", default-features = false }
|
||||
rand = "0.8.5"
|
||||
serde = { version = "1.0.183", features = ["derive"] }
|
||||
|
|
|
@ -18,7 +18,7 @@ use walkdir::WalkDir;
|
|||
|
||||
use crate::{
|
||||
cli::parse::parse_tree_with_diagnostics,
|
||||
config::Config,
|
||||
config::{Config, ConfigDerivedData},
|
||||
html::{
|
||||
breadcrumbs::breadcrumbs_to_html,
|
||||
navmap::{build_navigation_map, NavigationMap},
|
||||
|
@ -175,6 +175,7 @@ impl Generator {
|
|||
parsed_trees: impl IntoIterator<Item = ParsedTree>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut handlebars = Handlebars::new();
|
||||
let mut config_derived_data = ConfigDerivedData::default();
|
||||
|
||||
let mut template_file_ids = HashMap::new();
|
||||
for entry in WalkDir::new(paths.template_dir) {
|
||||
|
@ -232,6 +233,7 @@ impl Generator {
|
|||
&mut tree,
|
||||
treehouse,
|
||||
config,
|
||||
&mut config_derived_data,
|
||||
parsed_tree.file_id,
|
||||
&roots.branches,
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::HashMap, ffi::OsStr, path::Path};
|
||||
use std::{collections::HashMap, ffi::OsStr, fs::File, io::BufReader, path::Path};
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -108,3 +108,36 @@ impl Config {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Data derived from the config.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ConfigDerivedData {
|
||||
pub pic_sizes: HashMap<String, Option<PicSize>>,
|
||||
}
|
||||
|
||||
/// Picture size. This is useful for emitting <img> elements with a specific size to eliminate layout shifting.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PicSize {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
impl ConfigDerivedData {
|
||||
fn read_pic_size(config: &Config, pic_id: &str) -> Option<PicSize> {
|
||||
let pic_filename = config.pics.get(pic_id)?;
|
||||
let (width, height) = image::io::Reader::new(BufReader::new(
|
||||
File::open(format!("static/pic/{pic_filename}")).ok()?,
|
||||
))
|
||||
.into_dimensions()
|
||||
.ok()?;
|
||||
Some(PicSize { width, height })
|
||||
}
|
||||
|
||||
pub fn pic_size(&mut self, config: &Config, pic_id: &str) -> Option<PicSize> {
|
||||
if !self.pic_sizes.contains_key(pic_id) {
|
||||
self.pic_sizes
|
||||
.insert(pic_id.to_owned(), Self::read_pic_size(config, pic_id));
|
||||
}
|
||||
self.pic_sizes.get(pic_id).copied().flatten()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ use pulldown_cmark::escape::{escape_href, escape_html, StrWrite};
|
|||
use pulldown_cmark::{Alignment, CodeBlockKind, Event, LinkType, Tag};
|
||||
use pulldown_cmark::{CowStr, Event::*};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::config::{Config, ConfigDerivedData, PicSize};
|
||||
use crate::state::Treehouse;
|
||||
|
||||
enum TableState {
|
||||
|
@ -41,6 +41,7 @@ enum TableState {
|
|||
struct HtmlWriter<'a, I, W> {
|
||||
treehouse: &'a Treehouse,
|
||||
config: &'a Config,
|
||||
config_derived_data: &'a mut ConfigDerivedData,
|
||||
page_id: &'a str,
|
||||
|
||||
/// Iterator supplying events.
|
||||
|
@ -68,6 +69,7 @@ where
|
|||
fn new(
|
||||
treehouse: &'a Treehouse,
|
||||
config: &'a Config,
|
||||
config_derived_data: &'a mut ConfigDerivedData,
|
||||
page_id: &'a str,
|
||||
iter: I,
|
||||
writer: W,
|
||||
|
@ -75,7 +77,9 @@ where
|
|||
Self {
|
||||
treehouse,
|
||||
config,
|
||||
config_derived_data,
|
||||
page_id,
|
||||
|
||||
iter,
|
||||
writer,
|
||||
end_newline: true,
|
||||
|
@ -247,11 +251,11 @@ where
|
|||
kind,
|
||||
program_name,
|
||||
} => {
|
||||
self.write(match kind {
|
||||
self.write(match &kind {
|
||||
LiterateCodeKind::Input => {
|
||||
"<th-literate-program data-mode=\"input\" "
|
||||
}
|
||||
LiterateCodeKind::Output => {
|
||||
LiterateCodeKind::Output { .. } => {
|
||||
"<th-literate-program data-mode=\"output\" "
|
||||
}
|
||||
})?;
|
||||
|
@ -261,7 +265,30 @@ where
|
|||
escape_html(&mut self.writer, program_name)?;
|
||||
self.write("\" data-language=\"")?;
|
||||
escape_html(&mut self.writer, language)?;
|
||||
self.write("\" role=\"code\">")
|
||||
self.write("\" role=\"code\">")?;
|
||||
|
||||
if let LiterateCodeKind::Output { placeholder_pic_id } = kind {
|
||||
if !placeholder_pic_id.is_empty() {
|
||||
self.write(
|
||||
"<img class=\"placeholder\" loading=\"lazy\" src=\"",
|
||||
)?;
|
||||
escape_html(
|
||||
&mut self.writer,
|
||||
&self.config.pic_url(placeholder_pic_id),
|
||||
)?;
|
||||
self.write("\"")?;
|
||||
if let Some(PicSize { width, height }) = self
|
||||
.config_derived_data
|
||||
.pic_size(self.config, placeholder_pic_id)
|
||||
{
|
||||
self.write(&format!(
|
||||
" width=\"{width}\" height=\"{height}\""
|
||||
))?;
|
||||
}
|
||||
self.write(">")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
CodeBlockKind::Indented => self.write("<pre><code>"),
|
||||
|
@ -556,9 +583,9 @@ where
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum LiterateCodeKind {
|
||||
enum LiterateCodeKind<'a> {
|
||||
Input,
|
||||
Output,
|
||||
Output { placeholder_pic_id: &'a str },
|
||||
}
|
||||
|
||||
enum CodeBlockMode<'a> {
|
||||
|
@ -568,7 +595,7 @@ enum CodeBlockMode<'a> {
|
|||
},
|
||||
LiterateProgram {
|
||||
language: &'a str,
|
||||
kind: LiterateCodeKind,
|
||||
kind: LiterateCodeKind<'a>,
|
||||
program_name: &'a str,
|
||||
},
|
||||
}
|
||||
|
@ -578,14 +605,16 @@ impl<'a> CodeBlockMode<'a> {
|
|||
if language.is_empty() {
|
||||
CodeBlockMode::PlainText
|
||||
} else if let Some((language, program_name)) = language.split_once(' ') {
|
||||
let (program_name, placeholder_pic_id) =
|
||||
program_name.split_once(' ').unwrap_or((program_name, ""));
|
||||
CodeBlockMode::LiterateProgram {
|
||||
language,
|
||||
kind: if language == "output" {
|
||||
LiterateCodeKind::Output
|
||||
LiterateCodeKind::Output { placeholder_pic_id }
|
||||
} else {
|
||||
LiterateCodeKind::Input
|
||||
},
|
||||
program_name,
|
||||
program_name: program_name.split(' ').next().unwrap(),
|
||||
}
|
||||
} else {
|
||||
CodeBlockMode::SyntaxHighlightOnly { language }
|
||||
|
@ -624,12 +653,13 @@ pub fn push_html<'a, I>(
|
|||
s: &mut String,
|
||||
treehouse: &'a Treehouse,
|
||||
config: &'a Config,
|
||||
config_derived_data: &'a mut ConfigDerivedData,
|
||||
page_id: &'a str,
|
||||
iter: I,
|
||||
) where
|
||||
I: Iterator<Item = Event<'a>>,
|
||||
{
|
||||
HtmlWriter::new(treehouse, config, page_id, iter, s)
|
||||
HtmlWriter::new(treehouse, config, config_derived_data, page_id, iter, s)
|
||||
.run()
|
||||
.unwrap();
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use pulldown_cmark::{BrokenLink, LinkType};
|
|||
use treehouse_format::pull::BranchKind;
|
||||
|
||||
use crate::{
|
||||
config::Config,
|
||||
config::{Config, ConfigDerivedData},
|
||||
html::EscapeAttribute,
|
||||
state::{FileId, Treehouse},
|
||||
tree::{
|
||||
|
@ -19,6 +19,7 @@ pub fn branch_to_html(
|
|||
s: &mut String,
|
||||
treehouse: &mut Treehouse,
|
||||
config: &Config,
|
||||
config_derived_data: &mut ConfigDerivedData,
|
||||
file_id: FileId,
|
||||
branch_id: SemaBranchId,
|
||||
) {
|
||||
|
@ -145,6 +146,7 @@ pub fn branch_to_html(
|
|||
s,
|
||||
treehouse,
|
||||
config,
|
||||
config_derived_data,
|
||||
treehouse.tree_path(file_id).expect(".tree file expected"),
|
||||
markdown_parser,
|
||||
);
|
||||
|
@ -197,7 +199,7 @@ pub fn branch_to_html(
|
|||
let num_children = branch.children.len();
|
||||
for i in 0..num_children {
|
||||
let child_id = treehouse.tree.branch(branch_id).children[i];
|
||||
branch_to_html(s, treehouse, config, file_id, child_id);
|
||||
branch_to_html(s, treehouse, config, config_derived_data, file_id, child_id);
|
||||
}
|
||||
s.push_str("</ul>");
|
||||
}
|
||||
|
@ -213,12 +215,13 @@ pub fn branches_to_html(
|
|||
s: &mut String,
|
||||
treehouse: &mut Treehouse,
|
||||
config: &Config,
|
||||
config_derived_data: &mut ConfigDerivedData,
|
||||
file_id: FileId,
|
||||
branches: &[SemaBranchId],
|
||||
) {
|
||||
s.push_str("<ul>");
|
||||
for &child in branches {
|
||||
branch_to_html(s, treehouse, config, file_id, child);
|
||||
branch_to_html(s, treehouse, config, config_derived_data, file_id, child);
|
||||
}
|
||||
s.push_str("</ul>");
|
||||
}
|
||||
|
|
|
@ -556,10 +556,21 @@ th-literate-program[data-mode="output"] {
|
|||
border: none;
|
||||
border-radius: 0;
|
||||
|
||||
& iframe {
|
||||
& iframe,
|
||||
& img.placeholder {
|
||||
border-style: none;
|
||||
border-radius: 4px;
|
||||
display: block;
|
||||
transition: opacity var(--transition-duration);
|
||||
}
|
||||
|
||||
& iframe,
|
||||
& img.placeholder.loading {
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
& iframe.loaded {
|
||||
opacity: 100%;
|
||||
}
|
||||
|
||||
/* The inner iframe is hidden until something requests display. */
|
||||
|
|
|
@ -19,16 +19,19 @@ function getLiterateProgram(name) {
|
|||
return literatePrograms.get(name);
|
||||
}
|
||||
|
||||
function getLiterateProgramWorkerCommands(name) {
|
||||
function getLiterateProgramWorkerCommands(name, count) {
|
||||
let commands = [];
|
||||
let literateProgram = getLiterateProgram(name);
|
||||
for (let frame of literateProgram.frames) {
|
||||
|
||||
for (let i = 0; i < count; ++i) {
|
||||
let frame = literateProgram.frames[i];
|
||||
if (frame.mode == "input") {
|
||||
commands.push({ kind: "module", source: frame.textContent });
|
||||
} else if (frame.mode == "output") {
|
||||
commands.push({ kind: "output" });
|
||||
}
|
||||
}
|
||||
|
||||
return commands;
|
||||
}
|
||||
|
||||
|
@ -180,6 +183,8 @@ class OutputMode {
|
|||
}
|
||||
});
|
||||
|
||||
this.frame.placeholderImage.classList.add("loading");
|
||||
|
||||
this.frame.program.onChanged.push(_ => this.evaluate());
|
||||
}
|
||||
|
||||
|
@ -187,7 +192,7 @@ class OutputMode {
|
|||
this.requestConsoleClear();
|
||||
this.iframe.contentWindow.postMessage({
|
||||
action: "eval",
|
||||
input: getLiterateProgramWorkerCommands(this.frame.programName),
|
||||
input: getLiterateProgramWorkerCommands(this.frame.programName, this.frame.frameIndex + 1),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -229,6 +234,15 @@ class OutputMode {
|
|||
// iframe cannot be `display: none` to get its scrollWidth/scrollHeight.
|
||||
this.iframe.classList.remove("hidden");
|
||||
|
||||
if (this.frame.placeholderImage != null) {
|
||||
// Fade the iframe in after it becomes visible, and remove the image.
|
||||
setTimeout(() => this.iframe.classList.add("loaded"), 0);
|
||||
this.frame.removeChild(this.frame.placeholderImage);
|
||||
} else {
|
||||
// If there is no image, don't do the fade in.
|
||||
this.iframe.classList.add("loaded");
|
||||
}
|
||||
|
||||
let width = this.iframe.contentDocument.body.scrollWidth;
|
||||
let height = this.iframe.contentDocument.body.scrollHeight;
|
||||
|
||||
|
@ -244,8 +258,11 @@ class OutputMode {
|
|||
class LiterateProgram extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.programName = this.getAttribute("data-program");
|
||||
this.frameIndex = this.program.frames.length;
|
||||
this.program.frames.push(this);
|
||||
|
||||
this.placeholderImage = this.getElementsByClassName("placeholder")[0];
|
||||
|
||||
this.mode = this.getAttribute("data-mode");
|
||||
if (this.mode == "input") {
|
||||
this.modeImpl = new InputMode(this);
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
// A frameworking class assigning some CSS classes to the canvas to make it integrate nicer with CSS.
|
||||
export class Frame extends HTMLCanvasElement {
|
||||
static fontFace = "RecVar";
|
||||
static monoFontFace = "RecVarMono";
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
this.style.cssText = `
|
||||
margin-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
border-radius: 4px;
|
||||
max-width: 100%;
|
||||
`;
|
||||
|
||||
this.ctx = this.getContext("2d");
|
||||
|
||||
requestAnimationFrame(this.#drawLoop.bind(this));
|
||||
}
|
||||
|
||||
#drawLoop() {
|
||||
this.ctx.font = "14px RecVar";
|
||||
this.draw();
|
||||
requestAnimationFrame(this.#drawLoop.bind(this));
|
||||
}
|
||||
|
||||
// Override this!
|
||||
draw() {
|
||||
throw new ReferenceError("draw() must be overridden");
|
||||
}
|
||||
|
||||
getTextPositionInBox(text, x, y, width, height, hAlign, vAlign) {
|
||||
let measurements = this.ctx.measureText(text);
|
||||
|
||||
let leftX;
|
||||
switch (hAlign) {
|
||||
case "left":
|
||||
leftX = x;
|
||||
break;
|
||||
case "center":
|
||||
leftX = x + width / 2 - measurements.width / 2;
|
||||
break;
|
||||
case "right":
|
||||
leftX = x + width - measurements.width;
|
||||
break;
|
||||
}
|
||||
|
||||
let textHeight = measurements.fontBoundingBoxAscent;
|
||||
let baselineY;
|
||||
switch (vAlign) {
|
||||
case "top":
|
||||
baselineY = y + textHeight;
|
||||
break;
|
||||
case "center":
|
||||
baselineY = y + height / 2 + textHeight / 2;
|
||||
break;
|
||||
case "bottom":
|
||||
baselineY = y + height;
|
||||
break;
|
||||
}
|
||||
|
||||
return { leftX, baselineY };
|
||||
}
|
||||
|
||||
get scaleInViewportX() {
|
||||
return this.clientWidth / this.width;
|
||||
}
|
||||
|
||||
get scaleInViewportY() {
|
||||
return this.clientHeight / this.height;
|
||||
}
|
||||
|
||||
getMousePositionFromEvent(event) {
|
||||
return {
|
||||
x: event.offsetX / this.scaleInViewportX,
|
||||
y: event.offsetY / this.scaleInViewportY,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function defineFrame(elementName, claß) { // because `class` is a keyword.
|
||||
customElements.define(elementName, claß, { extends: "canvas" });
|
||||
}
|
||||
|
||||
defineFrame("tairu--frame", Frame);
|
|
@ -1,76 +0,0 @@
|
|||
import { TileEditor } from 'tairu/editor.js';
|
||||
|
||||
export function alignTextInRectangle(ctx, text, x, y, width, height, hAlign, vAlign) {
|
||||
let measurements = ctx.measureText(text);
|
||||
|
||||
let leftX;
|
||||
switch (hAlign) {
|
||||
case "left":
|
||||
leftX = x;
|
||||
break;
|
||||
case "center":
|
||||
leftX = x + width / 2 - measurements.width / 2;
|
||||
break;
|
||||
case "right":
|
||||
leftX = x + width - measurements.width;
|
||||
break;
|
||||
}
|
||||
|
||||
let textHeight = measurements.fontBoundingBoxAscent;
|
||||
let baselineY;
|
||||
switch (vAlign) {
|
||||
case "top":
|
||||
baselineY = y + textHeight;
|
||||
break;
|
||||
case "center":
|
||||
baselineY = y + height / 2 + textHeight / 2;
|
||||
break;
|
||||
case "bottom":
|
||||
baselineY = y + height;
|
||||
break;
|
||||
}
|
||||
|
||||
return { leftX, baselineY };
|
||||
}
|
||||
|
||||
export function shouldConnect(a, b) {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
export class TileEditorWithCardinalDirections extends TileEditor {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
this.colorScheme.tiles[1] = "#f96565";
|
||||
}
|
||||
|
||||
drawConnectionText(text, enabled, tileX, tileY, hAlign, vAlign) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.fillStyle = enabled ? "#6c023e" : "#d84161";
|
||||
this.ctx.font = `800 14px ${Frame.monoFontFace}`;
|
||||
const padding = 2;
|
||||
let topLeftX = tileX * this.tileSize + padding;
|
||||
let topLeftY = tileY * this.tileSize + padding;
|
||||
let rectSize = this.tileSize - padding * 2;
|
||||
let { leftX, baselineY } = this.getTextPositionInBox(text, topLeftX, topLeftY, rectSize, rectSize, hAlign, vAlign);
|
||||
this.ctx.fillText(text, leftX, baselineY);
|
||||
}
|
||||
|
||||
drawTiles() {
|
||||
super.drawTiles();
|
||||
for (let y = 0; y < this.tilemap.height; ++y) {
|
||||
for (let x = 0; x < this.tilemap.width; ++x) {
|
||||
let tile = this.tilemap.at(x, y);
|
||||
if (canConnect(tile)) {
|
||||
let connectedWithEast = shouldConnect(tile, this.tilemap.at(x + 1, y));
|
||||
let connectedWithSouth = shouldConnect(tile, this.tilemap.at(x, y + 1));
|
||||
let connectedWithNorth = shouldConnect(tile, this.tilemap.at(x, y - 1));
|
||||
let connectedWithWest = shouldConnect(tile, this.tilemap.at(x - 1, y));
|
||||
this.drawConnectionText("E", connectedWithEast, x, y, "right", "center");
|
||||
this.drawConnectionText("S", connectedWithSouth, x, y, "center", "bottom");
|
||||
this.drawConnectionText("N", connectedWithNorth, x, y, "center", "top");
|
||||
this.drawConnectionText("W", connectedWithWest, x, y, "left", "center");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
import { Tilemap } from './tilemap.js';
|
||||
|
||||
const alphabet = " x";
|
||||
|
||||
function parseTilemap(lineArray) {
|
||||
let tilemap = new Tilemap(lineArray[0].length, lineArray.length);
|
||||
for (let y in lineArray) {
|
||||
let line = lineArray[y];
|
||||
for (let x = 0; x < line.length; ++x) {
|
||||
let char = line.charAt(x);
|
||||
tilemap.setAt(x, y, alphabet.indexOf(char));
|
||||
}
|
||||
}
|
||||
return tilemap;
|
||||
}
|
||||
|
||||
export default {
|
||||
bitwiseAutotiling: parseTilemap([
|
||||
" ",
|
||||
" xxx ",
|
||||
" xxx ",
|
||||
" xxx ",
|
||||
" ",
|
||||
]),
|
||||
bitwiseAutotilingChapter2: parseTilemap([
|
||||
" ",
|
||||
" x ",
|
||||
" x ",
|
||||
" xxx ",
|
||||
" ",
|
||||
]),
|
||||
bitwiseAutotilingCorners: parseTilemap([
|
||||
" ",
|
||||
" x x ",
|
||||
" x ",
|
||||
" x x ",
|
||||
" ",
|
||||
]),
|
||||
bitwiseAutotiling47: parseTilemap([
|
||||
" x ",
|
||||
" x ",
|
||||
" xx xx ",
|
||||
" xxxx ",
|
||||
" x ",
|
||||
]),
|
||||
};
|
||||
|
|
@ -7,3 +7,12 @@ document.addEventListener("click", event => {
|
|||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
|
||||
// Certain words don't make sense if scripts are disabled.
|
||||
class YesScript extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.classList.add("yes-indeed");
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("th-yesscript", YesScript);
|
||||
|
|
BIN
static/pic/01HQ47ZX7520PJNPJ75M793R5G-noscript-placeholder.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
static/pic/01HQ49TJZFMK719KSE16SG3F7B-noscript-placeholder.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
static/pic/01HQ49X8Z57FNMN3E79FYF8CMG-noscript-placeholder.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
static/pic/01HQ49YDPQXYSAT5N6P241DG3C-noscript-placeholder.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
static/pic/01HQ49Z8JWR75D85DGHCB34K8E-noscript-placeholder.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
static/pic/01HQ4A01MPE6JT5ZZFEN9S635W-noscript-placeholder.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
static/pic/01HQ4A11RRXEQ850598GFBJN0B-noscript-placeholder.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
static/pic/01HQ4A45WNAEJGCT2WDMQJHK14-noscript-placeholder.png
Normal file
After Width: | Height: | Size: 30 KiB |