Rust library to generate static websites
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(assets): Image processin

Princesseuh c6468cf7 7fab1ab6

+1209 -99
+708 -10
Cargo.lock
··· 27 27 ] 28 28 29 29 [[package]] 30 + name = "aligned-vec" 31 + version = "0.6.4" 32 + source = "registry+https://github.com/rust-lang/crates.io-index" 33 + checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" 34 + dependencies = [ 35 + "equator", 36 + ] 37 + 38 + [[package]] 30 39 name = "allocator-api2" 31 40 version = "0.2.21" 32 41 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 119 128 ] 120 129 121 130 [[package]] 131 + name = "arbitrary" 132 + version = "1.4.2" 133 + source = "registry+https://github.com/rust-lang/crates.io-index" 134 + checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" 135 + 136 + [[package]] 122 137 name = "arcstr" 123 138 version = "1.2.0" 124 139 source = "registry+https://github.com/rust-lang/crates.io-index" 125 140 checksum = "03918c3dbd7701a85c6b9887732e2921175f26c350b4563841d0958c21d57e6d" 126 141 127 142 [[package]] 143 + name = "arg_enum_proc_macro" 144 + version = "0.3.4" 145 + source = "registry+https://github.com/rust-lang/crates.io-index" 146 + checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" 147 + dependencies = [ 148 + "proc-macro2", 149 + "quote", 150 + "syn 2.0.106", 151 + ] 152 + 153 + [[package]] 128 154 name = "arrayref" 129 155 version = "0.3.9" 130 156 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 197 223 checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 198 224 199 225 [[package]] 226 + name = "av1-grain" 227 + version = "0.2.4" 228 + source = "registry+https://github.com/rust-lang/crates.io-index" 229 + checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" 230 + dependencies = [ 231 + "anyhow", 232 + "arrayvec", 233 + "log", 234 + "nom 7.1.3", 235 + "num-rational", 236 + "v_frame", 237 + ] 238 + 239 + [[package]] 240 + name = "avif-serialize" 241 + version = "0.8.6" 242 + source = "registry+https://github.com/rust-lang/crates.io-index" 243 + checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" 244 + dependencies = [ 245 + "arrayvec", 246 + ] 247 + 248 + [[package]] 200 249 name = "axum" 201 250 version = "0.8.4" 202 251 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 335 384 checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" 336 385 337 386 [[package]] 387 + name = "bit_field" 388 + version = "0.10.3" 389 + source = "registry+https://github.com/rust-lang/crates.io-index" 390 + checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" 391 + 392 + [[package]] 338 393 name = "bitflags" 339 394 version = "1.3.2" 340 395 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 350 405 ] 351 406 352 407 [[package]] 408 + name = "bitstream-io" 409 + version = "2.6.0" 410 + source = "registry+https://github.com/rust-lang/crates.io-index" 411 + checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" 412 + 413 + [[package]] 414 + name = "bitvec" 415 + version = "1.0.1" 416 + source = "registry+https://github.com/rust-lang/crates.io-index" 417 + checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" 418 + dependencies = [ 419 + "funty", 420 + "radium", 421 + "tap", 422 + "wyz", 423 + ] 424 + 425 + [[package]] 353 426 name = "blake3" 354 427 version = "1.8.2" 355 428 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 402 475 "dunce", 403 476 "futures", 404 477 "indexmap", 405 - "itertools", 478 + "itertools 0.14.0", 406 479 "itoa", 407 480 "memchr", 408 481 "notify 8.2.0", ··· 440 513 "dashmap", 441 514 "derive_more", 442 515 "fast-glob", 443 - "itertools", 516 + "itertools 0.14.0", 444 517 "num-bigint", 445 518 "oxc", 446 519 "oxc_ecmascript", ··· 615 688 "brk_rolldown_fs", 616 689 "brk_rolldown_utils", 617 690 "dashmap", 618 - "itertools", 691 + "itertools 0.14.0", 619 692 "oxc_resolver", 620 693 "sugar_path", 621 694 ] ··· 675 748 "itoa", 676 749 "memchr", 677 750 "mime", 678 - "nom", 751 + "nom 8.0.0", 679 752 "oxc", 680 753 "oxc_index", 681 754 "phf 0.12.1", ··· 715 788 ] 716 789 717 790 [[package]] 791 + name = "built" 792 + version = "0.7.7" 793 + source = "registry+https://github.com/rust-lang/crates.io-index" 794 + checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" 795 + 796 + [[package]] 718 797 name = "bumpalo" 719 798 version = "3.19.0" 720 799 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 724 803 ] 725 804 726 805 [[package]] 806 + name = "bytemuck" 807 + version = "1.23.2" 808 + source = "registry+https://github.com/rust-lang/crates.io-index" 809 + checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" 810 + 811 + [[package]] 727 812 name = "byteorder" 728 813 version = "1.5.0" 729 814 source = "registry+https://github.com/rust-lang/crates.io-index" 730 815 checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 816 + 817 + [[package]] 818 + name = "byteorder-lite" 819 + version = "0.1.0" 820 + source = "registry+https://github.com/rust-lang/crates.io-index" 821 + checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" 731 822 732 823 [[package]] 733 824 name = "bytes" ··· 750 841 source = "registry+https://github.com/rust-lang/crates.io-index" 751 842 checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" 752 843 dependencies = [ 844 + "jobserver", 845 + "libc", 753 846 "shlex", 754 847 ] 755 848 ··· 762 855 "byteorder", 763 856 "fnv", 764 857 "uuid", 858 + ] 859 + 860 + [[package]] 861 + name = "cfg-expr" 862 + version = "0.15.8" 863 + source = "registry+https://github.com/rust-lang/crates.io-index" 864 + checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" 865 + dependencies = [ 866 + "smallvec", 867 + "target-lexicon", 765 868 ] 766 869 767 870 [[package]] ··· 873 976 checksum = "058167258e819b16a4ba601fdfe270349ef191154758dbce122c62a698f70ba8" 874 977 dependencies = [ 875 978 "divan-macros", 876 - "itertools", 979 + "itertools 0.14.0", 877 980 "proc-macro-crate", 878 981 "proc-macro2", 879 982 "quote", ··· 896 999 ] 897 1000 898 1001 [[package]] 1002 + name = "color_quant" 1003 + version = "1.1.0" 1004 + source = "registry+https://github.com/rust-lang/crates.io-index" 1005 + checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" 1006 + 1007 + [[package]] 899 1008 name = "colorchoice" 900 1009 version = "1.0.4" 901 1010 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1053 1162 dependencies = [ 1054 1163 "winapi", 1055 1164 ] 1165 + 1166 + [[package]] 1167 + name = "crunchy" 1168 + version = "0.2.4" 1169 + source = "registry+https://github.com/rust-lang/crates.io-index" 1170 + checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" 1056 1171 1057 1172 [[package]] 1058 1173 name = "crypto-common" ··· 1268 1383 ] 1269 1384 1270 1385 [[package]] 1386 + name = "equator" 1387 + version = "0.4.2" 1388 + source = "registry+https://github.com/rust-lang/crates.io-index" 1389 + checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" 1390 + dependencies = [ 1391 + "equator-macro", 1392 + ] 1393 + 1394 + [[package]] 1395 + name = "equator-macro" 1396 + version = "0.4.2" 1397 + source = "registry+https://github.com/rust-lang/crates.io-index" 1398 + checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" 1399 + dependencies = [ 1400 + "proc-macro2", 1401 + "quote", 1402 + "syn 2.0.106", 1403 + ] 1404 + 1405 + [[package]] 1271 1406 name = "equivalent" 1272 1407 version = "1.0.2" 1273 1408 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1295 1430 ] 1296 1431 1297 1432 [[package]] 1433 + name = "exr" 1434 + version = "1.73.0" 1435 + source = "registry+https://github.com/rust-lang/crates.io-index" 1436 + checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" 1437 + dependencies = [ 1438 + "bit_field", 1439 + "half", 1440 + "lebe", 1441 + "miniz_oxide", 1442 + "rayon-core", 1443 + "smallvec", 1444 + "zune-inflate", 1445 + ] 1446 + 1447 + [[package]] 1298 1448 name = "fancy-regex" 1299 1449 version = "0.14.0" 1300 1450 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1329 1479 version = "2.3.0" 1330 1480 source = "registry+https://github.com/rust-lang/crates.io-index" 1331 1481 checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 1482 + 1483 + [[package]] 1484 + name = "fdeflate" 1485 + version = "0.3.7" 1486 + source = "registry+https://github.com/rust-lang/crates.io-index" 1487 + checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" 1488 + dependencies = [ 1489 + "simd-adler32", 1490 + ] 1332 1491 1333 1492 [[package]] 1334 1493 name = "filetime" ··· 1387 1546 dependencies = [ 1388 1547 "libc", 1389 1548 ] 1549 + 1550 + [[package]] 1551 + name = "funty" 1552 + version = "2.0.0" 1553 + source = "registry+https://github.com/rust-lang/crates.io-index" 1554 + checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" 1390 1555 1391 1556 [[package]] 1392 1557 name = "futures" ··· 1538 1703 ] 1539 1704 1540 1705 [[package]] 1706 + name = "gif" 1707 + version = "0.13.3" 1708 + source = "registry+https://github.com/rust-lang/crates.io-index" 1709 + checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" 1710 + dependencies = [ 1711 + "color_quant", 1712 + "weezl", 1713 + ] 1714 + 1715 + [[package]] 1541 1716 name = "gimli" 1542 1717 version = "0.31.1" 1543 1718 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1802 1977 "log", 1803 1978 "regex-automata 0.4.9", 1804 1979 "regex-syntax 0.8.5", 1980 + ] 1981 + 1982 + [[package]] 1983 + name = "half" 1984 + version = "2.6.0" 1985 + source = "registry+https://github.com/rust-lang/crates.io-index" 1986 + checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" 1987 + dependencies = [ 1988 + "cfg-if", 1989 + "crunchy", 1805 1990 ] 1806 1991 1807 1992 [[package]] ··· 2111 2296 ] 2112 2297 2113 2298 [[package]] 2299 + name = "image" 2300 + version = "0.25.6" 2301 + source = "registry+https://github.com/rust-lang/crates.io-index" 2302 + checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" 2303 + dependencies = [ 2304 + "bytemuck", 2305 + "byteorder-lite", 2306 + "color_quant", 2307 + "exr", 2308 + "gif", 2309 + "image-webp", 2310 + "num-traits", 2311 + "png", 2312 + "qoi", 2313 + "ravif", 2314 + "rayon", 2315 + "rgb", 2316 + "tiff", 2317 + "zune-core", 2318 + "zune-jpeg", 2319 + ] 2320 + 2321 + [[package]] 2322 + name = "image-webp" 2323 + version = "0.2.4" 2324 + source = "registry+https://github.com/rust-lang/crates.io-index" 2325 + checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" 2326 + dependencies = [ 2327 + "byteorder-lite", 2328 + "quick-error", 2329 + ] 2330 + 2331 + [[package]] 2332 + name = "imgref" 2333 + version = "1.11.0" 2334 + source = "registry+https://github.com/rust-lang/crates.io-index" 2335 + checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" 2336 + 2337 + [[package]] 2114 2338 name = "indexmap" 2115 2339 version = "2.10.0" 2116 2340 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2118 2342 dependencies = [ 2119 2343 "equivalent", 2120 2344 "hashbrown 0.15.5", 2345 + "rayon", 2121 2346 "serde", 2122 2347 ] 2123 2348 ··· 2179 2404 ] 2180 2405 2181 2406 [[package]] 2407 + name = "interpolate_name" 2408 + version = "0.2.4" 2409 + source = "registry+https://github.com/rust-lang/crates.io-index" 2410 + checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" 2411 + dependencies = [ 2412 + "proc-macro2", 2413 + "quote", 2414 + "syn 2.0.106", 2415 + ] 2416 + 2417 + [[package]] 2182 2418 name = "io-uring" 2183 2419 version = "0.7.9" 2184 2420 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2197 2433 2198 2434 [[package]] 2199 2435 name = "itertools" 2436 + version = "0.12.1" 2437 + source = "registry+https://github.com/rust-lang/crates.io-index" 2438 + checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" 2439 + dependencies = [ 2440 + "either", 2441 + ] 2442 + 2443 + [[package]] 2444 + name = "itertools" 2200 2445 version = "0.14.0" 2201 2446 source = "registry+https://github.com/rust-lang/crates.io-index" 2202 2447 checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" ··· 2252 2497 ] 2253 2498 2254 2499 [[package]] 2500 + name = "jobserver" 2501 + version = "0.1.34" 2502 + source = "registry+https://github.com/rust-lang/crates.io-index" 2503 + checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" 2504 + dependencies = [ 2505 + "getrandom 0.3.3", 2506 + "libc", 2507 + ] 2508 + 2509 + [[package]] 2510 + name = "jpeg-decoder" 2511 + version = "0.3.2" 2512 + source = "registry+https://github.com/rust-lang/crates.io-index" 2513 + checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" 2514 + 2515 + [[package]] 2255 2516 name = "js-sys" 2256 2517 version = "0.3.77" 2257 2518 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2297 2558 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 2298 2559 2299 2560 [[package]] 2561 + name = "lebe" 2562 + version = "0.5.2" 2563 + source = "registry+https://github.com/rust-lang/crates.io-index" 2564 + checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" 2565 + 2566 + [[package]] 2300 2567 name = "libc" 2301 2568 version = "0.2.175" 2302 2569 source = "registry+https://github.com/rust-lang/crates.io-index" 2303 2570 checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" 2304 2571 2305 2572 [[package]] 2573 + name = "libdeflate-sys" 2574 + version = "1.24.0" 2575 + source = "registry+https://github.com/rust-lang/crates.io-index" 2576 + checksum = "805824325366c44599dfeb62850fe3c7d7b3e3d75f9ab46785bc7dba3676815c" 2577 + dependencies = [ 2578 + "cc", 2579 + ] 2580 + 2581 + [[package]] 2582 + name = "libdeflater" 2583 + version = "1.24.0" 2584 + source = "registry+https://github.com/rust-lang/crates.io-index" 2585 + checksum = "b270bcc7e9d6dce967a504a55b1b0444f966aa9184e8605b531bc0492abb30bb" 2586 + dependencies = [ 2587 + "libdeflate-sys", 2588 + ] 2589 + 2590 + [[package]] 2591 + name = "libfuzzer-sys" 2592 + version = "0.4.10" 2593 + source = "registry+https://github.com/rust-lang/crates.io-index" 2594 + checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" 2595 + dependencies = [ 2596 + "arbitrary", 2597 + "cc", 2598 + ] 2599 + 2600 + [[package]] 2306 2601 name = "libredox" 2307 2602 version = "0.1.9" 2308 2603 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2311 2606 "bitflags 2.9.2", 2312 2607 "libc", 2313 2608 "redox_syscall", 2609 + ] 2610 + 2611 + [[package]] 2612 + name = "libwebp-sys" 2613 + version = "0.9.6" 2614 + source = "registry+https://github.com/rust-lang/crates.io-index" 2615 + checksum = "54cd30df7c7165ce74a456e4ca9732c603e8dc5e60784558c1c6dc047f876733" 2616 + dependencies = [ 2617 + "cc", 2618 + "glob", 2314 2619 ] 2315 2620 2316 2621 [[package]] ··· 2388 2693 ] 2389 2694 2390 2695 [[package]] 2696 + name = "loop9" 2697 + version = "0.1.5" 2698 + source = "registry+https://github.com/rust-lang/crates.io-index" 2699 + checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" 2700 + dependencies = [ 2701 + "imgref", 2702 + ] 2703 + 2704 + [[package]] 2391 2705 name = "matchers" 2392 2706 version = "0.1.0" 2393 2707 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2428 2742 name = "maudit" 2429 2743 version = "0.3.2" 2430 2744 dependencies = [ 2745 + "base64", 2431 2746 "blake3", 2432 2747 "brk_rolldown", 2433 2748 "chrono", ··· 2435 2750 "dyn-eq", 2436 2751 "env_logger", 2437 2752 "glob", 2753 + "image", 2438 2754 "log", 2439 2755 "lol_html", 2440 2756 "maud", 2441 2757 "maudit-macros", 2442 2758 "oxc_sourcemap", 2759 + "oxipng", 2443 2760 "pulldown-cmark", 2761 + "rayon", 2444 2762 "rustc-hash", 2445 2763 "serde", 2446 2764 "serde_yml", 2447 2765 "slug", 2448 2766 "syntect", 2449 2767 "thiserror 2.0.16", 2768 + "thumbhash", 2450 2769 "tokio", 2770 + "webp", 2451 2771 ] 2452 2772 2453 2773 [[package]] ··· 2506 2826 ] 2507 2827 2508 2828 [[package]] 2829 + name = "maudit-example-image-processing" 2830 + version = "0.1.0" 2831 + dependencies = [ 2832 + "maud", 2833 + "maudit", 2834 + ] 2835 + 2836 + [[package]] 2509 2837 name = "maudit-example-kitchen-sink" 2510 2838 version = "0.1.0" 2511 2839 dependencies = [ ··· 2537 2865 "maud", 2538 2866 "maudit", 2539 2867 "serde", 2868 + ] 2869 + 2870 + [[package]] 2871 + name = "maybe-rayon" 2872 + version = "0.1.1" 2873 + source = "registry+https://github.com/rust-lang/crates.io-index" 2874 + checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" 2875 + dependencies = [ 2876 + "cfg-if", 2877 + "rayon", 2540 2878 ] 2541 2879 2542 2880 [[package]] ··· 2601 2939 ] 2602 2940 2603 2941 [[package]] 2942 + name = "minimal-lexical" 2943 + version = "0.2.1" 2944 + source = "registry+https://github.com/rust-lang/crates.io-index" 2945 + checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 2946 + 2947 + [[package]] 2604 2948 name = "miniz_oxide" 2605 2949 version = "0.8.9" 2606 2950 source = "registry+https://github.com/rust-lang/crates.io-index" 2607 2951 checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 2608 2952 dependencies = [ 2609 2953 "adler2", 2954 + "simd-adler32", 2610 2955 ] 2611 2956 2612 2957 [[package]] ··· 2708 3053 2709 3054 [[package]] 2710 3055 name = "nom" 3056 + version = "7.1.3" 3057 + source = "registry+https://github.com/rust-lang/crates.io-index" 3058 + checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 3059 + dependencies = [ 3060 + "memchr", 3061 + "minimal-lexical", 3062 + ] 3063 + 3064 + [[package]] 3065 + name = "nom" 2711 3066 version = "8.0.0" 2712 3067 source = "registry+https://github.com/rust-lang/crates.io-index" 2713 3068 checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" ··· 2720 3075 version = "0.5.5" 2721 3076 source = "registry+https://github.com/rust-lang/crates.io-index" 2722 3077 checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" 3078 + 3079 + [[package]] 3080 + name = "noop_proc_macro" 3081 + version = "0.3.0" 3082 + source = "registry+https://github.com/rust-lang/crates.io-index" 3083 + checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" 2723 3084 2724 3085 [[package]] 2725 3086 name = "normalize-path" ··· 2795 3156 version = "0.1.0" 2796 3157 source = "registry+https://github.com/rust-lang/crates.io-index" 2797 3158 checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 3159 + 3160 + [[package]] 3161 + name = "num-derive" 3162 + version = "0.4.2" 3163 + source = "registry+https://github.com/rust-lang/crates.io-index" 3164 + checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" 3165 + dependencies = [ 3166 + "proc-macro2", 3167 + "quote", 3168 + "syn 2.0.106", 3169 + ] 2798 3170 2799 3171 [[package]] 2800 3172 name = "num-integer" ··· 2806 3178 ] 2807 3179 2808 3180 [[package]] 3181 + name = "num-rational" 3182 + version = "0.4.2" 3183 + source = "registry+https://github.com/rust-lang/crates.io-index" 3184 + checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" 3185 + dependencies = [ 3186 + "num-bigint", 3187 + "num-integer", 3188 + "num-traits", 3189 + ] 3190 + 3191 + [[package]] 2809 3192 name = "num-traits" 2810 3193 version = "0.2.19" 2811 3194 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2925 3308 dependencies = [ 2926 3309 "bincode 2.0.1", 2927 3310 "flate2", 2928 - "nom", 3311 + "nom 8.0.0", 2929 3312 "rustc-hash", 2930 3313 "serde", 2931 3314 "serde_json", ··· 3021 3404 checksum = "5361ef66f38635d9671596d83abf44bcdbc3e39dbff303f3517828b86ad60e8f" 3022 3405 dependencies = [ 3023 3406 "bitflags 2.9.2", 3024 - "itertools", 3407 + "itertools 0.14.0", 3025 3408 "nonmax", 3026 3409 "oxc_index", 3027 3410 "oxc_syntax", ··· 3131 3514 checksum = "a19a662666a6a150e91a5aee2e99287800c561682841f68736170312b1275d10" 3132 3515 dependencies = [ 3133 3516 "fixedbitset", 3134 - "itertools", 3517 + "itertools 0.14.0", 3135 3518 "oxc_allocator", 3136 3519 "oxc_ast", 3137 3520 "oxc_data_structures", ··· 3228 3611 source = "registry+https://github.com/rust-lang/crates.io-index" 3229 3612 checksum = "06858ccc5976a5303ac903bef13d03b7f8860e837b7399e4899c05f5c729d30b" 3230 3613 dependencies = [ 3231 - "itertools", 3614 + "itertools 0.14.0", 3232 3615 "oxc_allocator", 3233 3616 "oxc_ast", 3234 3617 "oxc_ast_visit", ··· 3364 3747 ] 3365 3748 3366 3749 [[package]] 3750 + name = "oxipng" 3751 + version = "9.1.5" 3752 + source = "registry+https://github.com/rust-lang/crates.io-index" 3753 + checksum = "26c613f0f566526a647c7473f6a8556dbce22c91b13485ee4b4ec7ab648e4973" 3754 + dependencies = [ 3755 + "bitvec", 3756 + "clap", 3757 + "crossbeam-channel", 3758 + "env_logger", 3759 + "filetime", 3760 + "glob", 3761 + "indexmap", 3762 + "libdeflater", 3763 + "log", 3764 + "rayon", 3765 + "rgb", 3766 + "rustc-hash", 3767 + "zopfli", 3768 + ] 3769 + 3770 + [[package]] 3367 3771 name = "papaya" 3368 3772 version = "0.2.3" 3369 3773 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3403 3807 ] 3404 3808 3405 3809 [[package]] 3810 + name = "paste" 3811 + version = "1.0.15" 3812 + source = "registry+https://github.com/rust-lang/crates.io-index" 3813 + checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 3814 + 3815 + [[package]] 3406 3816 name = "path-slash" 3407 3817 version = "0.2.1" 3408 3818 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3579 3989 ] 3580 3990 3581 3991 [[package]] 3992 + name = "png" 3993 + version = "0.17.16" 3994 + source = "registry+https://github.com/rust-lang/crates.io-index" 3995 + checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" 3996 + dependencies = [ 3997 + "bitflags 1.3.2", 3998 + "crc32fast", 3999 + "fdeflate", 4000 + "flate2", 4001 + "miniz_oxide", 4002 + ] 4003 + 4004 + [[package]] 3582 4005 name = "pnp" 3583 4006 version = "0.12.1" 3584 4007 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3709 4132 ] 3710 4133 3711 4134 [[package]] 4135 + name = "profiling" 4136 + version = "1.0.17" 4137 + source = "registry+https://github.com/rust-lang/crates.io-index" 4138 + checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" 4139 + dependencies = [ 4140 + "profiling-procmacros", 4141 + ] 4142 + 4143 + [[package]] 4144 + name = "profiling-procmacros" 4145 + version = "1.0.17" 4146 + source = "registry+https://github.com/rust-lang/crates.io-index" 4147 + checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" 4148 + dependencies = [ 4149 + "quote", 4150 + "syn 2.0.106", 4151 + ] 4152 + 4153 + [[package]] 3712 4154 name = "project-origins" 3713 4155 version = "1.4.2" 3714 4156 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3739 4181 checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" 3740 4182 3741 4183 [[package]] 4184 + name = "qoi" 4185 + version = "0.4.1" 4186 + source = "registry+https://github.com/rust-lang/crates.io-index" 4187 + checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" 4188 + dependencies = [ 4189 + "bytemuck", 4190 + ] 4191 + 4192 + [[package]] 4193 + name = "quick-error" 4194 + version = "2.0.1" 4195 + source = "registry+https://github.com/rust-lang/crates.io-index" 4196 + checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" 4197 + 4198 + [[package]] 3742 4199 name = "quick-xml" 3743 4200 version = "0.38.2" 3744 4201 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3763 4220 checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 3764 4221 3765 4222 [[package]] 4223 + name = "radium" 4224 + version = "0.7.0" 4225 + source = "registry+https://github.com/rust-lang/crates.io-index" 4226 + checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" 4227 + 4228 + [[package]] 3766 4229 name = "radix_trie" 3767 4230 version = "0.2.1" 3768 4231 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3778 4241 source = "registry+https://github.com/rust-lang/crates.io-index" 3779 4242 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 3780 4243 dependencies = [ 4244 + "libc", 4245 + "rand_chacha 0.3.1", 3781 4246 "rand_core 0.6.4", 3782 4247 ] 3783 4248 ··· 3787 4252 source = "registry+https://github.com/rust-lang/crates.io-index" 3788 4253 checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 3789 4254 dependencies = [ 3790 - "rand_chacha", 4255 + "rand_chacha 0.9.0", 3791 4256 "rand_core 0.9.3", 3792 4257 ] 3793 4258 3794 4259 [[package]] 3795 4260 name = "rand_chacha" 4261 + version = "0.3.1" 4262 + source = "registry+https://github.com/rust-lang/crates.io-index" 4263 + checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 4264 + dependencies = [ 4265 + "ppv-lite86", 4266 + "rand_core 0.6.4", 4267 + ] 4268 + 4269 + [[package]] 4270 + name = "rand_chacha" 3796 4271 version = "0.9.0" 3797 4272 source = "registry+https://github.com/rust-lang/crates.io-index" 3798 4273 checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" ··· 3806 4281 version = "0.6.4" 3807 4282 source = "registry+https://github.com/rust-lang/crates.io-index" 3808 4283 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 4284 + dependencies = [ 4285 + "getrandom 0.2.16", 4286 + ] 3809 4287 3810 4288 [[package]] 3811 4289 name = "rand_core" ··· 3817 4295 ] 3818 4296 3819 4297 [[package]] 4298 + name = "rav1e" 4299 + version = "0.7.1" 4300 + source = "registry+https://github.com/rust-lang/crates.io-index" 4301 + checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" 4302 + dependencies = [ 4303 + "arbitrary", 4304 + "arg_enum_proc_macro", 4305 + "arrayvec", 4306 + "av1-grain", 4307 + "bitstream-io", 4308 + "built", 4309 + "cfg-if", 4310 + "interpolate_name", 4311 + "itertools 0.12.1", 4312 + "libc", 4313 + "libfuzzer-sys", 4314 + "log", 4315 + "maybe-rayon", 4316 + "new_debug_unreachable", 4317 + "noop_proc_macro", 4318 + "num-derive", 4319 + "num-traits", 4320 + "once_cell", 4321 + "paste", 4322 + "profiling", 4323 + "rand 0.8.5", 4324 + "rand_chacha 0.3.1", 4325 + "simd_helpers", 4326 + "system-deps", 4327 + "thiserror 1.0.69", 4328 + "v_frame", 4329 + "wasm-bindgen", 4330 + ] 4331 + 4332 + [[package]] 4333 + name = "ravif" 4334 + version = "0.11.20" 4335 + source = "registry+https://github.com/rust-lang/crates.io-index" 4336 + checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" 4337 + dependencies = [ 4338 + "avif-serialize", 4339 + "imgref", 4340 + "loop9", 4341 + "quick-error", 4342 + "rav1e", 4343 + "rayon", 4344 + "rgb", 4345 + ] 4346 + 4347 + [[package]] 3820 4348 name = "rayon" 3821 4349 version = "1.11.0" 3822 4350 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3903 4431 dependencies = [ 3904 4432 "hashbrown 0.15.5", 3905 4433 "memchr", 4434 + ] 4435 + 4436 + [[package]] 4437 + name = "rgb" 4438 + version = "0.8.52" 4439 + source = "registry+https://github.com/rust-lang/crates.io-index" 4440 + checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" 4441 + dependencies = [ 4442 + "bytemuck", 3906 4443 ] 3907 4444 3908 4445 [[package]] ··· 4120 4657 ] 4121 4658 4122 4659 [[package]] 4660 + name = "serde_spanned" 4661 + version = "0.6.9" 4662 + source = "registry+https://github.com/rust-lang/crates.io-index" 4663 + checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" 4664 + dependencies = [ 4665 + "serde", 4666 + ] 4667 + 4668 + [[package]] 4123 4669 name = "serde_urlencoded" 4124 4670 version = "0.7.1" 4125 4671 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4219 4765 checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" 4220 4766 dependencies = [ 4221 4767 "libc", 4768 + ] 4769 + 4770 + [[package]] 4771 + name = "simd-adler32" 4772 + version = "0.3.7" 4773 + source = "registry+https://github.com/rust-lang/crates.io-index" 4774 + checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 4775 + 4776 + [[package]] 4777 + name = "simd_helpers" 4778 + version = "0.1.0" 4779 + source = "registry+https://github.com/rust-lang/crates.io-index" 4780 + checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" 4781 + dependencies = [ 4782 + "quote", 4222 4783 ] 4223 4784 4224 4785 [[package]] ··· 4385 4946 ] 4386 4947 4387 4948 [[package]] 4949 + name = "system-deps" 4950 + version = "6.2.2" 4951 + source = "registry+https://github.com/rust-lang/crates.io-index" 4952 + checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" 4953 + dependencies = [ 4954 + "cfg-expr", 4955 + "heck", 4956 + "pkg-config", 4957 + "toml", 4958 + "version-compare", 4959 + ] 4960 + 4961 + [[package]] 4962 + name = "tap" 4963 + version = "1.0.1" 4964 + source = "registry+https://github.com/rust-lang/crates.io-index" 4965 + checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 4966 + 4967 + [[package]] 4388 4968 name = "tar" 4389 4969 version = "0.4.44" 4390 4970 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4394 4974 "libc", 4395 4975 "xattr", 4396 4976 ] 4977 + 4978 + [[package]] 4979 + name = "target-lexicon" 4980 + version = "0.12.16" 4981 + source = "registry+https://github.com/rust-lang/crates.io-index" 4982 + checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" 4397 4983 4398 4984 [[package]] 4399 4985 name = "tempfile" ··· 4488 5074 ] 4489 5075 4490 5076 [[package]] 5077 + name = "thumbhash" 5078 + version = "0.1.0" 5079 + source = "registry+https://github.com/rust-lang/crates.io-index" 5080 + checksum = "8b7726e0245a7331bd0c9a1fb4fd99fd695bcd478ca569f0eda2ff2cb14e7a00" 5081 + 5082 + [[package]] 5083 + name = "tiff" 5084 + version = "0.9.1" 5085 + source = "registry+https://github.com/rust-lang/crates.io-index" 5086 + checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" 5087 + dependencies = [ 5088 + "flate2", 5089 + "jpeg-decoder", 5090 + "weezl", 5091 + ] 5092 + 5093 + [[package]] 4491 5094 name = "time" 4492 5095 version = "0.3.41" 4493 5096 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4610 5213 ] 4611 5214 4612 5215 [[package]] 5216 + name = "toml" 5217 + version = "0.8.23" 5218 + source = "registry+https://github.com/rust-lang/crates.io-index" 5219 + checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" 5220 + dependencies = [ 5221 + "serde", 5222 + "serde_spanned", 5223 + "toml_datetime", 5224 + "toml_edit", 5225 + ] 5226 + 5227 + [[package]] 4613 5228 name = "toml_datetime" 4614 5229 version = "0.6.11" 4615 5230 source = "registry+https://github.com/rust-lang/crates.io-index" 4616 5231 checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" 5232 + dependencies = [ 5233 + "serde", 5234 + ] 4617 5235 4618 5236 [[package]] 4619 5237 name = "toml_edit" ··· 4622 5240 checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" 4623 5241 dependencies = [ 4624 5242 "indexmap", 5243 + "serde", 5244 + "serde_spanned", 4625 5245 "toml_datetime", 4626 5246 "toml_write", 4627 5247 "winnow", ··· 4980 5600 ] 4981 5601 4982 5602 [[package]] 5603 + name = "v_frame" 5604 + version = "0.3.9" 5605 + source = "registry+https://github.com/rust-lang/crates.io-index" 5606 + checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" 5607 + dependencies = [ 5608 + "aligned-vec", 5609 + "num-traits", 5610 + "wasm-bindgen", 5611 + ] 5612 + 5613 + [[package]] 4983 5614 name = "valuable" 4984 5615 version = "0.1.1" 4985 5616 source = "registry+https://github.com/rust-lang/crates.io-index" 4986 5617 checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 4987 5618 4988 5619 [[package]] 5620 + name = "version-compare" 5621 + version = "0.2.0" 5622 + source = "registry+https://github.com/rust-lang/crates.io-index" 5623 + checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" 5624 + 5625 + [[package]] 4989 5626 name = "version_check" 4990 5627 version = "0.9.5" 4991 5628 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5159 5796 ] 5160 5797 5161 5798 [[package]] 5799 + name = "webp" 5800 + version = "0.3.1" 5801 + source = "registry+https://github.com/rust-lang/crates.io-index" 5802 + checksum = "c071456adef4aca59bf6a583c46b90ff5eb0b4f758fc347cea81290288f37ce1" 5803 + dependencies = [ 5804 + "image", 5805 + "libwebp-sys", 5806 + ] 5807 + 5808 + [[package]] 5162 5809 name = "webpki-roots" 5163 5810 version = "1.0.2" 5164 5811 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5166 5813 dependencies = [ 5167 5814 "rustls-pki-types", 5168 5815 ] 5816 + 5817 + [[package]] 5818 + name = "weezl" 5819 + version = "0.1.10" 5820 + source = "registry+https://github.com/rust-lang/crates.io-index" 5821 + checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" 5169 5822 5170 5823 [[package]] 5171 5824 name = "winapi" ··· 5556 6209 checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 5557 6210 5558 6211 [[package]] 6212 + name = "wyz" 6213 + version = "0.5.1" 6214 + source = "registry+https://github.com/rust-lang/crates.io-index" 6215 + checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" 6216 + dependencies = [ 6217 + "tap", 6218 + ] 6219 + 6220 + [[package]] 5559 6221 name = "xattr" 5560 6222 version = "1.5.1" 5561 6223 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5689 6351 "quote", 5690 6352 "syn 2.0.106", 5691 6353 ] 6354 + 6355 + [[package]] 6356 + name = "zopfli" 6357 + version = "0.8.2" 6358 + source = "registry+https://github.com/rust-lang/crates.io-index" 6359 + checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" 6360 + dependencies = [ 6361 + "bumpalo", 6362 + "crc32fast", 6363 + "log", 6364 + "simd-adler32", 6365 + ] 6366 + 6367 + [[package]] 6368 + name = "zune-core" 6369 + version = "0.4.12" 6370 + source = "registry+https://github.com/rust-lang/crates.io-index" 6371 + checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" 6372 + 6373 + [[package]] 6374 + name = "zune-inflate" 6375 + version = "0.2.54" 6376 + source = "registry+https://github.com/rust-lang/crates.io-index" 6377 + checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" 6378 + dependencies = [ 6379 + "simd-adler32", 6380 + ] 6381 + 6382 + [[package]] 6383 + name = "zune-jpeg" 6384 + version = "0.4.20" 6385 + source = "registry+https://github.com/rust-lang/crates.io-index" 6386 + checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089" 6387 + dependencies = [ 6388 + "zune-core", 6389 + ]
+7 -1
crates/maudit/Cargo.toml
··· 6 6 repository = "https://github.com/bruits/maudit" 7 7 version = "0.3.2" 8 8 license = "MIT" 9 - edition = "2021" 9 + edition = "2024" 10 10 11 11 [features] 12 12 default = ["maud"] ··· 30 30 syntect = "5.0" 31 31 lol_html = "2.1.0" 32 32 slug = "0.1.6" 33 + image = "0.25.6" 34 + webp = "0.3.1" 33 35 34 36 maudit-macros = { path = "../maudit-macros", version = "0.3" } 35 37 log = { version = "0.4", features = ["kv"] } ··· 41 43 thiserror = "2.0.9" 42 44 blake3 = "1.8.2" 43 45 oxc_sourcemap = "4.1.0" 46 + rayon = "1.11.0" 47 + thumbhash = "0.1.0" 48 + base64 = "0.22.1" 49 + oxipng = "9.1.5"
+296 -26
crates/maudit/src/assets.rs
··· 1 + use base64::Engine; 1 2 use dyn_eq::DynEq; 3 + use image::GenericImageView; 2 4 use rustc_hash::FxHashSet; 3 5 use std::hash::Hash; 4 - use std::path::Path; 6 + use std::sync::OnceLock; 5 7 use std::{fs, path::PathBuf}; 8 + use thumbhash::{rgba_to_thumb_hash, thumb_hash_to_average_rgba, thumb_hash_to_rgba}; 6 9 7 10 #[derive(Default)] 8 11 pub struct PageAssets { 9 - pub(crate) assets: FxHashSet<Box<dyn Asset>>, 12 + pub(crate) images: FxHashSet<Image>, 10 13 pub(crate) scripts: FxHashSet<Script>, 11 14 pub(crate) styles: FxHashSet<Style>, 12 15 ··· 22 25 /// The image will not automatically be included in the page, but can be included through the `.url()` method on the returned `Image` object. 23 26 /// 24 27 /// Subsequent calls to this function using the same path will return the same image, as such, the value returned by this function can be cloned and used multiple times without issue. 25 - pub fn add_image<P>(&mut self, image_path: P) -> Image 28 + pub fn add_image_with_options<P>(&mut self, image_path: P, options: ImageOptions) -> Image 26 29 where 27 30 P: Into<PathBuf>, 28 31 { 29 32 let image_path = image_path.into(); 30 33 31 34 // Check if the image already exists in the assets, if so, return it 32 - if let Some(image) = self.assets.iter().find_map(|asset| { 33 - asset 34 - .as_any() 35 - .downcast_ref::<Image>() 36 - .filter(|image| image.path == image_path) 35 + if let Some(image) = self.images.iter().find_map(|asset| { 36 + asset.as_any().downcast_ref::<Image>().filter(|image| { 37 + image.path == image_path 38 + && options == *image.options.as_ref().unwrap_or(&ImageOptions::default()) 39 + }) 37 40 }) { 38 41 return image.clone(); 39 42 } 40 43 41 - let image = Box::new(Image { 44 + let image = Image { 42 45 path: image_path.clone(), 43 46 assets_dir: self.assets_dir.clone(), 44 47 hash: calculate_hash(&image_path), 45 - }); 48 + options: if options == ImageOptions::default() { 49 + None 50 + } else { 51 + Some(options) 52 + }, 53 + __cache_placeholder: OnceLock::new(), 54 + }; 46 55 47 - self.assets.insert(image.clone()); 56 + self.images.insert(image.clone()); 48 57 49 - *image 58 + image 59 + } 60 + 61 + pub fn add_image<P>(&mut self, image_path: P) -> Image 62 + where 63 + P: Into<PathBuf>, 64 + { 65 + self.add_image_with_options(image_path, ImageOptions::default()) 50 66 } 51 67 52 68 /// Add a script to the page assets, causing the file to be created in the output directory. The script is resolved relative to the current working directory. ··· 168 184 fn url(&self) -> Option<String>; 169 185 fn path(&self) -> &PathBuf; 170 186 171 - fn process(&self, _dist_assets_dir: &Path, _tmp_dir: &Path) -> Option<String> { 172 - None 173 - } 174 - 175 187 fn hash(&self) -> String { 176 188 // This will be overridden by each implementation to return the cached hash 177 189 String::new() ··· 234 246 dyn_eq::eq_trait_object!(Asset); 235 247 236 248 #[derive(Clone, PartialEq, Eq, Hash)] 249 + pub enum ImageFormat { 250 + Png, 251 + Jpeg, 252 + Webp, 253 + Avif, 254 + Gif, 255 + } 256 + 257 + impl ImageFormat { 258 + pub fn extension(&self) -> &'static str { 259 + match self { 260 + ImageFormat::Png => "png", 261 + ImageFormat::Jpeg => "jpg", 262 + ImageFormat::Webp => "webp", 263 + ImageFormat::Avif => "avif", 264 + ImageFormat::Gif => "gif", 265 + } 266 + } 267 + } 268 + 269 + impl From<ImageFormat> for image::ImageFormat { 270 + fn from(val: ImageFormat) -> Self { 271 + match val { 272 + ImageFormat::Png => image::ImageFormat::Png, 273 + ImageFormat::Jpeg => image::ImageFormat::Jpeg, 274 + ImageFormat::Webp => image::ImageFormat::WebP, 275 + ImageFormat::Avif => image::ImageFormat::Avif, 276 + ImageFormat::Gif => image::ImageFormat::Gif, 277 + } 278 + } 279 + } 280 + 281 + #[derive(Default, Clone, PartialEq, Eq, Hash)] 282 + pub struct ImageOptions { 283 + pub width: Option<u32>, 284 + pub height: Option<u32>, 285 + pub format: Option<ImageFormat>, 286 + } 287 + 288 + #[derive(Clone, PartialEq, Eq)] 237 289 #[non_exhaustive] 238 290 pub struct Image { 239 291 pub path: PathBuf, 240 292 pub(crate) assets_dir: PathBuf, 241 293 pub(crate) hash: String, 294 + pub(crate) options: Option<ImageOptions>, 295 + pub(crate) __cache_placeholder: OnceLock<ImagePlaceholder>, 296 + } 297 + 298 + impl Hash for Image { 299 + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { 300 + self.path.hash(state); 301 + self.assets_dir.hash(state); 302 + self.hash.hash(state); 303 + self.options.hash(state); 304 + } 305 + } 306 + 307 + impl Image { 308 + /// Get a placeholder for the image, which can be used for low-quality image placeholders (LQIP) or similar techniques. 309 + /// 310 + /// This uses the [ThumbHash](https://evanw.github.io/thumbhash/) algorithm to generate a very small placeholder image. 311 + pub fn placeholder(&self) -> &ImagePlaceholder { 312 + self.__cache_placeholder 313 + .get_or_init(|| get_placeholder(&self.path).unwrap_or_default()) 314 + } 315 + } 316 + 317 + #[derive(Debug, Clone, PartialEq, Default, Eq)] 318 + pub struct ImagePlaceholder { 319 + pub thumbhash: Vec<u8>, 320 + pub thumbhash_base64: String, 321 + pub average_rgba: Option<(u8, u8, u8, u8)>, 322 + pub data_uri: String, 323 + } 324 + 325 + fn get_placeholder(path: &PathBuf) -> Option<ImagePlaceholder> { 326 + let image = image::open(path).ok()?; 327 + let (width, height) = image.dimensions(); 328 + let (width, height) = (width as usize, height as usize); 329 + 330 + // If width or height > 100, resize image down to max 100 331 + let (width, height, rgba) = if width.max(height) > 100 { 332 + let scale = 100.0 / width.max(height) as f32; 333 + let new_width = (width as f32 * scale).round() as usize; 334 + let new_height = (height as f32 * scale).round() as usize; 335 + 336 + let resized = image::imageops::resize( 337 + &image, 338 + new_width as u32, 339 + new_height as u32, 340 + image::imageops::FilterType::Triangle, 341 + ); 342 + (new_width, new_height, resized.into_raw()) 343 + } else { 344 + (width, height, image.to_rgba8().into_raw()) 345 + }; 346 + 347 + let thumb_hash = rgba_to_thumb_hash(width, height, &rgba); 348 + let average_rgba = thumb_hash_to_average_rgba(&thumb_hash) 349 + .ok() 350 + .map(|(r, g, b, a)| { 351 + ( 352 + (r * 255.0) as u8, 353 + (g * 255.0) as u8, 354 + (b * 255.0) as u8, 355 + (a * 255.0) as u8, 356 + ) 357 + }); 358 + 359 + let thumbhash_rgba = thumb_hash_to_rgba(&thumb_hash).ok().unwrap(); 360 + let thumbhash_png = thumbhash_to_png(&thumbhash_rgba); 361 + let optimized_png = oxipng::optimize_from_memory(&thumbhash_png, &Default::default()).unwrap(); 362 + 363 + let base64 = base64::engine::general_purpose::STANDARD.encode(&optimized_png); 364 + let data_uri = format!("data:image/png;base64,{}", base64); 365 + 366 + let thumbhash_base64 = base64::engine::general_purpose::STANDARD.encode(&thumb_hash); 367 + 368 + Some(ImagePlaceholder { 369 + thumbhash: thumb_hash, 370 + thumbhash_base64, 371 + average_rgba, 372 + data_uri, 373 + }) 374 + } 375 + 376 + /// Port of https://github.com/evanw/thumbhash/blob/a652ce6ed691242f459f468f0a8756cda3b90a82/js/thumbhash.js#L234 377 + /// TODO: Do this some other way, not only is the code, well, unreadable, the result is also quite inefficient. 378 + fn thumbhash_to_png(thumbhash_rgba: &(usize, usize, Vec<u8>)) -> Vec<u8> { 379 + let w = thumbhash_rgba.0 as u32; 380 + let h = thumbhash_rgba.1 as u32; 381 + let rgba = &thumbhash_rgba.2; 382 + 383 + let row = w * 4 + 1; 384 + let idat = 6 + h * (5 + row); 385 + 386 + let mut bytes = vec![ 387 + 137, 388 + 80, 389 + 78, 390 + 71, 391 + 13, 392 + 10, 393 + 26, 394 + 10, 395 + 0, 396 + 0, 397 + 0, 398 + 13, 399 + 73, 400 + 72, 401 + 68, 402 + 82, 403 + 0, 404 + 0, 405 + (w >> 8) as u8, 406 + (w & 255) as u8, 407 + 0, 408 + 0, 409 + (h >> 8) as u8, 410 + (h & 255) as u8, 411 + 8, 412 + 6, 413 + 0, 414 + 0, 415 + 0, 416 + 0, 417 + 0, 418 + 0, 419 + 0, 420 + (idat >> 24) as u8, 421 + ((idat >> 16) & 255) as u8, 422 + ((idat >> 8) & 255) as u8, 423 + (idat & 255) as u8, 424 + 73, 425 + 68, 426 + 65, 427 + 84, 428 + 120, 429 + 1, 430 + ]; 431 + 432 + let table = [ 433 + 0u32, 498536548, 997073096, 651767980, 1994146192, 1802195444, 1303535960, 1342533948, 434 + 3988292384, 4027552580, 3604390888, 3412177804, 2607071920, 2262029012, 2685067896, 435 + 3183342108, 436 + ]; 437 + 438 + let mut a = 1u32; 439 + let mut b = 0u32; 440 + let mut i = 0usize; 441 + let mut end = (row - 1) as usize; 442 + 443 + for y in 0..h { 444 + let filter_type = if y + 1 < h { 0 } else { 1 }; 445 + bytes.extend_from_slice(&[ 446 + filter_type, 447 + (row & 255) as u8, 448 + (row >> 8) as u8, 449 + (!row & 255) as u8, 450 + ((row >> 8) ^ 255) as u8, 451 + 0, 452 + ]); 453 + 454 + b = (b + a) % 65521; 455 + while i < end { 456 + let u = rgba[i]; 457 + bytes.push(u); 458 + a = (a + u as u32) % 65521; 459 + b = (b + a) % 65521; 460 + i += 1; 461 + } 462 + end += (row - 1) as usize; 463 + } 464 + 465 + bytes.extend_from_slice(&[ 466 + (b >> 8) as u8, 467 + (b & 255) as u8, 468 + (a >> 8) as u8, 469 + (a & 255) as u8, 470 + 0, 471 + 0, 472 + 0, 473 + 0, 474 + 0, 475 + 0, 476 + 0, 477 + 0, 478 + 73, 479 + 69, 480 + 78, 481 + 68, 482 + 174, 483 + 66, 484 + 96, 485 + 130, 486 + ]); 487 + 488 + let ranges = [(12usize, 29usize), (37usize, 41 + idat as usize)]; 489 + 490 + for (start, end_pos) in ranges { 491 + let mut c = !0u32; 492 + for &byte in &bytes[start..end_pos] { 493 + c ^= byte as u32; 494 + c = (c >> 4) ^ table[(c & 15) as usize]; 495 + c = (c >> 4) ^ table[(c & 15) as usize]; 496 + } 497 + c = !c; 498 + let mut end_idx = end_pos; 499 + bytes[end_idx] = (c >> 24) as u8; 500 + end_idx += 1; 501 + bytes[end_idx] = ((c >> 16) & 255) as u8; 502 + end_idx += 1; 503 + bytes[end_idx] = ((c >> 8) & 255) as u8; 504 + end_idx += 1; 505 + bytes[end_idx] = (c & 255) as u8; 506 + } 507 + 508 + bytes 242 509 } 243 510 244 511 impl InternalAsset for Image { ··· 265 532 self.hash.clone() 266 533 } 267 534 268 - fn process(&self, dist_path: &Path, _: &Path) -> Option<String> { 269 - // TODO: Image processing 270 - fs::copy(&self.path, dist_path.join(self.final_file_name())).unwrap(); 271 - 272 - None 535 + fn final_extension(&self) -> String { 536 + if let Some(options) = &self.options 537 + && let Some(format) = &options.format 538 + { 539 + format.extension() 540 + } else { 541 + self.path() 542 + .extension() 543 + .and_then(|ext| ext.to_str()) 544 + .unwrap_or_default() 545 + } 546 + .to_string() 273 547 } 274 548 } 275 549 ··· 357 631 fn hash(&self) -> String { 358 632 self.hash.clone() 359 633 } 360 - 361 - fn process(&self, _: &Path, _: &Path) -> Option<String> { 362 - None 363 - } 364 634 } 365 635 366 636 #[cfg(test)] ··· 440 710 }; 441 711 442 712 page_assets.add_image(temp_dir.join("image.png")); 443 - assert!(page_assets.assets.len() == 1); 713 + assert!(page_assets.images.len() == 1); 444 714 } 445 715 446 716 #[test]
+62 -52
crates/maudit/src/build.rs
··· 1 1 use core::panic; 2 2 use std::{ 3 3 env, 4 - fs::{self, remove_dir_all, File}, 4 + fs::{self, File, remove_dir_all}, 5 5 io::{self, Write}, 6 6 path::{Path, PathBuf}, 7 7 process::Command, ··· 11 11 }; 12 12 13 13 use crate::{ 14 - assets, 14 + BuildOptions, BuildOutput, 15 + assets::{self}, 15 16 content::{Content, ContentSources}, 16 17 errors::BuildError, 17 18 logging::print_title, 18 19 page::{DynamicRouteContext, FullPage, RenderResult, RouteContext, RouteParams, RouteType}, 19 20 route::{ 20 - extract_params_from_raw_route, get_route_file_path, get_route_type_from_route_params, 21 - get_route_url, ParameterDef, 21 + ParameterDef, extract_params_from_raw_route, get_route_file_path, 22 + get_route_type_from_route_params, get_route_url, 22 23 }, 23 - BuildOptions, BuildOutput, 24 24 }; 25 25 use colored::{ColoredString, Colorize}; 26 26 use log::{info, trace}; 27 27 use oxc_sourcemap::SourceMap; 28 28 use rolldown::{ 29 - plugin::{HookUsage, Plugin}, 30 29 Bundler, BundlerOptions, InputItem, ModuleType, 30 + plugin::{HookUsage, Plugin}, 31 31 }; 32 32 use rustc_hash::{FxHashMap, FxHashSet}; 33 33 34 34 use crate::assets::Asset; 35 - use crate::logging::{format_elapsed_time, FormatElapsedTimeOptions}; 35 + use crate::logging::{FormatElapsedTimeOptions, format_elapsed_time}; 36 36 37 - use lol_html::{element, rewrite_str, RewriteStrSettings}; 37 + use lol_html::{RewriteStrSettings, element, rewrite_str}; 38 + use rayon::prelude::*; 38 39 40 + pub mod images; 39 41 pub mod metadata; 40 42 pub mod options; 41 43 ··· 175 177 let source_start = SystemTime::now(); 176 178 source.init(); 177 179 178 - info!(target: "build", "{} initialized in {}", source.get_name(), format_elapsed_time(source_start.elapsed(), &FormatElapsedTimeOptions::default()).unwrap()); 180 + info!(target: "content", "{} initialized in {}", source.get_name(), format_elapsed_time(source_start.elapsed(), &FormatElapsedTimeOptions::default()).unwrap()); 179 181 }); 180 182 181 - info!(target: "build", "{}", format!("Content sources initialized in {}", format_elapsed_time( 183 + info!(target: "content", "{}", format!("Content sources initialized in {}", format_elapsed_time( 182 184 content_sources_start.elapsed(), 183 185 &FormatElapsedTimeOptions::default(), 184 186 ).unwrap()).bold()); ··· 206 208 ..Default::default() 207 209 }; 208 210 209 - let mut build_pages_assets: FxHashSet<Box<dyn Asset>> = FxHashSet::default(); 211 + #[allow(clippy::mutable_key_type)] // Image's Hash does not depend on mutable fields 212 + let mut build_pages_images: FxHashSet<assets::Image> = FxHashSet::default(); 210 213 let mut build_pages_scripts: FxHashSet<assets::Script> = FxHashSet::default(); 211 214 let mut build_pages_styles: FxHashSet<assets::Style> = FxHashSet::default(); 212 215 ··· 248 251 route.route_raw(), 249 252 )?; 250 253 251 - info!(target: "build", "{} -> {} {}", get_route_url(&route.route_raw(), &params_def, &params), file_path.to_string_lossy().dimmed(), format_elapsed_time(route_start.elapsed(), &route_format_options).unwrap()); 254 + info!(target: "pages", "{} -> {} {}", get_route_url(&route.route_raw(), &params_def, &params), file_path.to_string_lossy().dimmed(), format_elapsed_time(route_start.elapsed(), &route_format_options).unwrap()); 252 255 253 - build_pages_assets.extend(page_assets.assets); 256 + build_pages_images.extend(page_assets.images); 254 257 build_pages_scripts.extend(page_assets.scripts); 255 258 build_pages_styles.extend(page_assets.styles); 256 259 ··· 310 313 route.route_raw(), 311 314 )?; 312 315 313 - info!(target: "build", "├─ {} {}", file_path.to_string_lossy().dimmed(), format_elapsed_time(route_start.elapsed(), &route_format_options).unwrap()); 316 + info!(target: "pages", "├─ {} {}", file_path.to_string_lossy().dimmed(), format_elapsed_time(route_start.elapsed(), &route_format_options).unwrap()); 314 317 315 - build_pages_assets.extend(pages_assets.assets); 318 + build_pages_images.extend(pages_assets.images); 316 319 build_pages_scripts.extend(pages_assets.scripts); 317 320 build_pages_styles.extend(pages_assets.styles); 318 321 ··· 322 325 } 323 326 } 324 327 325 - info!(target: "build", "{}", format!("generated {} pages in {}", page_count, format_elapsed_time(pages_start.elapsed(), &section_format_options).unwrap()).bold()); 328 + info!(target: "pages", "{}", format!("generated {} pages in {}", page_count, format_elapsed_time(pages_start.elapsed(), &section_format_options).unwrap()).bold()); 326 329 327 - if !build_pages_assets.is_empty() 328 - || !build_pages_styles.is_empty() 329 - || !build_pages_scripts.is_empty() 330 - { 330 + if !build_pages_styles.is_empty() || !build_pages_scripts.is_empty() { 331 331 let assets_start = SystemTime::now(); 332 332 print_title("generating assets"); 333 333 334 - build_pages_assets.iter().for_each(|asset| { 335 - asset.process(&assets_dir, &tmp_dir); 336 - 337 - // TODO: Add outputted assets to build_metadata, might need dedicated fs methods for this 338 - }); 339 - 340 334 let css_inputs = build_pages_styles 341 335 .iter() 342 - .map(|style| { 343 - let processed_path = style.process(&assets_dir, &tmp_dir); 344 - 345 - InputItem { 346 - name: Some( 347 - style 348 - .final_file_name() 349 - .strip_suffix(&format!( 350 - ".{}", 351 - style 352 - .path() 353 - .extension() 354 - .map(|ext| ext.to_str().unwrap()) 355 - .unwrap_or("") 356 - )) 357 - .unwrap_or(&style.final_file_name()) 358 - .to_string(), 359 - ), 360 - import: { 361 - if let Some(processed_path) = processed_path { 362 - processed_path 363 - } else { 364 - style.path().to_string_lossy().to_string() 365 - } 366 - }, 367 - } 336 + .map(|style| InputItem { 337 + name: Some( 338 + style 339 + .final_file_name() 340 + .strip_suffix(&format!( 341 + ".{}", 342 + style 343 + .path() 344 + .extension() 345 + .map(|ext| ext.to_str().unwrap()) 346 + .unwrap_or("") 347 + )) 348 + .unwrap_or(&style.final_file_name()) 349 + .to_string(), 350 + ), 351 + import: { style.path().to_string_lossy().to_string() }, 368 352 }) 369 353 .collect::<Vec<InputItem>>(); 370 354 ··· 418 402 } 419 403 420 404 info!(target: "build", "{}", format!("Assets generated in {}", format_elapsed_time(assets_start.elapsed(), &section_format_options).unwrap()).bold()); 405 + } 406 + 407 + if !build_pages_images.is_empty() { 408 + print_title("processing images"); 409 + 410 + let start_time = SystemTime::now(); 411 + build_pages_images.par_iter().for_each(|image| { 412 + let start_process = SystemTime::now(); 413 + let dest_path = assets_dir.join(image.final_file_name()); 414 + if let Some(image_options) = &image.options { 415 + images::process_image(image, &dest_path, image_options); 416 + } else if !dest_path.exists() { 417 + // TODO: Check if copying should be done in this parallel iterator, I/O doesn't benefit from parallelism so having those tasks here might just be slowing processing 418 + fs::copy(image.path(), &dest_path).unwrap_or_else(|e| { 419 + panic!( 420 + "Failed to copy image from {} to {}: {}", 421 + image.path().to_string_lossy(), 422 + dest_path.to_string_lossy(), 423 + e 424 + ) 425 + }); 426 + } 427 + info!(target: "assets", "{} -> {} {}", image.path().to_string_lossy(), dest_path.to_string_lossy().dimmed(), format_elapsed_time(start_process.elapsed(), &route_format_options).unwrap().dimmed()); 428 + }); 429 + 430 + info!(target: "assets", "{}", format!("Images processed in {}", format_elapsed_time(start_time.elapsed(), &section_format_options).unwrap()).bold()); 421 431 } 422 432 423 433 // Check if static directory exists
+40
crates/maudit/src/build/images.rs
··· 1 + use std::{fs::File, io::BufWriter, path::Path}; 2 + 3 + use image::ImageReader; 4 + use webp::{Encoder, WebPMemory}; 5 + 6 + use crate::assets::{Asset, Image, ImageFormat, ImageOptions}; 7 + 8 + pub fn process_image(image: &Image, dest_path: &Path, image_options: &ImageOptions) { 9 + let mut img = ImageReader::open(image.path()).unwrap().decode().unwrap(); 10 + 11 + let new_format = image_options.format.clone().unwrap_or(ImageFormat::Webp); 12 + let new_width = image_options.width.unwrap_or(img.width()); 13 + let new_height = image_options.height.unwrap_or(img.height()); 14 + 15 + if new_width != img.width() || new_height != img.height() { 16 + img = img.resize(new_width, new_height, image::imageops::FilterType::Lanczos3); 17 + } 18 + 19 + // image doesn't support lossy WebP encoding, so we'll use webp directly for that to avoid huge files 20 + // TODO: Add a feature so that people can choose not to depend on libwebp 21 + // TODO: Add a way for people to choose lossless WebP encoding, despite the larger file sizes 22 + if new_format == ImageFormat::Webp { 23 + let encoder: Encoder = Encoder::from_image(&img).unwrap(); 24 + let webp: WebPMemory = encoder.encode(90f32); // TODO: Allow configuring quality 25 + std::fs::write(dest_path, &*webp).unwrap(); 26 + } else { 27 + let file = File::create(dest_path).unwrap(); 28 + 29 + let mut writer = BufWriter::new(file); 30 + img.write_to(&mut writer, new_format.into()) 31 + .unwrap_or_else(|e| { 32 + panic!( 33 + "Failed to process image from {} to {}: {}", 34 + image.path().to_string_lossy(), 35 + dest_path.to_string_lossy(), 36 + e 37 + ) 38 + }); 39 + } 40 + }
+9 -9
crates/maudit/src/content/markdown.rs
··· 2 2 3 3 use glob::glob as glob_fs; 4 4 use log::warn; 5 - use pulldown_cmark::{html::push_html, CodeBlockKind, Event, Options, Parser, Tag, TagEnd}; 5 + use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd, html::push_html}; 6 6 use serde::de::DeserializeOwned; 7 7 8 8 pub mod components; ··· 11 11 12 12 use crate::{assets::Asset, page::RouteContext}; 13 13 14 - use super::{highlight::CodeBlock, slugger, ContentEntry}; 14 + use super::{ContentEntry, highlight::CodeBlock, slugger}; 15 15 16 16 /// Represents a Markdown heading. 17 17 /// ··· 185 185 for entry in glob_fs(pattern).unwrap() { 186 186 let entry = entry.unwrap(); 187 187 188 - if let Some(extension) = entry.extension() { 189 - if extension != "md" { 190 - warn!("Other file types than Markdown are not supported yet"); 191 - continue; 192 - } 188 + if let Some(extension) = entry.extension() 189 + && extension != "md" 190 + { 191 + warn!("Other file types than Markdown are not supported yet"); 192 + continue; 193 193 } 194 194 195 195 let id = entry.file_stem().unwrap().to_str().unwrap().to_string(); ··· 361 361 } 362 362 363 363 // TODO: Handle this differently so it's compatible with the component system - erika, 2025-08-24 364 - Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(ref fence))) => { 364 + Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(fence))) => { 365 365 let (block, begin) = CodeBlock::new(fence); 366 366 code_block = Some(block); 367 367 events[i] = Event::Html(begin.into()); ··· 378 378 } 379 379 380 380 // TODO: User should be able to replace the text component too perhaps, but it'd require merging the text events 381 - Event::Text(ref text) => { 381 + Event::Text(text) => { 382 382 if !in_frontmatter { 383 383 if in_image { 384 384 // This seem to work to create "an empty event", but it's not ideal. Using `events.remove` is probably
+4
examples/image-processing/.gitignore
··· 1 + target 2 + dist 3 + node_modules 4 + .DS_Store
+12
examples/image-processing/Cargo.toml
··· 1 + [package] 2 + name = "maudit-example-image-processing" 3 + version = "0.1.0" 4 + edition = "2021" 5 + publish = false 6 + 7 + [package.metadata.maudit] 8 + intended_version = "0.3.0" 9 + 10 + [dependencies] 11 + maudit = { workspace = true } 12 + maud = "0.26.0"
+1
examples/image-processing/images/logo.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" width="357.3" height="281" fill="none"><path fill="#0d0d0d" d="M303 267c-51-6-51-6-83-5h-32l-63 7a1419 1419 0 0 0-92 12L22 173 12 66l-3-1C3 63-1 55 0 46c0-5 1-6 6-9 5-4 5-4 11-3 5 0 7 1 9 3 3 3 3 3 4 12 0 8 0 10-2 12l-2 2 15 19c22 27 58 67 59 66l4-15a1924 1924 0 0 1 13-64l-4-8c-3-3-7-11-9-16l-3-10 6-7c6-6 6-7 13-8 9-3 13-1 18 5 5 8 5 11 1 23l-4 12 7 21c7 19 7 19 27 52 20 32 25 39 26 36l3-14 14-60 10-48c0-2-2-3-5-6l-5-7 1-6c1-6 2-7 7-11 4-2 6-3 10-2 6 0 8 1 12 7 4 7 5 15 3 19l-6 5-5 3c0 5 48 117 49 117 4 0 5-5 18-63l13-59-6-4-5-3-1-10-1-10 6-8 6-8 8-1c8 0 8 0 13 3s6 4 7 11c2 8 2 15 0 18l-8 6-6 4 8 34 8 34 8 81c7 80 7 81 5 81z"/></svg>
examples/image-processing/images/walrus.jpg

This is a binary file and will not be displayed.

+16
examples/image-processing/src/layout.rs
··· 1 + use maud::{html, Markup, DOCTYPE}; 2 + 3 + pub fn layout(content: Markup) -> Markup { 4 + html! { 5 + (DOCTYPE) 6 + html { 7 + head { 8 + meta charset="utf-8"; 9 + title { "Test page" } 10 + } 11 + body { 12 + (content) 13 + } 14 + } 15 + } 16 + }
+14
examples/image-processing/src/main.rs
··· 1 + mod layout; 2 + 3 + use maudit::{coronate, routes, BuildOptions, BuildOutput}; 4 + 5 + mod pages { 6 + mod index; 7 + pub use index::Index; 8 + } 9 + 10 + pub use pages::Index; 11 + 12 + fn main() -> Result<BuildOutput, Box<dyn std::error::Error>> { 13 + coronate(routes![Index], vec![].into(), BuildOptions::default()) 14 + }
+30
examples/image-processing/src/pages/index.rs
··· 1 + use crate::layout::layout; 2 + use maud::html; 3 + use maudit::{assets::ImageOptions, page::prelude::*}; 4 + 5 + #[route("/")] 6 + pub struct Index; 7 + 8 + impl Page for Index { 9 + fn render(&self, ctx: &mut RouteContext) -> RenderResult { 10 + let logo = ctx.assets.add_image("images/logo.svg"); 11 + let walrus = ctx.assets.add_image_with_options( 12 + "images/walrus.jpg", 13 + ImageOptions { 14 + width: Some(200), 15 + height: Some(200), 16 + format: Some(maudit::assets::ImageFormat::Webp), 17 + }, 18 + ); 19 + 20 + println!("Walrus placeholder ({:?})", walrus.placeholder()); 21 + 22 + layout(html! { 23 + (logo) 24 + h1 { "Hello World" } 25 + h2 { "Here's a 200x200 walrus:" } 26 + (walrus) 27 + }) 28 + .into() 29 + } 30 + }
+2 -1
package.json
··· 5 5 "dependencies": { 6 6 "@tailwindcss/cli": "^4.0.0", 7 7 "@tailwindcss/typography": "^0.5.15", 8 - "tailwindcss": "^4.0.0" 8 + "tailwindcss": "^4.0.0", 9 + "thumbhash": "^0.1.1" 9 10 } 10 11 }
+8
pnpm-lock.yaml
··· 17 17 tailwindcss: 18 18 specifier: ^4.0.0 19 19 version: 4.0.0 20 + thumbhash: 21 + specifier: ^0.1.1 22 + version: 0.1.1 20 23 21 24 packages: 22 25 ··· 327 330 resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} 328 331 engines: {node: '>=6'} 329 332 333 + thumbhash@0.1.1: 334 + resolution: {integrity: sha512-kH5pKeIIBPQXAOni2AiY/Cu/NKdkFREdpH+TLdM0g6WA7RriCv0kPLgP731ady67MhTAqrVG/4mnEeibVuCJcg==} 335 + 330 336 to-regex-range@5.0.1: 331 337 resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 332 338 engines: {node: '>=8.0'} ··· 569 575 tailwindcss@4.0.0: {} 570 576 571 577 tapable@2.2.1: {} 578 + 579 + thumbhash@0.1.1: {} 572 580 573 581 to-regex-range@5.0.1: 574 582 dependencies: