diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..22b6c3a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf + +[*.js] +indent_style = space +indent_size = 2 + +[*.json] +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore index 51174d6..e54971b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,12 @@ -# wallpapers -/src/wallpapers.rs - # built css /public/style.css +# wallpapers +wallpapers.json + # build output -/target +target/ +pkg # environment variables .env @@ -15,4 +16,7 @@ .DS_Store # ide -.idea/ \ No newline at end of file +.idea/ + +# rustfmt +**/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock index c783cd6..5abd17b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,15 +29,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -48,19 +39,10 @@ dependencies = [ ] [[package]] -name = "alloc-no-stdlib" -version = "2.0.4" +name = "allocator-api2" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "android-tzdata" @@ -125,6 +107,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" + [[package]] name = "approx" version = "0.5.1" @@ -135,89 +123,10 @@ dependencies = [ ] [[package]] -name = "askama" -version = "0.12.1" +name = "async-recursion" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" -dependencies = [ - "askama_derive", - "askama_escape", - "humansize", - "num-traits", - "percent-encoding", -] - -[[package]] -name = "askama_derive" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83" -dependencies = [ - "askama_parser", - "basic-toml", - "mime", - "mime_guess", - "proc-macro2", - "quote", - "serde", - "syn 2.0.58", -] - -[[package]] -name = "askama_escape" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" - -[[package]] -name = "askama_parser" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0" -dependencies = [ - "nom", -] - -[[package]] -name = "askama_rocket" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebf814a0f98928b2ea4a0625ad7563d737ecf0707260b8360f4ad2a4b51918e6" -dependencies = [ - "askama", - "rocket", -] - -[[package]] -name = "async-compression" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86a9249d1447a85f95810c620abea82e001fe58a31713fcce614caf52499f905" -dependencies = [ - "brotli", - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", @@ -236,18 +145,33 @@ dependencies = [ ] [[package]] -name = "atomic" -version = "0.5.3" +name = "attribute-derive" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" +checksum = "8b48808b337d6b74c15ff9becfc0e139fe2b4e2b224d670a0ecdb46b0b2d3d9b" +dependencies = [ + "attribute-derive-macro", + "derive-where", + "manyhow", + "proc-macro2", + "quote", + "syn 2.0.58", +] [[package]] -name = "atomic" -version = "0.6.0" +name = "attribute-derive-macro" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" +checksum = "5b19cbd63850ecff821c413e12846a67ec9f4ce7309c70959b94ecf9b2575ee2" dependencies = [ - "bytemuck", + "collection_literals", + "interpolator", + "manyhow", + "proc-macro-utils", + "proc-macro2", + "quote", + "quote-use", + "syn 2.0.58", ] [[package]] @@ -256,6 +180,62 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.2.0", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "multer", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.0", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -278,19 +258,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] -name = "basic-toml" -version = "0.1.9" +name = "base64" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" -dependencies = [ - "serde", -] - -[[package]] -name = "binascii" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" [[package]] name = "bitflags" @@ -314,45 +285,56 @@ dependencies = [ "radium", ] -[[package]] -name = "brotli" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - [[package]] name = "bumpalo" version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" -[[package]] -name = "bytemuck" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" - [[package]] name = "bytes" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +[[package]] +name = "cached" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90eb5776f28a149524d1d8623035760b4454ec881e8cf3838fa8d7e1b11254b3" +dependencies = [ + "cached_proc_macro", + "cached_proc_macro_types", + "hashbrown 0.13.2", + "instant", + "once_cell", + "thiserror", +] + +[[package]] +name = "cached_proc_macro" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cached_proc_macro_types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" + +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" + [[package]] name = "cc" version = "1.0.90" @@ -379,6 +361,33 @@ dependencies = [ "windows-targets 0.52.4", ] +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "clap" version = "4.5.4" @@ -398,7 +407,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.1", ] [[package]] @@ -419,6 +428,12 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +[[package]] +name = "collection_literals" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271" + [[package]] name = "colorchoice" version = "1.0.0" @@ -426,20 +441,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] -name = "convert_case" -version = "0.4.0" +name = "config" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" +dependencies = [ + "convert_case", + "lazy_static", + "nom", + "pathdiff", + "serde", + "toml", +] [[package]] -name = "cookie" -version = "0.18.1" +name = "console_error_panic_hook" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "percent-encoding", - "time", - "version_check", + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", ] [[package]] @@ -459,76 +510,101 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] -name = "crc32fast" -version = "1.4.0" +name = "crunchy" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "cfg-if", + "darling_core 0.14.4", + "darling_macro 0.14.4", ] [[package]] -name = "css-minify" -version = "0.3.1" +name = "darling" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874c6e2d19f8d4a285083b11a3241bfbe01ac3ed85f26e1e6b34888d960552bd" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ - "derive_more", - "indexmap 1.9.3", - "nom", + "darling_core 0.20.8", + "darling_macro 0.20.8", ] [[package]] -name = "deranged" -version = "0.3.11" +name = "darling_core" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", + "fnv", + "ident_case", "proc-macro2", "quote", - "rustc_version", + "strsim 0.10.0", "syn 1.0.109", ] [[package]] -name = "devise" -version = "0.4.1" +name = "darling_core" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6eacefd3f541c66fc61433d65e54e0e46e0a029a819a7dbbc7a7b489e8a85f8" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 2.0.58", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core 0.20.8", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.3", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "derive-where" +version = "1.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ - "devise_codegen", - "devise_core", -] - -[[package]] -name = "devise_codegen" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8cf4b8dd484ede80fd5c547592c46c3745a617c8af278e2b72bea86b2dfed6" -dependencies = [ - "devise_core", - "quote", -] - -[[package]] -name = "devise_core" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a" -dependencies = [ - "bitflags 2.5.0", "proc-macro2", - "proc-macro2-diagnostics", "quote", "syn 2.0.58", ] @@ -547,6 +623,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "drain_filter_polyfill" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" + [[package]] name = "either" version = "1.10.0" @@ -584,30 +666,6 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" -[[package]] -name = "figment" -version = "0.10.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7270677e7067213e04f323b55084586195f18308cd7546cfac9f873344ccceb6" -dependencies = [ - "atomic 0.6.0", - "pear", - "serde", - "toml", - "uncased", - "version_check", -] - -[[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 = "fnv" version = "1.0.7" @@ -739,19 +797,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "generator" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" -dependencies = [ - "cc", - "libc", - "log", - "rustversion", - "windows", -] - [[package]] name = "geo-types" version = "0.7.13" @@ -770,8 +815,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -781,10 +828,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] -name = "glob" -version = "0.3.1" +name = "gloo-net" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http 0.2.12", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] [[package]] name = "h2" @@ -797,8 +872,8 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.2.6", + "http 0.2.12", + "indexmap", "slab", "tokio", "tokio-util", @@ -806,26 +881,30 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.12.3" +name = "half" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if", + "crunchy", +] [[package]] name = "hashbrown" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", - "bumpalo", -] [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" @@ -839,6 +918,15 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "html-escape" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +dependencies = [ + "utf8-width", +] + [[package]] name = "http" version = "0.2.12" @@ -850,6 +938,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -857,10 +956,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http 1.1.0", + "http-body 1.0.0", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe" + [[package]] name = "httparse" version = "1.8.0" @@ -873,15 +1001,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humansize" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" -dependencies = [ - "libm", -] - [[package]] name = "hyper" version = "0.14.28" @@ -893,8 +1012,8 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -906,6 +1025,25 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -913,12 +1051,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.28", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.2.0", + "pin-project-lite", + "socket2", + "tokio", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -942,6 +1096,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.5.0" @@ -952,16 +1112,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.2.6" @@ -970,14 +1120,28 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", - "serde", ] [[package]] -name = "inlinable_string" -version = "0.1.15" +name = "instant" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "interpolator" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" + +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" [[package]] name = "ipnet" @@ -985,17 +1149,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "is-terminal" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "itertools" version = "0.12.1" @@ -1035,6 +1188,268 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "leaflet" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1ca4f4ecc791314ac38d50a5af6dc6506977aa7c7f0231b1bfd66989a9c4be3" +dependencies = [ + "js-sys", + "paste", + "url", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd996d00a55895327b2eb5d2d7d440ab756b4a9fe43ec78411c74199306808c" +dependencies = [ + "cfg-if", + "leptos_config", + "leptos_dom", + "leptos_macro", + "leptos_reactive", + "leptos_server", + "server_fn", + "tracing", + "typed-builder", + "typed-builder-macro", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos-leaflet" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c65a03c5300f8ad496c3029e4e0f565c7e60d42f7ea7a1a69d2cf1e8414e2771" +dependencies = [ + "getrandom", + "js-sys", + "leaflet", + "leptos", + "leptos_meta", + "paste", + "rand", + "serde-wasm-bindgen", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_axum" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0572d6549c7b4d59d6c6c8240c0faa25c1d20af144db7b2e372ffc537fd0aaa3" +dependencies = [ + "axum", + "cfg-if", + "futures", + "http-body-util", + "leptos", + "leptos_integration_utils", + "leptos_macro", + "leptos_meta", + "leptos_router", + "once_cell", + "parking_lot", + "serde_json", + "server_fn", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "leptos_config" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc190458a62433fb1d31f65f1afe7f1044650c667cdf9166956b48907ac821bd" +dependencies = [ + "config", + "regex", + "serde", + "thiserror", + "typed-builder", +] + +[[package]] +name = "leptos_dom" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ffd84c459d7c517fb8c9bfff704e819f3e6a2f5baf574d8636cb23840323d0a" +dependencies = [ + "async-recursion", + "cfg-if", + "drain_filter_polyfill", + "futures", + "getrandom", + "html-escape", + "indexmap", + "itertools", + "js-sys", + "leptos_reactive", + "once_cell", + "pad-adapter", + "paste", + "rustc-hash", + "serde", + "serde_json", + "server_fn", + "smallvec", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "leptos_hot_reload" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2f157fc6d4a356e3dd2734c6a65cba9e5eadb7c7cfd979c0adb752d293dba1" +dependencies = [ + "anyhow", + "camino", + "indexmap", + "parking_lot", + "proc-macro2", + "quote", + "rstml", + "serde", + "syn 2.0.58", + "walkdir", +] + +[[package]] +name = "leptos_integration_utils" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046984e2ca5e2136eb1021ced1f1dc1725b4045f4a370d28f2594b23604477ac" +dependencies = [ + "futures", + "leptos", + "leptos_config", + "leptos_hot_reload", + "leptos_meta", + "tracing", +] + +[[package]] +name = "leptos_macro" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69a20596eb0afe56296d3187c8f46680bb8ea1df58b6566e2588c9c53c549581" +dependencies = [ + "attribute-derive", + "cfg-if", + "convert_case", + "html-escape", + "itertools", + "leptos_hot_reload", + "prettyplease", + "proc-macro-error", + "proc-macro2", + "quote", + "rstml", + "server_fn_macro", + "syn 2.0.58", + "tracing", + "uuid", +] + +[[package]] +name = "leptos_meta" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a85d2f3e3c4eb7ffb8a97abe6c79eefcb0a2c6ae5bacd7645b5a5df1ba0a212" +dependencies = [ + "cfg-if", + "indexmap", + "leptos", + "tracing", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "leptos_reactive" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9152b4b9af932896ae69d08b8ab7108513b9cdddf8e2922d5c234dab7ac9af" +dependencies = [ + "base64 0.22.0", + "cfg-if", + "futures", + "indexmap", + "js-sys", + "paste", + "pin-project", + "rustc-hash", + "self_cell", + "serde", + "serde-wasm-bindgen", + "serde_json", + "slotmap", + "thiserror", + "tokio", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "leptos_router" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e42ebcdc1663ab03f249b91c367daa00f164d84c5f30eaec492bd45dffd3148" +dependencies = [ + "cached", + "cfg-if", + "gloo-net", + "itertools", + "js-sys", + "lazy_static", + "leptos", + "leptos_integration_utils", + "leptos_meta", + "linear-map", + "lru", + "once_cell", + "percent-encoding", + "regex", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "thiserror", + "tracing", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "leptos_server" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bed1e82ded6bfbfd61ad2f77066a789b94dc889513a47223e3c6a3e8ca2b109f" +dependencies = [ + "inventory", + "lazy_static", + "leptos_macro", + "leptos_reactive", + "serde", + "server_fn", + "thiserror", + "tracing", +] + [[package]] name = "libc" version = "0.2.153" @@ -1047,6 +1462,16 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "linear-map" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" +dependencies = [ + "serde", + "serde_test", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -1070,18 +1495,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] -name = "loom" -version = "0.5.6" +name = "lru" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21" dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", + "hashbrown 0.14.3", +] + +[[package]] +name = "manyhow" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91ea592d76c0b6471965708ccff7e6a5d277f676b90ab31f4d3f3fc77fade64" +dependencies = [ + "manyhow-macros", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "manyhow-macros" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64621e2c08f2576e4194ea8be11daf24ac01249a4f53cd8befcbb7077120ead" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", ] [[package]] @@ -1091,13 +1533,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fc1b6107fbd06c96e5e481fcf3e6575b873eb84f5b68f1f5706cde0fed42c4" [[package]] -name = "matchers" -version = "0.1.0" +name = "matchit" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" @@ -1121,59 +1560,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "minify-html" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c467a6330fab16054a5676afe3efc354f11a0f81b2a7f59bc0af6a20feb076ba" -dependencies = [ - "aho-corasick 0.7.20", - "css-minify", - "lazy_static", - "memchr", - "minify-html-common", - "minify-js 0.5.6", - "rustc-hash", -] - -[[package]] -name = "minify-html-common" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0deb4840e5f4aee1ef0cd2e8131bdd475dd56e3649cacf41dd7e871557700c7f" -dependencies = [ - "aho-corasick 0.7.20", - "css-minify", - "itertools", - "lazy_static", - "memchr", - "minify-js 0.4.3", - "reqwest", - "rustc-hash", - "serde", - "serde_json", -] - -[[package]] -name = "minify-js" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c300f90ba1138b5c5daf5d9441dc9bdc67b808aac22cf638362a2647bc213be4" -dependencies = [ - "lazy_static", - "parse-js 0.10.3", -] - -[[package]] -name = "minify-js" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22d6c512a82abddbbc13b70609cb2beff01be2c7afff534d6e5e1c85e438fc8b" -dependencies = [ - "lazy_static", - "parse-js 0.17.0", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1202,21 +1588,19 @@ dependencies = [ [[package]] name = "multer" -version = "2.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +checksum = "a15d522be0a9c3e46fd2632e272d178f56387bdb5c9fbb3a36c649062e9b5219" dependencies = [ "bytes", "encoding_rs", "futures-util", - "http", + "http 1.1.0", "httparse", "log", "memchr", "mime", "spin", - "tokio", - "tokio-util", "version_check", ] @@ -1226,12 +1610,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" -[[package]] -name = "nanorand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" - [[package]] name = "native-tls" version = "0.2.11" @@ -1260,16 +1638,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - [[package]] name = "num" version = "0.2.1" @@ -1305,12 +1673,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" version = "0.1.46" @@ -1423,10 +1785,10 @@ dependencies = [ ] [[package]] -name = "overload" +name = "pad-adapter" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "56d80efc4b6721e8be2a10a5df21a30fa0b470f1539e53d8b4e6e75faf938b63" [[package]] name = "parking_lot" @@ -1452,51 +1814,16 @@ dependencies = [ ] [[package]] -name = "parse-js" -version = "0.10.3" +name = "paste" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30534759e6ad87aa144c396544747e1c25b1020bd133356fd758c8facec764e5" -dependencies = [ - "aho-corasick 0.7.20", - "lazy_static", - "memchr", -] +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] -name = "parse-js" -version = "0.17.0" +name = "pathdiff" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ec3b11d443640ec35165ee8f6f0559f1c6f41878d70330fe9187012b5935f02" -dependencies = [ - "aho-corasick 0.7.20", - "bumpalo", - "hashbrown 0.13.2", - "lazy_static", - "memchr", -] - -[[package]] -name = "pear" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.58", -] +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "percent-encoding" @@ -1504,6 +1831,26 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -1526,27 +1873,79 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" name = "plcom" version = "0.1.0" dependencies = [ - "askama", - "askama_rocket", + "axum", "chrono", - "minify-html", - "nanorand", - "rocket", - "rocket_async_compression", + "console_error_panic_hook", + "gen-wallpapers", + "getrandom", + "http 1.1.0", + "leptos", + "leptos-leaflet", + "leptos_axum", + "leptos_meta", + "leptos_router", + "rand", + "serde", + "serde_json", + "tailwind_fuse", + "thiserror", + "tokio", + "tower", + "tower-http", + "tracing", + "wasm-bindgen", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" +dependencies = [ + "proc-macro2", + "syn 2.0.58", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-utils" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f59e109e2f795a5070e69578c4dc101068139f74616778025ae1011d4cd41a8" +dependencies = [ + "proc-macro2", + "quote", + "smallvec", +] + [[package]] name = "proc-macro2" version = "1.0.79" @@ -1578,6 +1977,29 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "quote-use" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b393938dcaab992375d7b3df7887fa98cc91c2f3590598251e7c609e2b788139" +dependencies = [ + "quote", + "quote-use-macros", +] + +[[package]] +name = "quote-use-macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d8772387900c205780e2c240cfe4dd01355ab4f96a503d99bdf34ad73180ef" +dependencies = [ + "derive-where", + "proc-macro-utils", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "radium" version = "0.3.0" @@ -1623,45 +2045,16 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "ref-cast" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4846d4c50d1721b1a3bef8af76924eef20d5e723647333798c1b519b3a9473f" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.58", -] - [[package]] name = "regex" version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ - "aho-corasick 1.1.3", + "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -1670,17 +2063,11 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ - "aho-corasick 1.1.3", + "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.3" @@ -1693,15 +2080,15 @@ version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", "h2", - "http", - "http-body", - "hyper", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", "hyper-tls", "ipnet", "js-sys", @@ -1715,7 +2102,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -1728,97 +2115,17 @@ dependencies = [ ] [[package]] -name = "rocket" -version = "0.5.0" +name = "rstml" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7bb57ccb26670d73b6a47396c83139447b9e7878cab627fdfe9ea8da489150" +checksum = "fe542870b8f59dd45ad11d382e5339c9a1047cde059be136a7016095bbdefa77" dependencies = [ - "async-stream", - "async-trait", - "atomic 0.5.3", - "binascii", - "bytes", - "either", - "figment", - "futures", - "indexmap 2.2.6", - "log", - "memchr", - "multer", - "num_cpus", - "parking_lot", - "pin-project-lite", - "rand", - "ref-cast", - "rocket_codegen", - "rocket_http", - "serde", - "state", - "tempfile", - "time", - "tokio", - "tokio-stream", - "tokio-util", - "ubyte", - "version_check", - "yansi", -] - -[[package]] -name = "rocket_async_compression" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50253076f89fd47fa68b1d571f811f188ef90b606d82fa952326c34cd955669e" -dependencies = [ - "async-compression", - "futures", - "lazy_static", - "log", - "rocket", -] - -[[package]] -name = "rocket_codegen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2238066abf75f21be6cd7dc1a09d5414a671f4246e384e49fe3f8a4936bd04c" -dependencies = [ - "devise", - "glob", - "indexmap 2.2.6", "proc-macro2", + "proc-macro2-diagnostics", "quote", - "rocket_http", "syn 2.0.58", - "unicode-xid", - "version_check", -] - -[[package]] -name = "rocket_http" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a1663694d059fe5f943ea5481363e48050acedd241d46deb2e27f71110389e" -dependencies = [ - "cookie", - "either", - "futures", - "http", - "hyper", - "indexmap 2.2.6", - "log", - "memchr", - "pear", - "percent-encoding", - "pin-project-lite", - "ref-cast", - "serde", - "smallvec", - "stable-pattern", - "state", - "time", - "tokio", - "uncased", + "syn_derive", + "thiserror", ] [[package]] @@ -1844,15 +2151,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "0.38.32" @@ -1872,7 +2170,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64", + "base64 0.21.7", ] [[package]] @@ -1887,6 +2185,15 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.23" @@ -1896,12 +2203,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -1932,10 +2233,19 @@ dependencies = [ ] [[package]] -name = "semver" -version = "1.0.22" +name = "self_cell" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +dependencies = [ + "futures-core", +] [[package]] name = "serde" @@ -1946,6 +2256,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_derive" version = "1.0.197" @@ -1968,6 +2289,27 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_qs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + [[package]] name = "serde_spanned" version = "0.6.5" @@ -1977,6 +2319,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_test" +version = "1.0.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a2f49ace1498612d14f7e0b8245519584db8299541dfe31a06374a828d620ab" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1990,21 +2341,62 @@ dependencies = [ ] [[package]] -name = "sharded-slab" -version = "0.1.7" +name = "server_fn" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +checksum = "15a46a2ffdecb81430ecfb995989218a18b6e94c1ead50cb806b5927c986a8ce" dependencies = [ - "lazy_static", + "axum", + "bytes", + "ciborium", + "const_format", + "dashmap", + "futures", + "gloo-net", + "http 1.1.0", + "http-body-util", + "hyper 1.2.0", + "inventory", + "js-sys", + "once_cell", + "send_wrapper", + "serde", + "serde_json", + "serde_qs", + "server_fn_macro_default", + "thiserror", + "tower", + "tower-layer", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "xxhash-rust", ] [[package]] -name = "signal-hook-registry" -version = "1.4.1" +name = "server_fn_macro" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "324a248dedb786315ba738f3618dbd65ba0c1b22ebea76f15a3e96a04643a73f" dependencies = [ - "libc", + "const_format", + "convert_case", + "proc-macro2", + "quote", + "syn 2.0.58", + "xxhash-rust", +] + +[[package]] +name = "server_fn_macro_default" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af19028131998f73134a9adcdefb6d9de2eeaed5b01ef74f6d902a1659d2a32f" +dependencies = [ + "server_fn_macro", + "syn 2.0.58", ] [[package]] @@ -2016,6 +2408,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "serde", + "version_check", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -2039,22 +2441,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] -name = "stable-pattern" -version = "0.1.0" +name = "strsim" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" -dependencies = [ - "memchr", -] - -[[package]] -name = "state" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" -dependencies = [ - "loom", -] +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strsim" @@ -2084,12 +2474,30 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "sync_wrapper" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384595c11a4e2969895cad5a8c4029115f5ab956a9e5ef4de79d11a426e5f20c" + [[package]] name = "system-configuration" version = "0.5.1" @@ -2111,6 +2519,28 @@ dependencies = [ "libc", ] +[[package]] +name = "tailwind_fuse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540816d3bd4e690d75d0cfcad47c8129487f0f730fc32ffa913aeef0b7634a8" +dependencies = [ + "nom", + "tailwind_fuse_macro", +] + +[[package]] +name = "tailwind_fuse_macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473b0a76a09cf00567794d9aa58fafa568ddb66053cf75ae776f7034584c2e46" +dependencies = [ + "darling 0.20.8", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "tempfile" version = "3.10.1" @@ -2143,47 +2573,6 @@ dependencies = [ "syn 2.0.58", ] -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -2211,7 +2600,6 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.48.0", @@ -2238,17 +2626,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-stream" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.10" @@ -2258,6 +2635,8 @@ dependencies = [ "bytes", "futures-core", "futures-sink", + "futures-util", + "hashbrown 0.14.3", "pin-project-lite", "tokio", "tracing", @@ -2290,13 +2669,60 @@ version = "0.22.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" dependencies = [ - "indexmap 2.2.6", + "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags 2.5.0", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -2309,6 +2735,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2332,36 +2759,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", ] [[package]] @@ -2371,22 +2768,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "ubyte" -version = "0.10.4" +name = "typed-builder" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" +checksum = "444d8748011b93cb168770e8092458cb0f8854f931ff82fdf6ddfbd72a9c933e" dependencies = [ - "serde", + "typed-builder-macro", ] [[package]] -name = "uncased" -version = "0.9.10" +name = "typed-builder-macro" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +checksum = "563b3b88238ec95680aef36bdece66896eaa7ce3c0f1b4f39d38fb2435261352" dependencies = [ - "serde", - "version_check", + "proc-macro2", + "quote", + "syn 2.0.58", ] [[package]] @@ -2419,6 +2817,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-xid" version = "0.2.4" @@ -2436,6 +2840,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + [[package]] name = "utf8parse" version = "0.2.1" @@ -2443,10 +2853,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] -name = "valuable" -version = "0.1.0" +name = "uuid" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom", +] [[package]] name = "vcpkg" @@ -2460,6 +2873,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2541,6 +2964,19 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.69" @@ -2567,21 +3003,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-core" version = "0.52.0" @@ -2742,14 +3178,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + [[package]] name = "yansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" -dependencies = [ - "is-terminal", -] [[package]] name = "zerocopy" diff --git a/Cargo.toml b/Cargo.toml index 5b3fc8c..2cfd947 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,26 +1,15 @@ [workspace] -members = ["crates/gen-wallpapers"] +resolver = "2" +members = ["crates/gen-wallpapers", "crates/plcom"] -[package] -name = "plcom" -version = "0.1.0" -edition = "2021" -publish = false -default-run = "plcom" +[workspace.dependencies] +serde = "1.0" -[[bin]] -name = "plcom" -path = "src/main.rs" +# Defines a size-optimized profile for the WASM bundle in release mode +[profile.wasm-release] +inherits = "release" +opt-level = 'z' +lto = true +codegen-units = 1 +panic = "abort" -[[bin]] -name = "gen-wallpapers" -path = "src/gen-wallpapers.rs" - -[dependencies] -rocket = "0.5" -rocket_async_compression = "0.5" -askama = { version = "0.12.1", features = ["with-rocket"] } -askama_rocket = "0.12.0" -chrono = "0.4.31" -minify-html = "0.11.1" -nanorand = { version = "0.7.0", features = ["chacha"] } diff --git a/build.rs b/build.rs deleted file mode 100644 index 46f4344..0000000 --- a/build.rs +++ /dev/null @@ -1,21 +0,0 @@ -fn main() -> Result<(), Box> { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=tailwind.config.cjs"); - println!("cargo:rerun-if-changed=templates/"); - - let result = std::process::Command::new("tailwindcss") - .arg("-i") - .arg("./css/tailwind.css") - .arg("-o") - .arg("./public/style.css") - .output() - .unwrap(); - - println!("{:?}", result); - - if !result.status.success() { - panic!("Failed to run tailwindcss") - } - - Ok(()) -} diff --git a/crates/gen-wallpapers/examples/cli.rs b/crates/gen-wallpapers/examples/cli.rs index 2e9fa7c..45ddf0d 100644 --- a/crates/gen-wallpapers/examples/cli.rs +++ b/crates/gen-wallpapers/examples/cli.rs @@ -20,7 +20,7 @@ fn main() { let metadata = MetadataList::process_folder(dir, true); - let json = match metadata.to_json() { + let json = match metadata.to_pretty_json() { Ok(json) => json, Err(e) => { eprintln!("failed to serialize json: {e}"); diff --git a/crates/gen-wallpapers/src/lib.rs b/crates/gen-wallpapers/src/lib.rs index 9a7e2de..8446cd8 100644 --- a/crates/gen-wallpapers/src/lib.rs +++ b/crates/gen-wallpapers/src/lib.rs @@ -165,8 +165,8 @@ impl MetadataList { Self(files) } - pub fn to_json(&self) -> serde_json::Result { - serde_json::to_string(&self.0) + pub fn to_pretty_json(&self) -> serde_json::Result { + serde_json::to_string_pretty(&self.0) } } diff --git a/crates/plcom/.gitignore b/crates/plcom/.gitignore new file mode 100644 index 0000000..cc4c514 --- /dev/null +++ b/crates/plcom/.gitignore @@ -0,0 +1 @@ +css/tailwind-output.css diff --git a/crates/plcom/Cargo.toml b/crates/plcom/Cargo.toml new file mode 100644 index 0000000..70f4f6c --- /dev/null +++ b/crates/plcom/Cargo.toml @@ -0,0 +1,118 @@ +[package] +name = "plcom" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +# leptos + axum +axum = { version = "0.7", optional = true } +console_error_panic_hook = "0.1" +leptos = { version = "0.6" } +leptos_axum = { version = "0.6", optional = true } +leptos_meta = { version = "0.6" } +leptos_router = { version = "0.6" } +tokio = { version = "1", features = ["rt-multi-thread"], optional = true } +tower = { version = "0.4", optional = true } +tower-http = { version = "0.5", features = ["fs"], optional = true } +wasm-bindgen = "=0.2.92" +thiserror = "1" +tracing = { version = "0.1", optional = true } +http = "1" + +# external crates +tailwind_fuse = { version = "0.2.0", features = ["variant"] } +chrono = "0.4.37" +getrandom = { version = "0.2", features = ["js"] } +rand = "0.8.5" +leptos-leaflet = "0.8.0" + +[build-dependencies] +serde = { workspace = true } +serde_json = "1.0" +gen-wallpapers = { path = "../../crates/gen-wallpapers" } + +[features] +hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate", "leptos-leaflet/hydrate"] +ssr = [ + "dep:axum", + "dep:tokio", + "dep:tower", + "dep:tower-http", + "dep:leptos_axum", + "leptos/ssr", + "leptos_meta/ssr", + "leptos_router/ssr", + "dep:tracing", + "leptos-leaflet/ssr" +] + +[package.metadata.leptos] +# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name +output-name = "plcom" + +# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup. +site-root = "target/site" + +# The site-root relative folder where all compiled output (JS, WASM and CSS) is written +# Defaults to pkg +site-pkg-dir = "pkg" + +# The tailwind input file. +# +# Optional, Activates the tailwind build +tailwind-input-file = "css/main.css" + +# The tailwind config file. +# +# Optional, defaults to "tailwind.config.js" which if is not present +# is generated for you +tailwind-config-file = "tailwind.config.js" + +# Assets source dir. All files found here will be copied and synchronized to site-root. +# The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir. +# +# Optional. Env: LEPTOS_ASSETS_DIR. +assets-dir = "../../public" + +# Additional files triggering recompilation +watch-additional-files = ["resume.json", "wallpapers.json"] + +# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup. +site-addr = "0.0.0.0:3000" + +# The port to use for automatic reload monitoring +reload-port = 3001 + +# The browserlist query used for optimizing the CSS. +browserquery = "defaults" + +# The environment Leptos will run in, usually either "DEV" or "PROD" +env = "DEV" + +# The features to use when compiling the bin target +# +# Optional. Can be over-ridden with the command line parameter --bin-features +bin-features = ["ssr"] + +# If the --no-default-features flag should be used when compiling the bin target +# +# Optional. Defaults to false. +bin-default-features = false + +# The features to use when compiling the lib target +# +# Optional. Can be over-ridden with the command line parameter --lib-features +lib-features = ["hydrate"] + +# If the --no-default-features flag should be used when compiling the lib target +# +# Optional. Defaults to false. +lib-default-features = false + +# The profile to use for the lib target when compiling for release +# +# Optional. Defaults to "release". +lib-profile-release = "wasm-release" diff --git a/crates/plcom/build.rs b/crates/plcom/build.rs new file mode 100644 index 0000000..cf92435 --- /dev/null +++ b/crates/plcom/build.rs @@ -0,0 +1,365 @@ +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Work { + name: String, + position: String, + start_date: String, + end_date: Option, + logo: Logo, + description: String, + highlights: Vec, + technologies: Vec, + link: Option, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Project { + name: String, + description: String, + start_date: String, + end_date: Option, + presentation: Vec, + highlights: Vec, + keywords: Vec, + link: Option, + logo: Option, + image: Option, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Image { + file: String, + // TODO: use enum directly instead of string + // position: ImagePosition, + position: String, +} + +#[derive(Debug, Deserialize)] +// TODO: possible implementation: https://www.reddit.com/r/rust/comments/10bab4v/serdejson_how_to_deserialize_and_serialize_an/ +enum ImagePosition { + Left, + Right, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Link { + uri: String, + label: String, + not_available: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Logo { + file: String, + transparent_background: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Education { + institution: String, + study_type: String, + area: String, + start_date: String, + end_date: Option, + logo: Option, + courses: Vec, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Wallpaper { + filename: String, + date: String, + gps: Gps, + location: Location, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Gps { + latitude: f32, + longitude: f32, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Location { + precise: String, + broad: String, +} + +fn vec_strings(vec: &Vec) -> String { + let mut string = String::new(); + string.push('['); + for el in vec { + string.push_str(&format!("{:?},", el)); + } + string.push(']'); + string +} + +fn resume(dest: &std::path::Path, source: std::fs::File) { + let reader = std::io::BufReader::new(source); + let data: serde_json::Value = serde_json::from_reader(reader).expect("Failed to parse JSON"); + let mut final_ser = String::new(); + + // Work + let work_json = &data["work"]; + let work_data: Vec = + serde_json::from_value(work_json.clone()).expect("Failed to parse work"); + let mut work_str = String::new(); + work_str.push('['); + for work in &work_data { + work_str.push_str("Work {"); + work_str.push_str(&format!("name: {:?},", work.name)); + work_str.push_str(&format!("position: {:?},", work.position)); + work_str.push_str(&format!("start_date: {:?},", work.start_date)); + + work_str.push_str(&format!( + "end_date: {},", + match &work.end_date { + Some(end) => format!("Some({:?})", end), + None => "None".into(), + } + )); + + // Optional struct + // TODO: refacto + match &work.link { + // TODO: enum with 2 variants for unavailable link + Some(link) => match link.not_available { + true => work_str.push_str(&format!( + "link: Some(ResumeLink {{ uri: {:?}, label: \"Not available\", not_available: true }}),", + link.uri + )), + false => work_str.push_str(&format!( + "link: Some(ResumeLink {{ uri: {:?}, label: {:?}, not_available: false }}),", + link.uri, link.label + )), + }, + None => work_str.push_str("link: None,"), + } + + // Struct + work_str.push_str(&format!( + "logo: Logo {{ file: {:?}, transparent_background: {} }},", + work.logo.file, work.logo.transparent_background + )); + + work_str.push_str(&format!("description: {:?},", work.description)); + + // Vector + let mut highlights = String::new(); + highlights.push('['); + for high in &work.highlights { + highlights.push_str(&format!("{:?},", high)); + } + highlights.push(']'); + work_str.push_str(&format!("highlights: &{},", highlights)); + + // Vector + let mut technologies = String::new(); + technologies.push('['); + for tech in &work.technologies { + technologies.push_str(&format!("{:?},", tech)); + } + technologies.push(']'); + work_str.push_str(&format!("technologies: &{},", technologies)); + + work_str.push_str("},\n"); + } + work_str.push(']'); + let work_ser = format!( + "pub const WORK: [Work; {}] = \n{};\n", + work_data.len(), + work_str + ); + final_ser.push_str(&work_ser); + + // Projects + let projects_json = &data["projects"]; + let projects_data: Vec = + serde_json::from_value(projects_json.clone()).expect("Failed to parse projects"); + let mut projects_str = String::new(); + projects_str.push('['); + for project in &projects_data { + projects_str.push_str("Project {"); + projects_str.push_str(&format!("name: {:?},", project.name)); + projects_str.push_str(&format!("description: {:?},", project.description)); + projects_str.push_str(&format!("start_date: {:?},", project.start_date)); + + projects_str.push_str(&format!( + "end_date: {},", + match &project.end_date { + Some(end) => format!("Some({:?})", end), + None => "None".into(), + } + )); + + // Optional struct + match &project.link { + // TODO: enum with 2 variants for unavailable link + Some(link) => match link.not_available { + true => projects_str.push_str(&format!( + "link: Some(ResumeLink {{ uri: {:?}, label: \"Not available\", not_available: true }}),", + link.uri + )), + false => projects_str.push_str(&format!( + "link: Some(ResumeLink {{ uri: {:?}, label: {:?}, not_available: false }}),", + link.uri, link.label + )), + }, + None => projects_str.push_str("link: None,"), + } + + // Vector + projects_str.push_str(&format!( + "presentation: &{},", + vec_strings(&project.presentation) + )); + + // Vector + projects_str.push_str(&format!( + "highlights : &{},", + vec_strings(&project.highlights) + )); + + // Vector + projects_str.push_str(&format!("keywords: &{},", vec_strings(&project.keywords))); + + // Optional struct + match &project.logo { + Some(logo) => projects_str.push_str(&format!( + "logo: Some(Logo {{ file: {:?}, transparent_background: {} }}),", + logo.file, logo.transparent_background + )), + None => projects_str.push_str("logo: None,"), + } + + // Optional struct + match &project.image { + Some(image) => projects_str.push_str(&format!( + "image: Some(Image {{ file: {:?}, position: {:?} }}),", + image.file, image.position + )), + None => projects_str.push_str("image: None,"), + } + + projects_str.push_str("},\n"); + } + projects_str.push(']'); + let projects_ser = format!( + "pub const PROJECTS: [Project; {}] = \n{};\n", + projects_data.len(), + projects_str + ); + final_ser.push_str(&projects_ser); + + // Education + let education_json = &data["education"]; + let education_data: Vec = + serde_json::from_value(education_json.clone()).expect("Failed to parse education"); + let mut education_str = String::new(); + education_str.push('['); + for education in &education_data { + education_str.push_str("Education {"); + education_str.push_str(&format!("institution: {:?},", education.institution)); + education_str.push_str(&format!("area: {:?},", education.area)); + education_str.push_str(&format!("study_type: {:?},", education.study_type)); + education_str.push_str(&format!("start_date: {:?},", education.start_date)); + education_str.push_str(&format!( + "end_date: {},", + match &education.end_date { + Some(end) => format!("Some({:?})", end), + None => "None".into(), + } + )); + + // Optional struct + match &education.logo { + Some(logo) => education_str.push_str(&format!( + "logo: Some(Logo {{ file: {:?}, transparent_background: {} }}),", + logo.file, logo.transparent_background + )), + None => education_str.push_str("logo: None,"), + } + + // Vector + let mut courses = String::new(); + courses.push('['); + for course in &education.courses { + courses.push_str(&format!("{:?},", course)); + } + courses.push(']'); + education_str.push_str(&format!("courses: &{},", courses)); + education_str.push_str("},\n"); + } + education_str.push(']'); + let education_ser = format!( + "pub const EDUCATION: [Education; {}] = \n{};\n", + education_data.len(), + education_str + ); + final_ser.push_str(&education_ser); + + std::fs::write(dest, final_ser).unwrap(); +} + +fn wallpapers(dest: &std::path::Path, source: std::fs::File) { + let reader = std::io::BufReader::new(source); + let wallpapers_data: Vec = + serde_json::from_reader(reader).expect("Failed to parse JSON"); + let mut final_ser = String::new(); + + let mut wallpapers_str = String::new(); + wallpapers_str.push('['); + + for wallpaper in &wallpapers_data { + wallpapers_str.push_str("Wallpaper {"); + wallpapers_str.push_str(&format!("filename: {:?},", wallpaper.filename)); + wallpapers_str.push_str(&format!("date: {:?},", wallpaper.date)); + wallpapers_str.push_str(&format!( + "gps: Gps {{ latitude: {}, longitude: {} }},", + wallpaper.gps.latitude, wallpaper.gps.longitude + )); + wallpapers_str.push_str(&format!( + "location: Location {{ precise: {:?}, broad: {:?} }},", + wallpaper.location.precise, wallpaper.location.broad + )); + wallpapers_str.push_str("},\n"); + } + + wallpapers_str.push(']'); + let wallpapers_ser = format!( + "pub const WALLPAPERS: [Wallpaper; {}] = \n{};\n", + wallpapers_data.len(), + wallpapers_str + ); + final_ser.push_str(&wallpapers_ser); + + std::fs::write(dest, final_ser).unwrap(); +} + +fn main() { + println!("cargo::rerun-if-changed=resume.json"); + println!("cargo::rerun-if-changed=wallpapers.json"); + + let out_dir = std::env::var_os("OUT_DIR").unwrap(); + + let resume_dest = std::path::Path::new(&out_dir).join("resume.rs"); + let resume_source = std::fs::File::open("resume.json").expect("Failed to open file"); + resume(&resume_dest, resume_source); + + let wallpapers_dest = std::path::Path::new(&out_dir).join("wallpapers.rs"); + match std::fs::File::open("wallpapers.json") { + Ok(source) => wallpapers(&wallpapers_dest, source), + Err(_) => println!("cargo::warning=skipping wallpapers, file not found"), + } +} diff --git a/crates/plcom/css/main.css b/crates/plcom/css/main.css new file mode 100644 index 0000000..2dcc3d7 --- /dev/null +++ b/crates/plcom/css/main.css @@ -0,0 +1,24 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* Fixed background when scrolling in tailwind + * Disabled on iOS devices + + * https://tailwindcss.com/docs/background-attachment + * https://stackoverflow.com/a/60220757/4809297 + */ + +@supports (-webkit-touch-callout: none) { + /* CSS specific to iOS devices */ + #wallpaper { + background-attachment: scroll; + } +} + +@supports not (-webkit-touch-callout: none) { + /* CSS for other than iOS devices */ + #wallpaper { + background-attachment: fixed; + } +} diff --git a/crates/plcom/resume.json b/crates/plcom/resume.json new file mode 100644 index 0000000..e6ca3bb --- /dev/null +++ b/crates/plcom/resume.json @@ -0,0 +1,428 @@ +{ + "$schema": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json", + "basics": { + "name": "Philippe Loctaux", + "url": "https://philippeloctaux.com", + "label": "Developer of all sorts", + "email": "www@philippeloctaux.com", + "location": { + "countryCode": "FR", + "city": "Rennes" + } + }, + "languages": [ + { + "language": "French" + }, + { + "language": "English" + }, + { + "language": "Russian" + } + ], + "education": [ + { + "institution": "Epitech", + "studyType": "Master", + "area": "Computer software engineering", + "startDate": "2018-10", + "endDate": "2023-08", + "logo": { + "file": "/icons/epitech.png", + "transparentBackground": true + }, + "courses": [ + "Many small projects in C to learn a technical concept during first year", + "my_teams: Microsoft Teams clone in C (protocol design, networking, server, client)", + "Arcade: Small gaming platform capable of loading dynamic libraries with a high abstraction level in C++", + "Epicture: Android client in Kotlin of the Imgur image service (OAuth2, listing, viewing, uploading images)" + ] + }, + { + "institution": "UQAC", + "studyType": "Year abroad", + "area": "Computer software engineering", + "startDate": "2021-09", + "endDate": "2022-05", + "logo": { + "file": "/icons/uqac.png", + "transparentBackground": true + }, + "courses": [ + "Android app development", + "Object Oriented Programming", + "Software engineering", + "Entreprise application development" + ] + } + ], + "work": [ + { + "name": "Rubycat", + "position": "Software engineer", + "startDate": "2023-06", + "logo": { + "file": "/icons/rubycat.png", + "transparentBackground": true + }, + "description": "Maintenance and improvement of PROVE IT, a Privileged Access Management software platform.", + "highlights": [ + "Rewrote an internal component in Rust, making it faster and simpler to use for the development team", + "Bug investigation and fixes", + "Public speaking about the product" + ], + "technologies": [ + "Rust", + "Angular", + "Python", + "PostgreSQL", + "GitLab" + ], + "link": { + "uri": "https://rubycat.eu", + "label": "Company website", + "notAvailable": false + } + }, + { + "name": "Acklio", + "position": "Rust developer", + "startDate": "2023-03", + "endDate": "2023-05", + "logo": { + "file": "/icons/acklio.png", + "transparentBackground": true + }, + "description": "The first usage of the SCHC framework (RFC 8724) on Rust!", + "highlights": [ + "Creation of Rust bindings of a C library implementing the SCHC framework", + "Demonstration of SCHC with applications in Rust on x86 platform", + "Proof of concept usage of embedded STM32 controllers exclusively in Rust", + "Transmission of knowledge to the technical team" + ], + "technologies": [ + "Rust", + "SCHC", + "STM32 controllers", + "LoRa", + "LoRaWAN" + ], + "link": { + "uri": "https://ackl.io", + "label": "Company website", + "notAvailable": false + } + }, + { + "name": "Vélorail du Kreiz Breizh", + "position": "Freelance developer", + "startDate": "2021-08", + "endDate": "2022-04", + "logo": { + "file": "/icons/velorail.png", + "transparentBackground": true + }, + "description": "Creation of an online booking platform focused on the tourist activity of rail biking (vélorail).", + "highlights": [ + "Design, UX, booking and payment flow for customers", + "Dashboard for managers with calendar view, manual bookings, slots management", + "Ability to generate invoices, booking recaps for managers", + "Sending emails to customers and managers about bookings", + "Online deployment, maintenance of the service", + "5 months after the initial deployment, 43% of the bookings were made with the online platform", + "Focus to use the least amount of external services, resulting in implementation of a invoice generation service, an image-based captcha, and a templating system for transactional emails" + ], + "technologies": [ + "Angular", + "NestJS", + "GraphQL", + "Rust", + "Stripe", + "Amazon SES", + "GitLab CI/CD" + ], + "link": { + "uri": "https://resa.velorail.bzh", + "label": "Booking platform", + "notAvailable": true + } + }, + { + "name": "Yaakadev", + "position": "Full-Stack developer", + "startDate": "2021-04", + "endDate": "2021-07", + "logo": { + "file": "/icons/yaakadev.png", + "transparentBackground": false + }, + "description": "Design, development, deployment and maintenance of many projects for various clients.", + "highlights": [ + "Admin dashboard of a local merchants solution", + "Calendar planning application with filtering and custom views for a SaaS for the agency", + "Intranet to upload and download documents for a client" + ], + "technologies": [ + "NodeJS", + "ExpressJS", + "Angular", + "MongoDB", + "CI/CD" + ], + "link": { + "uri": "https://yaakadev.com", + "label": "Agency website", + "notAvailable": false + } + }, + { + "name": "Epitech", + "position": "Teaching assistant", + "startDate": "2020-02", + "endDate": "2021-03", + "logo": { + "file": "/icons/epitech.png", + "transparentBackground": true + }, + "description": "Pedagogical supervision of three classes of students, conducting educational activites throughout the school year.", + "highlights": [ + "Start of projects", + "Technical help and guidance", + "Proctoring exams", + "Grading students on their work" + ], + "technologies": [ + "C", + "C++", + "Haskell", + "Rust", + "Web and mobile development" + ], + "link": { + "uri": "https://www.epitech.eu/ecole-informatique-rennes", + "label": "School", + "notAvailable": false + } + }, + { + "name": "Ubiscale", + "position": "Embedded developer", + "startDate": "2019-08", + "endDate": "2019-12", + "logo": { + "file": "/icons/ubiscale.png", + "transparentBackground": true + }, + "description": "Creation of a home Wifi gateway for a commercial IoT object.", + "highlights": [ + "Research, reverse engineering of existing products", + "Design and implementation" + ], + "technologies": [ + "C on a ESP8266 controller", + "Wi-Fi", + "Bluetooth" + ], + "link": { + "uri": "https://ubiscale.com", + "label": "Company website", + "notAvailable": false + } + } + ], + "projects": [ + { + "name": "ezidam", + "description": "Identity and Access Management system", + "presentation": [ + "A simple identity and access management system for SMEs or personal use.", + "Low maintenance required, easy to deploy and to backup." + ], + "highlights": [ + "Users management", + "Roles management", + "Assign users to roles and the other way around", + "OAuth2 / OIDC applications (code flow)", + "Multi-Factor Authentication (TOTP)", + "Password reset (via email or backup token)", + "Simple administration panel", + "Good security measures for users and administrators" + ], + "keywords": [ + "Rust", + "SQLite", + "OAuth2 / OIDC", + "TOTP", + "SMTP", + "Docker" + ], + "startDate": "2023-01", + "endDate": "2023-07", + "logo": { + "file": "/icons/ezidam.png", + "transparentBackground": true + }, + "link": null + }, + { + "name": "pass4thewin", + "description": "Password manager", + "startDate": "2020-11", + "endDate": "2021-01", + "presentation": [ + "Port of passwordstore, the standard unix password manager on the Windows platform.", + "Warning! Unfinished command line application, may cause data corruption when using existing passwords." + ], + "highlights": [ + "Creation of a store", + "List secrets", + "Decrypt secret", + "Insert or generate secrets", + "Edit existing secrets", + "Synchronisation with git", + "TOTP support" + ], + "keywords": [ + "Windows", + "Rust", + "OpenPGP", + "libgit2" + ], + "link": { + "uri": "https://github.com/x4m3/pass4thewin", + "label": "Source code", + "notAvailable": false + } + }, + { + "name": "NaviaRent", + "description": "Epitech Innovative Project", + "startDate": "2020-09", + "endDate": "2023-01", + "presentation": [ + "A B2B platform helping rentals of standup paddle boards." + ], + "highlights": [ + "DevOps of all software in the NaviaRent stack", + "Creation of the iOS application", + "Contributions to the Android application", + "Contributions to the backend server", + "Creation and contributions to the web client", + "Server administration, backups", + "Meetings managements spread across 3 timezones", + "Technical writing", + "Public presentations" + ], + "keywords": [ + "NodeJS", + "Angular", + "Kotlin", + "SwiftUI", + "Docker", + "GitLab CI/CD", + "Raspberry Pi", + "ESP32" + ], + "logo": { + "file": "/icons/naviarent.png", + "transparentBackground": false + }, + "image": { + "file": "/images/naviarent.jpg", + "position": "right" + }, + "link": { + "uri": "https://naviarent.fr", + "label": "Website", + "notAvailable": true + } + }, + { + "name": "epitok", + "description": "Presence system at Epitech", + "startDate": "2020-06", + "endDate": "2020-09", + "presentation": [ + "A library and web client to simplify students presence at Epitech.", + "Students are handed a piece of paper with a 6 digits number (called a \"token\") to verify their presence at school events.", + "Teachers use epitok to scan student cards with QR codes on them instead of printing and handing tokens to students." + ], + "highlights": [ + "Reverse engineering of a partially documented web API", + "Design, conception", + "User experience", + "Improvements based of usage of the application" + ], + "keywords": [ + "Rust", + "HTML", + "Bootstrap", + "jQuery", + "Docker" + ], + "link": { + "uri": "https://github.com/x4m3/epitok", + "label": "Source code", + "notAvailable": false + } + }, + { + "name": "epi.today", + "description": "Calendar for Epitech", + "startDate": "2019-11", + "endDate": "2019-02", + "presentation": [ + "A viewer of the Epitech intranet calendar.", + "Students and teachers glance at their planning without the need to go on the school's intranet." + ], + "highlights": [ + "Reverse engineering of a web api", + "Design a web page", + "Mobile UX", + "Deployment on a server" + ], + "keywords": [ + "TypeScript", + "HTML", + "Bootstrap", + "Docker" + ], + "link": { + "uri": "https://github.com/x4m3/epi.today", + "label": "Source code", + "notAvailable": false + } + }, + { + "name": "canvas.place", + "description": "Timelapse", + "startDate": "2017-04", + "endDate": "2020-01", + "presentation": [ + "canvas.place is a shared place to express creativity.", + "People from all over the world share one single canvas to paint on.", + "I created and maintained a timelapse of the virtual canvas." + ], + "highlights": [], + "keywords": [ + "FFmpeg", + "Shell scripting", + "nginx" + ], + "logo": { + "file": "/icons/canvas.png", + "transparentBackground": false + }, + "image": { + "file": "/images/canvas.png", + "position": "left" + }, + "link": { + "uri": "https://timelapse.canvas.place", + "label": "Website", + "notAvailable": false + } + } + ] +} diff --git a/crates/plcom/src/app.rs b/crates/plcom/src/app.rs new file mode 100644 index 0000000..9adf2f4 --- /dev/null +++ b/crates/plcom/src/app.rs @@ -0,0 +1,71 @@ +use crate::{ + error_template::{AppError, ErrorTemplate}, + Link, UnderlineLink,pages::* +}; +use leptos::*; +use leptos_meta::*; +use leptos_router::*; +use tailwind_fuse::*; + +#[component] +pub fn App() -> impl IntoView { + // Provides context that manages stylesheets, titles, meta tags, etc. + provide_meta_context(); + let formatter = |text| format!("{text} — Philippe Loctaux"); + + view! { + + + // sets the document title + + + <Meta name="viewport" content="width=device-width"/> + + // favicon + <Link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"/> + <Link rel="icon" type_="image/png" sizes="32x32" href="/favicon-32x32.png"/> + <Link rel="icon" type_="image/png" sizes="16x16" href="/favicon-16x16.png"/> + <Link rel="manifest" href="/site.webmanifest"/> + <Link + rel="mask-icon" + href="/safari-pinned-tab.svg" + attrs=vec![("color", Attribute::String(Oco::Borrowed("#0c4a6e")))] + /> + <Meta name="msapplication-TileColor" content="#0c4a6e"/> + <Meta name="theme-color" content="#0c4a6e"/> + + // stats + <Script + defer="true" + src="https://plausible.y.z.x4m3.rocks/js/script.js" + attrs=vec![("data-domain", Attribute::String(Oco::Borrowed("philippeloctaux.com")))] + /> + + // actual routes + <Router fallback=|| { + let mut outside_errors = Errors::default(); + outside_errors.insert_with_default_key(AppError::NotFound); + view! { <ErrorTemplate outside_errors/> }.into_view() + }> + <Body class=tw_join!("flex", "flex-col", "min-h-screen", "bg-gray-900", "text-white")/> + <main class=tw_join!("flex-grow")> + <Routes> + <Route path="" view=RootPage ssr=SsrMode::Async/> + <Route path="email" view=EmailPage/> + <Route path="wallpapers" view=WallpapersPage/> + </Routes> + </main> + <footer class=tw_join!("bg-black")> + <div class=tw_join!("container", "mx-auto", "px-4", "py-8")> + <p> + "© 2015 - "{crate::get_year()}" Philippe Loctaux, made with " + <UnderlineLink link=Link::new("https://leptos.dev", "Leptos")/> + </p> + </div> + </footer> + </Router> + } +} + + + diff --git a/crates/plcom/src/common.rs b/crates/plcom/src/common.rs new file mode 100644 index 0000000..b286fbb --- /dev/null +++ b/crates/plcom/src/common.rs @@ -0,0 +1,137 @@ +pub mod icon; +pub mod link; + +pub fn get_year() -> i32 { + use chrono::Datelike; + chrono::Utc::now().year() +} + +#[derive(Clone, PartialEq)] +pub struct Date { + pub year: u32, + pub month: u8, +} + +impl std::fmt::Display for Date { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}-{:02}", self.year, self.month) + } +} + +pub mod resume { + #[derive(Clone, Copy)] + pub struct Work { + pub name: &'static str, + pub position: &'static str, + pub start_date: &'static str, + pub end_date: Option<&'static str>, + pub logo: Logo, + pub description: &'static str, + pub highlights: &'static [&'static str], + pub technologies: &'static [&'static str], + pub link: Option<ResumeLink>, + } + + #[derive(Clone, Copy)] + pub struct Logo { + pub file: &'static str, + pub transparent_background: bool, + } + + #[derive(Clone, Copy)] + pub struct Project { + pub name: &'static str, + pub description: &'static str, + pub start_date: &'static str, + pub end_date: Option<&'static str>, + pub presentation: &'static [&'static str], + pub highlights: &'static [&'static str], + pub keywords: &'static [&'static str], + pub link: Option<ResumeLink>, + pub logo: Option<Logo>, + pub image: Option<Image>, + } + + #[derive(Clone, Copy)] + pub struct Image { + pub file: &'static str, + pub position: &'static str, + } + + #[derive(Clone, Copy)] + pub struct ResumeLink { + pub uri: &'static str, + pub label: &'static str, + pub not_available: bool, + } + + use crate::{Link, OutlineButtonLink}; + use http::Uri; + use leptos::*; + use tailwind_fuse::tw_join; + impl IntoView for ResumeLink { + fn into_view(self) -> View { + if !self.not_available { + let link = Link { + label: self.label.into(), + uri: Uri::from_static(self.uri), + }; + view! { <OutlineButtonLink link=link/> }.into_view() + } else { + view! { + <span class=tw_join!( + "mt-4", "cursor-not-allowed", "inline-flex", "max-w-fit", "bg-gray-400", + "text-gray-600", "font-semibold", "py-1.5", "px-4", "rounded-xl", + "items-center" + )>{self.label}</span> + } + .into_view() + } + } + } + + #[derive(Clone, Copy)] + pub struct Education { + pub institution: &'static str, + pub study_type: &'static str, + pub area: &'static str, + pub start_date: &'static str, + pub end_date: Option<&'static str>, + pub logo: Option<Logo>, + pub courses: &'static [&'static str], + } + + include!(concat!(env!("OUT_DIR"), "/resume.rs")); +} + +pub mod wallpapers { + + #[derive(Clone, Copy)] + pub struct Wallpaper { + pub filename: &'static str, + pub date: &'static str, + pub gps: Gps, + pub location: Location, + } + + #[derive(Clone, Copy)] + pub struct Gps { + pub latitude: f32, + pub longitude: f32, + } + + #[derive(Clone, Copy)] + pub struct Location { + pub precise: &'static str, + pub broad: &'static str, + } + + impl Wallpaper { + pub fn random() -> Option<&'static Wallpaper> { + let random_value = rand::Rng::gen_range(&mut rand::thread_rng(), 0..WALLPAPERS.len()); + WALLPAPERS.get(random_value) + } + } + + include!(concat!(env!("OUT_DIR"), "/wallpapers.rs")); +} diff --git a/crates/plcom/src/common/icon.rs b/crates/plcom/src/common/icon.rs new file mode 100644 index 0000000..58b52c3 --- /dev/null +++ b/crates/plcom/src/common/icon.rs @@ -0,0 +1,140 @@ +use leptos::*; +use tailwind_fuse::tw_join; + +#[derive(Clone, Copy, PartialEq)] +pub enum Icon { + Email, + Link, + Calendar, + Location, + Twitter, + Telegram, + Mastodon, + Github, + Linkedin, + Map, +} + +impl IntoView for Icon { + fn into_view(self) -> View { + match self { + Self::Email => view! { + <svg + class=tw_join!("w-6", "h-6", "mr-0", "sm:mr-2") + xmlns="http://www.w3.org/2000/svg" + fill="none" + viewBox="0 0 24 24" + stroke-width="1.5" + stroke="currentColor" + > + <path + stroke-linecap="round" + stroke-linejoin="round" + d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" + ></path> + </svg> + }.into_view(), + + Self::Link => view! { + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + class=tw_join!("w-5", "h-5", "fill-current", "mr-2") + > + <path d="M12.232 4.232a2.5 2.5 0 013.536 3.536l-1.225 1.224a.75.75 0 001.061 1.06l1.224-1.224a4 4 0 00-5.656-5.656l-3 3a4 4 0 00.225 5.865.75.75 0 00.977-1.138 2.5 2.5 0 01-.142-3.667l3-3z"></path> + <path d="M11.603 7.963a.75.75 0 00-.977 1.138 2.5 2.5 0 01.142 3.667l-3 3a2.5 2.5 0 01-3.536-3.536l1.225-1.224a.75.75 0 00-1.061-1.06l-1.224 1.224a4 4 0 105.656 5.656l3-3a4 4 0 00-.225-5.865z"></path> + </svg> + }.into_view(), + + Self::Calendar => view! { + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + class=tw_join!("w-5", "h-5") + > + <path + fill-rule="evenodd" + d="M5.75 2a.75.75 0 01.75.75V4h7V2.75a.75.75 0 011.5 0V4h.25A2.75 2.75 0 0118 6.75v8.5A2.75 2.75 0 0115.25 18H4.75A2.75 2.75 0 012 15.25v-8.5A2.75 2.75 0 014.75 4H5V2.75A.75.75 0 015.75 2zm-1 5.5c-.69 0-1.25.56-1.25 1.25v6.5c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25v-6.5c0-.69-.56-1.25-1.25-1.25H4.75z" + clip-rule="evenodd" + ></path> + </svg> + }.into_view(), + + Self::Location => view! { + <svg + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + class=tw_join!("w-5", "h-5") + > + <path + fill-rule="evenodd" + d="M9.69 18.933l.003.001C9.89 19.02 10 19 10 19s.11.02.308-.066l.002-.001.006-.003.018-.008a5.741 5.741 0 00.281-.14c.186-.096.446-.24.757-.433.62-.384 1.445-.966 2.274-1.765C15.302 14.988 17 12.493 17 9A7 7 0 103 9c0 3.492 1.698 5.988 3.355 7.584a13.731 13.731 0 002.273 1.765 11.842 11.842 0 00.976.544l.062.029.018.008.006.003zM10 11.25a2.25 2.25 0 100-4.5 2.25 2.25 0 000 4.5z" + clip-rule="evenodd" + ></path> + </svg> + }.into_view(), + Icon::Twitter => view! { + <svg + class=tw_join!("w-6", "h-6", "fill-current", "mr-0", "sm:mr-2") + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"></path> + </svg> + }.into_view(), + Icon::Telegram => view! { + <svg + class=tw_join!("w-6", "h-6", "fill-current", "mr-0", "sm:mr-2") + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"></path> + </svg> + }.into_view(), + Icon::Mastodon => view! { + <svg + class=tw_join!("w-6", "h-6", "fill-current", "mr-0", "sm:mr-2") + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"></path> + </svg> + }.into_view(), + Icon::Github => view! { + <svg + class=tw_join!("w-6", "h-6", "fill-current", "mr-0", "sm:mr-2") + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path> + </svg> + }.into_view(), + Icon::Linkedin => view! { + <svg + class=tw_join!("w-6", "h-6", "fill-current", "mr-0", "sm:mr-2") + viewBox="0 0 24 24" + xmlns="http://www.w3.org/2000/svg" + > + <path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"></path> + </svg> + }.into_view(), + Icon::Map => view! { + <svg + class=tw_join!("w-5", "h-5") + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + > + <path + fill-rule="evenodd" + d="M8.157 2.175a1.5 1.5 0 00-1.147 0l-4.084 1.69A1.5 1.5 0 002 5.251v10.877a1.5 1.5 0 002.074 1.386l3.51-1.453 4.26 1.763a1.5 1.5 0 001.146 0l4.083-1.69A1.5 1.5 0 0018 14.748V3.873a1.5 1.5 0 00-2.073-1.386l-3.51 1.452-4.26-1.763zM7.58 5a.75.75 0 01.75.75v6.5a.75.75 0 01-1.5 0v-6.5A.75.75 0 017.58 5zm5.59 2.75a.75.75 0 00-1.5 0v6.5a.75.75 0 001.5 0v-6.5z" + clip-rule="evenodd" + ></path> + </svg> + }.into_view(), + } + } +} diff --git a/crates/plcom/src/common/link.rs b/crates/plcom/src/common/link.rs new file mode 100644 index 0000000..a5ab605 --- /dev/null +++ b/crates/plcom/src/common/link.rs @@ -0,0 +1,99 @@ +use http::Uri; +use leptos::*; +use tailwind_fuse::tw_join; + +use crate::Icon; + +#[derive(Clone, PartialEq)] +pub struct Link { + pub label: String, + pub uri: Uri, +} + +impl Link { + pub fn new(uri: &'static str, label: impl Into<String>) -> Self { + Self { + uri: Uri::from_static(uri), + label: label.into(), + } + } + pub fn slides(uri: &'static str) -> Self { + Self::new(uri, "Slides") + } +} + +#[component] +pub fn UnderlineLink( + #[prop(into)] link: MaybeSignal<Link>, + #[prop(into, optional)] class: MaybeSignal<String>, + #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>, +) -> impl IntoView { + let class = tailwind_fuse::tw_merge!("underline", class.get()); + view! { + <a href=link.get().uri.to_string() {..attributes} class=class target="_blank"> + {link.get().label} + </a> + } +} + +type HideTextSmallDisplay = bool; + +#[component] +pub fn ButtonLink( + #[prop(into)] link: MaybeSignal<Link>, + #[prop(into, optional)] icon: Option<MaybeSignal<Icon>>, + #[prop(into, optional)] hide_text_small_display: Option<MaybeSignal<HideTextSmallDisplay>>, + #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>, +) -> impl IntoView { + let text_css = hide_text_small_display + .and_then(|hide| { + if hide.get() { + Some(tw_join!("hidden", "sm:inline")) + } else { + None + } + }) + .unwrap_or(tw_join!("ml-2", "sm:ml-0", "text-center")); + + view! { + <a + href=link.get().uri.to_string() + {..attributes} + class=tw_join!( + "inline-flex", "bg-sky-900", "hover:bg-sky-700", "transition-all", "duration-200", + "text-white", "font-bold", "py-2", "px-4", "rounded-xl", "items-center" + ) + > + + {icon} + <div class=tw_join!("inline-flex", "items-center")> + <span class=text_css>{link.get().label}</span> + </div> + </a> + } +} + +#[component] +pub fn OutlineButtonLink( + #[prop(into)] link: MaybeSignal<Link>, + #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>, +) -> impl IntoView { + view! { + <a + href=link.get().uri.to_string() + {..attributes} + class=tw_join!( + "mt-4", "inline-flex", "bg-transparent", "hover:bg-sky-700", "text-white", + "font-semibold", "py-1.5", "px-4", "rounded-xl", "items-center", "border", + "border-white", "hover:border-transparent", "transition-all", "duration-200" + ) + > + + {Icon::Link} + <div class=tw_join!("inline-flex", "items-center")> + <span class=tw_join!("ml-2", "sm:ml-0", "text-center")>{link.get().label}</span> + </div> + </a> + } +} + diff --git a/crates/plcom/src/error_template.rs b/crates/plcom/src/error_template.rs new file mode 100644 index 0000000..14bfc35 --- /dev/null +++ b/crates/plcom/src/error_template.rs @@ -0,0 +1,83 @@ +use crate::ContentPage; +use http::status::StatusCode; +use leptos::*; +use thiserror::Error; + +#[derive(Clone, Debug, Error)] +pub enum AppError { + #[error("Not Found")] + NotFound, +} + +impl AppError { + pub fn status_code(&self) -> StatusCode { + match self { + AppError::NotFound => StatusCode::NOT_FOUND, + } + } + + pub fn canonical_reason(&self) -> String { + match self { + AppError::NotFound => StatusCode::NOT_FOUND.to_string(), + } + } + + pub fn description(&self) -> String { + match self { + AppError::NotFound => "This page could not be found.".to_string(), + } + } +} + +// A basic function to display errors served by the error boundaries. +// Feel free to do more complicated things here than just displaying the error. +#[component] +pub fn ErrorTemplate( + #[prop(optional)] outside_errors: Option<Errors>, + #[prop(optional)] errors: Option<RwSignal<Errors>>, +) -> impl IntoView { + let errors = match outside_errors { + Some(e) => create_rw_signal(e), + None => match errors { + Some(e) => e, + None => panic!("No Errors found and we expected errors!"), + }, + }; + // Get Errors from Signal + let errors = errors.get_untracked(); + + // Downcast lets us take a type that implements `std::error::Error` + let errors: Vec<AppError> = errors + .into_iter() + .filter_map(|(_k, v)| v.downcast_ref::<AppError>().cloned()) + .collect(); + println!("Errors: {errors:#?}"); + + // Only the response code for the first error is actually sent from the server + // this may be customized by the specific application + #[cfg(feature = "ssr")] + { + use leptos_axum::ResponseOptions; + let response = use_context::<ResponseOptions>(); + if let Some(response) = response { + response.set_status(errors[0].status_code()); + } + } + + view! { + <For + // a function that returns the items we're iterating over; a signal is fine + each=move || { errors.clone().into_iter().enumerate() } + // a unique key for each item as a reference + key=|(index, _error)| *index + // renders each item to a view + children=move |error| { + view! { + <ContentPage title=error.1.canonical_reason()> + <p>{error.1.description()}</p> + </ContentPage> + } + } + /> + } +} diff --git a/crates/plcom/src/fileserv.rs b/crates/plcom/src/fileserv.rs new file mode 100644 index 0000000..e843576 --- /dev/null +++ b/crates/plcom/src/fileserv.rs @@ -0,0 +1,42 @@ +use axum::{ + body::Body, + extract::State, + response::IntoResponse, + http::{Request, Response, StatusCode, Uri}, +}; +use axum::response::Response as AxumResponse; +use tower::ServiceExt; +use tower_http::services::ServeDir; +use leptos::*; +use crate::app::App; + +pub async fn file_and_error_handler(uri: Uri, State(options): State<LeptosOptions>, req: Request<Body>) -> AxumResponse { + let root = options.site_root.clone(); + let res = get_static_file(uri.clone(), &root).await.unwrap(); + + if res.status() == StatusCode::OK { + res.into_response() + } else { + let handler = leptos_axum::render_app_to_stream(options.to_owned(), App); + handler(req).await.into_response() + } +} + +async fn get_static_file( + uri: Uri, + root: &str, +) -> Result<Response<Body>, (StatusCode, String)> { + let req = Request::builder() + .uri(uri.clone()) + .body(Body::empty()) + .unwrap(); + // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` + // This path is relative to the cargo root + match ServeDir::new(root).oneshot(req).await { + Ok(res) => Ok(res.into_response()), + Err(err) => Err(( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Something went wrong: {err}"), + )), + } +} diff --git a/crates/plcom/src/lib.rs b/crates/plcom/src/lib.rs new file mode 100644 index 0000000..cd69f0b --- /dev/null +++ b/crates/plcom/src/lib.rs @@ -0,0 +1,45 @@ +use leptos::*; +use prelude::*; + +pub mod app; +pub mod common; +pub mod error_template; +#[cfg(feature = "ssr")] +pub mod fileserv; +pub mod pages; + +#[cfg(feature = "hydrate")] +#[wasm_bindgen::prelude::wasm_bindgen] +pub fn hydrate() { + use crate::app::*; + console_error_panic_hook::set_once(); + mount_to_body(App); +} + +pub mod prelude { + pub use super::ContentPage; + pub use crate::common::icon::*; + pub use crate::common::link::*; + pub use crate::common::resume::*; + pub use crate::common::wallpapers::*; + pub use crate::common::*; + pub use leptos::*; + pub use leptos_meta::*; + pub use tailwind_fuse::tw_join; + pub use http::Uri; +} + +#[component] +pub fn ContentPage( + #[prop(into, optional)] title: MaybeSignal<String>, + children: Children, +) -> impl IntoView { + view! { + <leptos_meta::Title text=title.get()></leptos_meta::Title> + <div class=tw_join!("container", "mx-auto", "px-4", "py-16")> + <h1 class=tw_join!("text-3xl", "sm:text-4xl", "font-bold")>{title}</h1> + <UnderlineLink link=Link::new("/", "← Home")/> + <div class=tw_join!("mt-8")>{children()}</div> + </div> + } +} diff --git a/crates/plcom/src/main.rs b/crates/plcom/src/main.rs new file mode 100644 index 0000000..d084ee4 --- /dev/null +++ b/crates/plcom/src/main.rs @@ -0,0 +1,38 @@ +#[cfg(feature = "ssr")] +#[tokio::main] +async fn main() { + use axum::Router; + use leptos::*; + use leptos_axum::{generate_route_list, LeptosRoutes}; + use plcom::app::*; + use plcom::fileserv::file_and_error_handler; + + // Setting get_configuration(None) means we'll be using cargo-leptos's env values + // For deployment these variables are: + // <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain> + // Alternately a file can be specified such as Some("Cargo.toml") + // The file would need to be included with the executable when moved to deployment + let conf = get_configuration(None).await.unwrap(); + let leptos_options = conf.leptos_options; + let addr = leptos_options.site_addr; + let routes = generate_route_list(App); + + // build our application with a route + let app = Router::new() + .leptos_routes(&leptos_options, routes, App) + .fallback(file_and_error_handler) + .with_state(leptos_options); + + let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); + logging::log!("listening on http://{}", &addr); + axum::serve(listener, app.into_make_service()) + .await + .unwrap(); +} + +#[cfg(not(feature = "ssr"))] +pub fn main() { + // no client-side main function + // unless we want this to work with e.g., Trunk for a purely client-side app + // see lib.rs for hydration function instead +} diff --git a/crates/plcom/src/pages.rs b/crates/plcom/src/pages.rs new file mode 100644 index 0000000..94a291f --- /dev/null +++ b/crates/plcom/src/pages.rs @@ -0,0 +1,7 @@ +pub mod root; +pub mod email; +pub mod wallpapers; + +pub use root::RootPage; +pub use email::EmailPage; +pub use wallpapers::WallpapersPage; diff --git a/crates/plcom/src/pages/email.rs b/crates/plcom/src/pages/email.rs new file mode 100644 index 0000000..1c65814 --- /dev/null +++ b/crates/plcom/src/pages/email.rs @@ -0,0 +1,37 @@ +use crate::prelude::*; + +#[component] +pub fn EmailPage() -> impl IntoView { + view! { + <ContentPage title="Email"> + <p> + "Send an email if you want to work with me, propose a project idea, or just to say hi!" + </p> + <div class=tw_join!("my-4")> + <ButtonLink + link=Link::new( + "mailto:wwwATphilippeloctaux~DOT~com", + "www at philippeloctaux dot com", + ) + + icon=Icon::Email + /> + </div> + + <p class=tw_join!( + "mb-2" + )> + "If you want to encrypt your message, I have a " + <UnderlineLink link=Link::new("/pub/pgp-0x69771CD04BA82EC0.txt", "pgp key")/> + " at your disposal." + </p> + <p class=tw_join!( + "mb-2" + )> + "I also have a " <UnderlineLink link=Link::new("/keybase.txt", "Keybase")/> + " account, but I do not check it often." + </p> + </ContentPage> + } +} + diff --git a/crates/plcom/src/pages/root.rs b/crates/plcom/src/pages/root.rs new file mode 100644 index 0000000..48d008e --- /dev/null +++ b/crates/plcom/src/pages/root.rs @@ -0,0 +1,76 @@ +use crate::prelude::*; + +mod hero; +mod www; +mod experience; +mod jobs; +mod projects; +mod education; +mod talks; +mod friends; + +#[component] +pub fn RootPage() -> impl IntoView { + let random_wallpaper = Wallpaper::random(); + + view! { + <Title text="Hello"/> + <hero::Hero wallpaper=random_wallpaper></hero::Hero> + + <div class=tw_join!("container", "mx-auto", "px-4", "md:px-8", "lg:px-16", "py-16")> + <Whoami/> + <div class=tw_join!("my-16", "space-y-16", "md:space-y-32")> + <www::Www></www::Www> + <jobs::Jobs></jobs::Jobs> + <projects::Projects></projects::Projects> + <education::EducationList></education::EducationList> + <talks::Talks></talks::Talks> + <friends::Friends></friends::Friends> + </div> + </div> + } +} + +#[component] +fn Whoami() -> impl IntoView { + view! { + <div class=tw_join!("md:flex", "md:flex-row-reverse", "items-center")> + <div class=tw_join!("md:w-1/2", "mb-4", "md:mb-0")> + <img + src="/phil.png" + alt="Phil" + class=tw_join!( + "rounded-3xl", "bg-sky-900", "h-36", "w-36", "md:mx-auto", "md:h-56", + "md:w-56", "lg:h-64", "lg:w-64", "mb-2", "md:mb-0" + ) + /> + + </div> + + <div class=tw_join!("md:w-1/2")> + <h1 class=tw_join!("text-4xl", "font-bold", "mb-4")>"About Phil"</h1> + <h2 class=tw_join!( + "text-2xl", "font-semibold", "mb-4" + )>"Developer of all sorts"</h2> + + <div class=tw_join!("text-lg", "space-y-6")> + <p> + "I got into computer science by learning about the Linux kernel and administrating servers." + </p> + <p> + "After high school, I became a student at Epitech and learned to tackle technical concepts and apply them quickly by working on small projects." + </p> + <p> + "During my studies at Epitech, I had the opportunity to be a teacher. My role was to assist students with technical problems in their projects." + </p> + <p> + "Now I have experience in software engineering, full-stack web and mobile development, system administration and CI/CD, as well as embedded development." + </p> + <p> + "My goal is to use my knowledge and experience to make software helping its users accomplish their needs." + </p> + </div> + </div> + </div> + } +} diff --git a/crates/plcom/src/pages/root/education.rs b/crates/plcom/src/pages/root/education.rs new file mode 100644 index 0000000..223b80b --- /dev/null +++ b/crates/plcom/src/pages/root/education.rs @@ -0,0 +1,51 @@ +use crate::prelude::*; +use super::experience::*; + +impl IntoView for Education { + fn into_view(self) -> View { + let subtitle = format!("{} in {}", self.study_type, self.area); + view! { + <div class=tw_join!( + "rounded-2xl", "w-full", "bg-amber-950", "p-6" + )> + + {ExperienceHeader::new( + self.start_date, + self.end_date, + self.institution, + &subtitle, + self.logo.as_ref(), + )} + <div class=tw_join!("space-y-2")> + <ul class=tw_join!( + "list-disc", "mt-6" + )> + {self + .courses + .iter() + .map(|h| { + view! { <li class=tw_join!("ml-5")>{*h}</li> } + }) + .collect_view()} + </ul> + </div> + + </div> + }.into_view() + } +} + +#[component] +pub fn EducationList() -> impl IntoView { + view! { + <div> + + <h1 class=tw_join!("text-4xl", "font-bold", "mb-4")>"Education"</h1> + + <div class=tw_join!( + "mt-4", "grid", "grid-cols-1", "md:grid-cols-2", "gap-6", "place-content-center" + )>{resume::EDUCATION.collect_view()}</div> + </div> + } +} + diff --git a/crates/plcom/src/pages/root/experience.rs b/crates/plcom/src/pages/root/experience.rs new file mode 100644 index 0000000..5b76f83 --- /dev/null +++ b/crates/plcom/src/pages/root/experience.rs @@ -0,0 +1,134 @@ +use tailwind_fuse::*; +use leptos::*; +use crate::common::Date; +use crate::common::resume::Logo; + +#[derive(TwClass, Clone, Copy, PartialEq)] +#[tw(class = r#"h-16 w-16 rounded-xl"#)] +struct LogoOptions { + background: ImageBackground, +} + +#[derive(TwVariant, PartialEq)] +enum ImageBackground { + #[tw(class = "p-2 bg-white")] + Transparent, + #[tw(default, class = "")] + Plain, +} + +#[component] +fn ExperienceLogo( + #[prop(into)] image: MaybeSignal<String>, + /// Name of the experience, used in the alt of the image + #[prop(into)] + name: MaybeSignal<String>, + #[prop(into, optional)] background: MaybeSignal<ImageBackground>, + #[prop(into, optional)] class: MaybeSignal<String>, + #[prop(attrs)] attributes: Vec<(&'static str, Attribute)>, +) -> impl IntoView { + let class = create_memo(move |_| { + let background = background.get(); + let logo = LogoOptions { background }; + logo.with_class(class.get()) + }); + let alt = format!("{} logo", name.get()); + + view! { <img {..attributes} loading="lazy" src=image.get() alt=alt class=class/> } +} + +struct ExperienceLogo { + file: String, + options: Option<LogoOptions>, +} + +pub struct ExperienceHeader { + name: String, + description: String, + date_start: Date, + date_end: Option<Date>, + logo: Option<ExperienceLogo>, +} + +impl IntoView for ExperienceHeader { + fn into_view(self) -> View { + let logo = match self.logo { + Some(logo) => view! { + <ExperienceLogo + image=logo.file + name=self.name.clone() + background=logo.options.map(|o| o.background).unwrap_or_default() + class=tw_join!("mr-4") + /> + } + .into_view(), + None => view! {}.into_view(), + }; + + let date = match self.date_end { + Some(end) => format!("{} - {}", self.date_start, end), + None => format!("Since {}", self.date_start), + }; + + view! { + <div class=tw_join!("flex", "flex-col")> + <div class=tw_join!( + "flex", "flex-row" + )> + {logo} <div class=tw_join!("flex", "flex-col", "justify-evenly")> + <div class=tw_join!( + "text-xl", "md:text-2xl", "font-semibold" + )>{self.name}</div> + <div class=tw_join!("text-xs", "md:text-sm")>{date}</div> + </div> + </div> + <p class=tw_join!( + "text-xl", "md:text-2xl", "font-semibold", "my-4" + )>{self.description}</p> + </div> + } + .into_view() + } +} + +impl ExperienceHeader { + pub fn new( + start_date: &str, + end_date: Option<&str>, + name: &str, + description: &str, + logo: Option<&Logo>, + ) -> Self { + let date_start: Vec<_> = start_date.split('-').collect(); + + let date_end = end_date.map(|end| { + let end: Vec<_> = end.split('-').collect(); + Date { + year: end[0].parse().expect("not a number"), + month: end[1].parse().expect("not a number"), + } + }); + + let logo = logo.map(|logo| ExperienceLogo { + file: logo.file.into(), + options: if logo.transparent_background { + Some(LogoOptions { + background: ImageBackground::Transparent, + }) + } else { + None + }, + }); + + Self { + name: name.into(), + description: description.into(), + date_start: Date { + year: date_start[0].parse().expect("not a number"), + month: date_start[1].parse().expect("not a number"), + }, + date_end, + logo, + } + } +} diff --git a/crates/plcom/src/pages/root/friends.rs b/crates/plcom/src/pages/root/friends.rs new file mode 100644 index 0000000..d034ec9 --- /dev/null +++ b/crates/plcom/src/pages/root/friends.rs @@ -0,0 +1,145 @@ +use crate::prelude::*; + +#[derive(Clone, PartialEq)] +struct Name { + first: String, + last: Option<String>, +} + +impl Name { + pub fn nick(nick: impl Into<String>) -> Self { + Self { + first: nick.into(), + last: None, + } + } + pub fn new(first: impl Into<String>, last: impl Into<String>) -> Self { + Self { + first: first.into(), + last: Some(last.into()), + } + } + + pub fn initials(&self) -> String { + let first = self + .first + .to_uppercase() + .chars() + .next() + .expect("Invalid first name"); + let last = self + .last + .as_ref() + .and_then(|last| last.to_uppercase().chars().next()); + + match last { + Some(last) => format!("{first}{last}"), + None => first.into(), + } + } +} + +impl std::fmt::Display for Name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.last { + Some(last) => write!(f, "{} {}", self.first, last), + None => write!(f, "{}", self.first), + } + } +} + +#[derive(Clone, PartialEq)] +struct Friend { + name: Name, + uri: Uri, +} + +impl Friend { + pub fn nick(nick: impl Into<String>, uri: &'static str) -> Self { + Self { + name: Name::nick(nick), + uri: Uri::from_static(uri), + } + } + + pub fn new(first: impl Into<String>, last: impl Into<String>, uri: &'static str) -> Self { + Self { + name: Name::new(first, last), + uri: Uri::from_static(uri), + } + } + + pub fn domain_name(&self) -> String { + self.uri + .authority() + .map(|authority| authority.to_string()) + .unwrap_or_else(|| self.uri.to_string()) + } +} + +#[component] +fn Friend(#[prop(into)] friend: MaybeSignal<Friend>) -> impl IntoView { + view! { + <a + href=friend.get().uri.to_string() + target="_blank" + class=tw_join!( + "hover:bg-gray-500", "transition-all", "duration-200", "flex", "items-center", + "rounded-lg", "p-2" + ) + > + + <span class=tw_join!( + "rounded-full", "flex-shrink-0", "mr-4", "w-10", "h-10", "bg-sky-900", "text-white", + "flex", "items-center", "justify-center", "text-lg", "font-medium" + )>{friend.get().name.initials()}</span> + <div> + <p class=tw_join!("font-bold")>{friend.get().name.to_string()}</p> + <p>{friend.get().domain_name()}</p> + </div> + </a> + } +} + +#[component] +pub fn Friends() -> impl IntoView { + let friends = [ + Friend::new("Paolo", "Rotolo", "https://rotolo.dev"), + Friend::new("Polly", "Bishop", "https://github.com/itspolly"), + Friend::new("Ayden", "Panhuyzen", "https://ayden.dev"), + Friend::new("Corbin", "Crutchley", "https://crutchcorn.dev"), + Friend::new("James", "Fenn", "https://jfenn.me"), + Friend::new("Alex", "Dueppen", "https://ajd.sh"), + Friend::new("Lyra", "Messier", "https://lyramsr.co"), + Friend::new("Peter", "Soboyejo", "https://twitter.com/pxtvr"), + Friend::nick("Millomaker", "https://youtube.com/millomaker"), + Friend::new("Alexandre", "Wagner", "https://wagnerwave.com"), + Friend::new("Aidan", "Follestad", "https://af.codes"), + Friend::new("Victor", "Simon", "https://simonvictor.com"), + ]; + + view! { + <div> + <h1 class=tw_join!("text-4xl", "font-bold", "mb-4")>"Friends"</h1> + <p class=tw_join!("text-lg")>"Folks I worked with, or I like what they do."</p> + + <ul class=tw_join!( + "my-4", "grid", "grid-cols-1", "sm:grid-cols-2", "md:grid-cols-3", "lg:grid-cols-4", + "sm:gap-4" + )> + {friends + .into_iter() + .map(|f| { + view! { + <li class=tw_join!("py-2")> + <Friend friend=f/> + </li> + } + }) + .collect_view()} + </ul> + + <p>"If you do not appear here and we know each other, hit me up!"</p> + </div> + } +} diff --git a/crates/plcom/src/pages/root/hero.rs b/crates/plcom/src/pages/root/hero.rs new file mode 100644 index 0000000..93b2b91 --- /dev/null +++ b/crates/plcom/src/pages/root/hero.rs @@ -0,0 +1,100 @@ +use crate::prelude::*; + +#[component] +fn WallpaperInfo(#[prop(into)] wallpaper: &'static Wallpaper) -> impl IntoView { + view! { + <div class=tw_join!( + "absolute", "bottom-3", "sm:bottom-5", "left-2", "sm:left-5", "inline-block", + "backdrop-blur-lg", "backdrop-brightness-75", "rounded-xl", "shadow-2xl", "p-2", + "space-y-0.5", "sm:space-y-2" + )> + + // See more + <div class=tw_join!("flex")> + <div class=tw_join!( + "inline-flex", "items-center" + )> + {Icon::Map} + <a + class=tw_join!("ml-1", "text-sm", "underline") + href="/wallpapers" + target="_blank" + > + "See more!" + </a> + </div> + </div> + + // Location + <div class=tw_join!("flex")> + <div class=tw_join!( + "inline-flex", "items-center" + )> + {Icon::Location} + <span class=tw_join!( + "ml-1", "text-sm" + )> + {wallpaper.location.precise} + <span class=tw_join!( + "hidden", "md:inline" + )>", "{wallpaper.location.broad}</span> + </span> + </div> + </div> + + // Date + <div class=tw_join!("flex")> + <div class=tw_join!( + "inline-flex", "items-center" + )> + {Icon::Calendar} <span class=tw_join!("ml-1", "text-sm")>{wallpaper.date}</span> + </div> + </div> + + </div> + } +} + +#[component] +pub fn Hero(#[prop(into)] wallpaper: Option<&'static Wallpaper>) -> impl IntoView { + let (wallpaper_info, background_image) = match wallpaper { + Some(wallpaper) => ( + view! { <WallpaperInfo wallpaper=wallpaper/> }.into_view(), + format!("background-image: url(/wallpapers/{});", wallpaper.filename), + ), + None => (view! {}.into_view(), "".into()), + }; + + view! { + <div class=tw_join!("bg-gradient-to-r", "from-red-900", "via-teal-900", "to-fuchsia-900")> + <div + id="wallpaper" + class=tw_join!( + "relative", "text-white", "w-full", "h-almostscreen", "bg-center", "bg-cover" + ) + + style=background_image + > + + <div class=tw_join!( + "container", "mx-auto", "px-8", "py-16", "w-full", "h-full", "justify-center", + "items-center", "flex", "flex-col" + )> + <div class=tw_join!( + "inline-block", "backdrop-blur-lg", "backdrop-brightness-75", "rounded-3xl", + "shadow-2xl", "px-4", "py-6", "sm:px-8", "sm:py-12", "space-y-4" + )> + <h1 class=tw_join!( + "text-3xl", "sm:text-4xl", "font-bold" + )>"Philippe Loctaux"</h1> + <h2 class=tw_join!( + "sm:text-xl", "font-semibold" + )>"Developer of all sorts. Epitech alumni, class of 2023."</h2> + </div> + </div> + + {wallpaper_info} + </div> + </div> + } +} diff --git a/crates/plcom/src/pages/root/jobs.rs b/crates/plcom/src/pages/root/jobs.rs new file mode 100644 index 0000000..2275b40 --- /dev/null +++ b/crates/plcom/src/pages/root/jobs.rs @@ -0,0 +1,76 @@ +use crate::prelude::*; +use super::experience::*; + +impl IntoView for Work { + fn into_view(self) -> View { + view! { + <div class=tw_join!("w-full", "rounded-2xl", "bg-sky-950")> + + <div class=tw_join!( + "p-6", "justify-between", "h-full" + )> + + {ExperienceHeader::new( + self.start_date, + self.end_date, + self.name, + self.position, + Some(&self.logo), + )} <div class=tw_join!("space-y-2")> + + <p>{self.description}</p> + + <div> + <ul class=tw_join!( + "list-disc", "mt-6" + )> + {self + .highlights + .iter() + .map(|h| { + view! { <li class=tw_join!("ml-5")>{*h}</li> } + }) + .collect_view()} + </ul> + </div> + + <div> + <div class=tw_join!( + "mt-6 flex flex-wrap gap-x-6 gap-y-4" + )> + {self + .technologies + .iter() + .map(|t| { + view! { + <span class=tw_join!( + "inline-flex", "items-center", "rounded-md", "bg-blue-100", + "px-2", "py-1", "font-medium", "text-blue-700" + )>{*t}</span> + } + }) + .collect_view()} + </div> + + </div> + </div> {self.link} + + </div> + </div> + }.into_view() + } +} + +#[component] +pub fn Jobs() -> impl IntoView { + view! { + <div> + <h1 class=tw_join!("text-4xl", "font-bold", "mb-4")>"Professional Experiences"</h1> + + <div class=tw_join!( + "mt-4", "grid", "grid-cols-1", "sm:grid-cols-2", "gap-4" + )>{resume::WORK.collect_view()}</div> + </div> + } +} + diff --git a/crates/plcom/src/pages/root/projects.rs b/crates/plcom/src/pages/root/projects.rs new file mode 100644 index 0000000..1bcc3fe --- /dev/null +++ b/crates/plcom/src/pages/root/projects.rs @@ -0,0 +1,163 @@ +use crate::prelude::*; +use super::experience::*; + +impl IntoView for Project { + fn into_view(self) -> View { + let (css_image_position, css_image_position_corner) = match self.image { + Some(image) => { + let position = match image.position { + "left" => tw_join!("2xl:flex items-stretch justify-between"), + "right" => { + tw_join!("2xl:flex items-stretch justify-between 2xl:flex-row-reverse") + } + _ => todo!("match on an enum instead of raw strings"), + }; + + let corner = match image.position { + "left" => tw_join!("2xl:rounded-tr-none", "2xl:rounded-l-2xl"), + "right" => tw_join!("2xl:rounded-tl-none", "2xl:rounded-r-2xl"), + _ => todo!("match on an enum instead of raw strings"), + }; + let corner = tw_join!( + "flex", + "w-full", + "2xl:w-1/2", + "grow", + "rounded-t-2xl", + "object-cover", + corner + ); + + (position, corner) + } + None => ("".into(), "".into()), + }; + + view! { + <div class=tw_join!( + "w-full", "rounded-2xl", "bg-pink-950", css_image_position + )> + + {if let Some(image) = self.image { + view! { + <img + loading="lazy" + src=image.file + alt=format!("{} image", self.name) + class=css_image_position_corner + /> + } + .into_view() + } else { + view! {}.into_view() + }} + <div class=tw_join!( + "p-6", "justify-between", "h-full" + )> + + {ExperienceHeader::new( + self.start_date, + self.end_date, + self.name, + self.description, + self.logo.as_ref(), + )} + <div class=tw_join!( + "space-y-2" + )> + + {self + .presentation + .iter() + .map(|p| { + view! { <p>{*p}</p> } + }) + .collect_view()} <div> + <ul class=tw_join!( + "list-disc", "mt-6" + )> + {self + .highlights + .iter() + .map(|h| { + view! { <li class=tw_join!("ml-5")>{*h}</li> } + }) + .collect_view()} + </ul> + </div> <div> + + <div class=tw_join!( + "mt-6", "grid", "grid-cols-2", "sm:grid-cols-3", "gap-x-6", + "gap-y-4" + )> + {self + .keywords + .iter() + .map(|t| { + view! { + <span class=tw_join!( + "items-center", "rounded-md", "bg-blue-100", "px-2", "py-1", + "font-medium", "text-blue-700", + )>{*t}</span> + } + }) + .collect_view()} + </div> + + </div> + </div> {self.link} + + </div> + </div> + }.into_view() + } +} + + type ImageProject = Project; + type TextProject = Project; + + enum DisplayProject { + Text(Box<(TextProject, Option<TextProject>)>), + Image(ImageProject) + } + +impl IntoView for DisplayProject { + fn into_view(self) -> View { + match self { + Self::Image(image) => image.into_view(), + Self::Text(boxy) => { + let (text1, text2) = *boxy; + view! { + <div class=tw_join!( + "my-4", "grid", "grid-cols-1", "sm:grid-cols-2", "gap-4" + )>{text1} {text2}</div> + }.into_view() + + } + } + } +} + +#[component] +pub fn Projects() -> impl IntoView { + let mut projects = vec![]; + let mut iter = resume::PROJECTS.iter(); + + while let Some(cur_proj) = iter.next() { + if cur_proj.image.is_some() { + projects.push(DisplayProject::Image(*cur_proj)); + } else { + let next_text = iter.next(); + projects.push(DisplayProject::Text(Box::new((*cur_proj, next_text.copied())))); + } + } + + view! { + <div> + + <h1 class=tw_join!("text-4xl", "font-bold", "mb-4")>"Projects"</h1> + + {projects} + </div> + } +} diff --git a/crates/plcom/src/pages/root/talks.rs b/crates/plcom/src/pages/root/talks.rs new file mode 100644 index 0000000..25d845c --- /dev/null +++ b/crates/plcom/src/pages/root/talks.rs @@ -0,0 +1,137 @@ +use crate::prelude::*; + +#[derive(Clone, PartialEq)] +struct Talk { + title: String, + date: Date, + location: String, + link: Link, +} + +impl Talk { + pub fn new( + title: impl Into<String>, + date: Date, + location: impl Into<String>, + link: Link, + ) -> Self { + Self { + title: title.into(), + date, + location: location.into(), + link, + } + } +} + +#[component] +fn Talk(#[prop(into)] talk: MaybeSignal<Talk>) -> impl IntoView { + view! { + <div class=tw_join!("rounded-2xl", "w-full", "bg-teal-950", "p-6")> + + <h3 class=tw_join!("text-xl", "font-semibold", "mb-4")>{talk.get().title}</h3> + + <div class=tw_join!("flex")> + <div class=tw_join!( + "inline-flex", "items-center" + )> + {Icon::Calendar} + <span class=tw_join!("ml-2")>{talk.get().date.to_string()}</span> + </div> + </div> + + <div class=tw_join!("flex")> + <div class=tw_join!( + "inline-flex", "items-center" + )>{Icon::Location} <span class=tw_join!("ml-2")>{talk.get().location}</span></div> + </div> + + <OutlineButtonLink link=talk.get().link/> + </div> + } +} + +#[component] +pub fn Talks() -> impl IntoView { + let talks = [ + Talk::new( + "Vim", + Date { + year: 2023, + month: 2, + }, + "Epitech Rennes", + Link::slides("/pub/talks/vim.pdf"), + ), + Talk::new( + "CLion", + Date { + year: 2021, + month: 3, + }, + "Epitech Rennes", + Link::slides("/pub/talks/clion.pdf"), + ), + Talk::new( + "git & devops 2", + Date { + year: 2021, + month: 2, + }, + "Epitech Rennes", + Link::slides("/pub/talks/git-devops2.pdf"), + ), + Talk::new( + "pass4thewin", + Date { + year: 2021, + month: 2, + }, + "Epitech Rennes", + Link::slides("/pub/talks/pass4thewin.pdf"), + ), + Talk::new( + "git & devops", + Date { + year: 2020, + month: 5, + }, + "Epitech Rennes", + Link::slides("/pub/talks/git-devops.pdf"), + ), + Talk::new( + "git gud", + Date { + year: 2019, + month: 5, + }, + "Epitech Rennes", + Link::slides("/pub/talks/git-tek.pdf"), + ), + ]; + + view! { + <div> + <h1 class=tw_join!("text-4xl", "font-bold", "mb-4")>"Talks"</h1> + <p class=tw_join!( + "text-lg" + )> + "Giving a talk is the opportunity to share what I know, and helps me reduce my fear of public speaking." + </p> + + <div class=tw_join!( + "mt-4", "grid", "grid-cols-1", "sm:grid-cols-2", "lg:grid-cols-3", "gap-6", + "place-content-center" + )> + {talks + .into_iter() + .map(|t| { + view! { <Talk talk=t/> } + }) + .collect_view()} + </div> + + </div> + } +} + diff --git a/crates/plcom/src/pages/root/www.rs b/crates/plcom/src/pages/root/www.rs new file mode 100644 index 0000000..325b4b4 --- /dev/null +++ b/crates/plcom/src/pages/root/www.rs @@ -0,0 +1,65 @@ +use crate::prelude::*; + +#[derive(Clone, PartialEq)] +struct Www { + link: Link, + icon: Icon, +} + +#[component] +pub fn Www() -> impl IntoView { + let www = [ + Www { + link: Link::new("https://twitter.com/philippeloctaux", "Twitter"), + icon: Icon::Twitter, + }, + Www { + link: Link::new("https://t.me/philippeloctaux", "Telegram"), + icon: Icon::Telegram, + }, + Www { + link: Link::new("https://mastodon.social/@philt3r", "Mastodon"), + icon: Icon::Mastodon, + }, + Www { + link: Link::new("https://github.com/x4m3", "GitHub"), + icon: Icon::Github, + }, + Www { + link: Link::new("https://linkedin.com/in/philippeloctaux", "LinkedIn"), + icon: Icon::Linkedin, + }, + Www { + link: Link::new("/email", "Email"), + icon: Icon::Email, + }, + ]; + view! { + <div class=tw_join!( + "grid", "grid-cols-3", "lg:grid-cols-6", "gap-4", "place-content-center" + )> + + {www + .into_iter() + .map(|w| { + view! { + <div class=tw_join!("w-full", "h-auto", "md:w-auto")> + <div class=tw_join!("text-center")> + <ButtonLink + link=w.link + icon=w.icon + hide_text_small_display=true + attributes=vec![ + ("target", Attribute::String(Oco::Borrowed("_blank"))), + ] + /> + + </div> + </div> + } + }) + .collect_view()} + + </div> + } +} diff --git a/crates/plcom/src/pages/wallpapers.rs b/crates/plcom/src/pages/wallpapers.rs new file mode 100644 index 0000000..c5dba14 --- /dev/null +++ b/crates/plcom/src/pages/wallpapers.rs @@ -0,0 +1,49 @@ +use crate::prelude::*; + +#[component] +pub fn WallpapersPage() -> impl IntoView { + use leptos_leaflet::{TileLayer, MapContainer, Position, Marker, position, Popup}; + + let wallpapers = WALLPAPERS; + + view! { + <ContentPage title="Wallpapers"> + <Stylesheet href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/> + <Script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"/> + <p class=tw_join!("mb-2")>"Pictures I took around the world"</p> + <MapContainer + center=Position::new(0.0, 0.0) + zoom=1.0 + class=tw_join!("w-full", "h-halfscreen") + > + <TileLayer + url="https://tile.openstreetmap.org/{z}/{x}/{y}.png" + attribution="© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors" + /> + <For + each=move || wallpapers + key=|w| w.filename + children=move |w: Wallpaper| { + let uri = format!("/wallpapers/{}", w.filename); + view! { + <Marker position=position!( + w.gps.latitude.into(), w.gps.longitude.into() + )> + <Popup> + <a target="_blank" href=uri> + {w.location.precise} + </a> + <br/> + {w.location.broad} + <br/> + <b>{w.date}</b> + </Popup> + </Marker> + } + } + /> + + </MapContainer> + </ContentPage> + } +} diff --git a/crates/plcom/tailwind.config.js b/crates/plcom/tailwind.config.js new file mode 100644 index 0000000..f3e50e1 --- /dev/null +++ b/crates/plcom/tailwind.config.js @@ -0,0 +1,16 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: { + relative: true, + files: ["./src/**/*.rs"], + }, + theme: { + extend: { + height: { + almostscreen: "90vh", + halfscreen: "60vh", + } + }, + }, + plugins: [], + } diff --git a/css/tailwind.css b/css/tailwind.css deleted file mode 100644 index bd6213e..0000000 --- a/css/tailwind.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; \ No newline at end of file diff --git a/public/icons/rubycat.png b/public/icons/rubycat.png new file mode 100644 index 0000000..f234b17 Binary files /dev/null and b/public/icons/rubycat.png differ diff --git a/public/icons/uqac.png b/public/icons/uqac.png new file mode 100644 index 0000000..a64b12c Binary files /dev/null and b/public/icons/uqac.png differ diff --git a/public/phil.png b/public/phil.png new file mode 100644 index 0000000..656150b Binary files /dev/null and b/public/phil.png differ diff --git a/public/pub/phil.png b/public/pub/phil.png deleted file mode 100644 index 9306695..0000000 Binary files a/public/pub/phil.png and /dev/null differ diff --git a/public/pub/philt3r.png b/public/pub/philt3r.png deleted file mode 100644 index 7c8a799..0000000 Binary files a/public/pub/philt3r.png and /dev/null differ diff --git a/readme.md b/readme.md index 1c2aa18..8f56143 100644 --- a/readme.md +++ b/readme.md @@ -6,7 +6,7 @@ http://philippeloctaux.com ## tech -- https://rocket.rs +- https://leptos.dev - https://tailwindcss.com ## colors @@ -16,9 +16,10 @@ http://philippeloctaux.com ## wallpapers 1. place **JPEG** files in `public/wallpapers` and make sure they have exif data (GPS + date) -2. run `cargo run --bin gen-wallpapers` to generate wallpaper metadata +2. run `cargo run -p gen-wallpapers --example cli -- ./public/wallpapers > ./crates/plcom/wallpapers.json` to generate wallpaper metadata ## icons - https://simpleicons.org for brand icons -- https://heroicons.com for the rest \ No newline at end of file +- https://heroicons.com for the rest + diff --git a/src/cache.rs b/src/cache.rs deleted file mode 100644 index 16fdc1f..0000000 --- a/src/cache.rs +++ /dev/null @@ -1,54 +0,0 @@ -use rocket::fairing::{self, Fairing}; -use rocket::http::{ContentType, Header}; -use rocket::{Request, Response}; - -#[derive(Debug)] -pub struct CacheControl { - duration_secs: u32, - types: Vec<ContentType>, - routes: Vec<&'static str>, -} - -impl Default for CacheControl { - fn default() -> Self { - CacheControl { - duration_secs: 60 * 60, // 60 secs * 60 minutes - types: vec![ContentType::CSS, ContentType::JavaScript], - routes: vec!["/wallpapers", "/pub", "/images", "/icons"], - } - } -} - -#[rocket::async_trait] -impl Fairing for CacheControl { - fn info(&self) -> fairing::Info { - fairing::Info { - name: "Cache Control", - kind: fairing::Kind::Response, - } - } - - async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) { - let mut should_cache = false; - - // Check if content type matches - if let Some(content_type) = response.content_type() { - if self.types.contains(&content_type) { - should_cache = true; - } - } - - // Check if route matches - self.routes - .iter() - .filter(|s| request.uri().path().starts_with(*s)) - .for_each(|_| should_cache = true); - - if should_cache { - response.set_header(Header::new( - "Cache-Control", - format!("public, max-age={}", self.duration_secs), - )); - } - } -} diff --git a/src/filters.rs b/src/filters.rs deleted file mode 100644 index f27ebf4..0000000 --- a/src/filters.rs +++ /dev/null @@ -1,11 +0,0 @@ -const SUFFIX: &str = "Philippe Loctaux"; - -pub fn title<T: std::fmt::Display>(s: T) -> ::askama::Result<String> { - let prefix = s.to_string(); - - Ok(if prefix != SUFFIX { - format!("{prefix} - {SUFFIX}") - } else { - prefix - }) -} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index ea737de..0000000 --- a/src/main.rs +++ /dev/null @@ -1,78 +0,0 @@ -use self::types::*; -use chrono::Datelike; -use rocket::fs::FileServer; -use rocket::{catch, catchers, get, launch, routes}; - -mod cache; -mod filters; -mod minify; -mod templates; -mod types; -mod wallpapers; - -#[launch] -fn rocket() -> _ { - let server = rocket::build() - .mount("/", FileServer::from("public")) - .mount("/", routes![root, email, wallpapers_route]) - .register("/", catchers![not_found]); - - if cfg!(debug_assertions) { - server - } else { - server - .attach( - rocket_async_compression::CachedCompression::path_suffix_fairing(vec![ - // Code - ".js".into(), - ".css".into(), - // Documents - ".pdf".into(), - ".txt".into(), - ]), - ) - .attach(cache::CacheControl::default()) - .attach(minify::Minify) - } -} - -#[catch(404)] -fn not_found() -> templates::NotFound<'static> { - templates::NotFound { - title: "404 Not found", - year: chrono::Utc::now().year(), - } -} - -#[get("/?<wallpaper>")] -fn root(wallpaper: Option<&str>) -> templates::Root<'static> { - templates::Root { - title: "Philippe Loctaux", - year: chrono::Utc::now().year(), - wallpaper: wallpaper - .and_then(Wallpaper::find) - .or_else(Wallpaper::random), - networks: Network::new(), - jobs: Job::new(), - talks: Talk::new(), - friends: Friend::new(), - projects: ProjectKind::new(), - } -} - -#[get("/email")] -fn email() -> templates::Email<'static> { - templates::Email { - title: "Email", - year: chrono::Utc::now().year(), - } -} - -#[get("/wallpapers")] -fn wallpapers_route() -> templates::Wallpapers<'static> { - templates::Wallpapers { - title: "Wallpapers", - year: chrono::Utc::now().year(), - wallpapers: WALLPAPERS, - } -} diff --git a/src/minify.rs b/src/minify.rs deleted file mode 100644 index 17848bc..0000000 --- a/src/minify.rs +++ /dev/null @@ -1,39 +0,0 @@ -use rocket::fairing::{self, Fairing, Kind}; -use rocket::http::ContentType; -use rocket::{Request, Response}; - -pub struct Minify; - -#[rocket::async_trait] -impl Fairing for Minify { - fn info(&self) -> fairing::Info { - fairing::Info { - name: "Minify HTML", - kind: Kind::Response, - } - } - - async fn on_response<'r>(&self, _request: &'r Request<'_>, response: &mut Response<'r>) { - if response.content_type() == Some(ContentType::HTML) { - let body = response.body_mut(); - - if let Ok(original) = body.to_bytes().await { - let cfg = minify_html::Cfg { - // Be HTML spec compliant - do_not_minify_doctype: true, - ensure_spec_compliant_unquoted_attribute_values: true, - keep_spaces_between_attributes: true, - - // The rest - keep_closing_tags: true, - keep_html_and_head_opening_tags: true, - minify_css: false, - minify_js: true, - ..Default::default() - }; - let minified = minify_html::minify(&original, &cfg); - response.set_sized_body(minified.len(), std::io::Cursor::new(minified)); - } - } - } -} diff --git a/src/templates.rs b/src/templates.rs deleted file mode 100644 index 18265bd..0000000 --- a/src/templates.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::filters; -use crate::types::*; - -#[derive(askama::Template)] -#[template(path = "pages/root.html")] -pub struct Root<'a> { - pub title: &'a str, - pub year: i32, - pub wallpaper: Option<&'static &'static Wallpaper>, - pub networks: Vec<Network>, - pub jobs: Vec<Job>, - pub talks: Vec<Talk>, - pub friends: Vec<Friend>, - pub projects: Vec<ProjectKind>, -} - -#[derive(rocket::Responder)] -struct RootResponder<'a> { - template: Root<'a>, -} - -#[derive(askama::Template)] -#[template(path = "pages/404.html")] -pub struct NotFound<'a> { - pub title: &'a str, - pub year: i32, -} - -#[derive(rocket::Responder)] -struct NotFoundResponder<'a> { - template: NotFound<'a>, -} - -#[derive(askama::Template)] -#[template(path = "pages/email.html")] -pub struct Email<'a> { - pub title: &'a str, - pub year: i32, -} - -#[derive(rocket::Responder)] -struct EmailResponder<'a> { - template: Email<'a>, -} - -#[derive(askama::Template)] -#[template(path = "pages/wallpapers.html")] -pub struct Wallpapers<'a> { - pub title: &'a str, - pub year: i32, - pub wallpapers: &'static [&'static Wallpaper], -} - -#[derive(rocket::Responder)] -struct WallpapersResponder<'a> { - template: Wallpapers<'a>, -} diff --git a/src/types.rs b/src/types.rs deleted file mode 100644 index 364fccc..0000000 --- a/src/types.rs +++ /dev/null @@ -1,65 +0,0 @@ -use rocket::http::uri::Absolute; - -pub mod friend; -pub mod job; -pub mod network; -pub mod project; -pub mod talk; - -pub use self::friend::*; -pub use self::job::*; -pub use self::network::*; -pub use self::project::*; -pub use self::talk::*; -pub use crate::wallpapers::WALLPAPERS; - -pub struct Logo { - pub file: &'static str, - pub transparent_background: bool, -} - -pub struct Link { - pub label: &'static str, - pub uri: Absolute<'static>, -} - -pub struct Location { - pub precise: &'static str, - pub broad: &'static str, - pub gps: Gps, -} - -pub struct Gps { - pub latitude: f32, - pub longitude: f32, -} - -impl std::fmt::Display for Gps { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "[{}, {}]", self.latitude, self.longitude) - } -} - -pub struct Wallpaper { - pub file: &'static str, - pub date: &'static str, - pub location: Location, -} - -impl Wallpaper { - pub fn random() -> Option<&'static &'static Wallpaper> { - use nanorand::{ChaCha20, Rng}; - use std::ops::Range; - - let range = Range { - start: 0, - end: WALLPAPERS.len(), - }; - - WALLPAPERS.get(ChaCha20::new().generate_range(range)) - } - - pub fn find(filename: &str) -> Option<&'static &'static Wallpaper> { - WALLPAPERS.iter().find(|w| w.file.contains(filename)) - } -} diff --git a/src/types/friend.rs b/src/types/friend.rs deleted file mode 100644 index a6fb0c4..0000000 --- a/src/types/friend.rs +++ /dev/null @@ -1,84 +0,0 @@ -use rocket::http::uri::Absolute; -use rocket::uri; - -pub struct Friend { - pub first_name: &'static str, - pub last_name: &'static str, - pub uri: Absolute<'static>, -} - -impl Friend { - pub fn initials(&self) -> String { - let first = self - .first_name - .to_uppercase() - .chars() - .next() - .expect("Invalid first name"); - let last = self - .last_name - .to_uppercase() - .chars() - .next() - .expect("Invalid last name"); - - format!("{first}{last}") - } - - pub fn domain_name(&self) -> String { - match self.uri.authority() { - Some(authority) => authority.to_string(), - None => self.uri.to_string(), - } - } - - pub fn new() -> Vec<Self> { - vec![ - Friend { - first_name: "Jamie", - last_name: "Bishop", - uri: uri!("https://jamiebi.shop"), - }, - Friend { - first_name: "Ayden", - last_name: "Panhuyzen", - uri: uri!("https://ayden.dev"), - }, - Friend { - first_name: "Corbin", - last_name: "Crutchley", - uri: uri!("https://crutchcorn.dev"), - }, - Friend { - first_name: "James", - last_name: "Fenn", - uri: uri!("https://jfenn.me"), - }, - Friend { - first_name: "Alex", - last_name: "Dueppen", - uri: uri!("https://ajd.sh"), - }, - Friend { - first_name: "Peter", - last_name: "Sobolev", - uri: uri!("https://petersoboyejo.com"), - }, - Friend { - first_name: "Alexandre", - last_name: "Wagner", - uri: uri!("https://wagnerwave.com"), - }, - Friend { - first_name: "Aidan", - last_name: "Follestad", - uri: uri!("https://af.codes"), - }, - Friend { - first_name: "Victor", - last_name: "Simon", - uri: uri!("https://simonvictor.com"), - }, - ] - } -} diff --git a/src/types/job.rs b/src/types/job.rs deleted file mode 100644 index 0be7de9..0000000 --- a/src/types/job.rs +++ /dev/null @@ -1,111 +0,0 @@ -use crate::types::Logo; - -pub struct Job { - pub company: &'static str, - pub title: &'static str, - pub dates: &'static str, - pub logo: Logo, - - pub description: Vec<&'static str>, - pub accomplishments: Vec<&'static str>, - pub technologies: Vec<&'static str>, -} - -impl Job { - pub fn new() -> Vec<Self> { - vec![ - Job { - company: "Acklio", - title: "Rust developer", - dates: "March 2023 - May 2023", - logo: Logo { - file: "/icons/acklio.png", - transparent_background: true, - }, - description: vec!["The first usage of the SCHC framework (RFC 8724) on Rust!"], - accomplishments: vec![ - "Creation of Rust bindings of a C library implementing the SCHC framework", - "Demonstration of SCHC with applications in Rust on x86 platform", - "Proof of concept usage of embedded STM32 controllers exclusively in Rust", - "Transmission of knowledge to the technical team", - ], - technologies: vec!["Rust", "SCHC", "STM32 controllers", "LoRa", "LoRaWAN"], - }, - Job { - company: "Vélorail du Kreiz Breizh", - title: "Freelance developer", - dates: "August 2021 - April 2022", - logo: Logo { - file: "/icons/velorail.png", - transparent_background: true, - }, - description: vec![ - "Creation of an online booking platform focused on the tourist activity of rail biking (vélorail).", - "During the first 5 months with the platform, 43% of the bookings were made online.", - ], - accomplishments: vec![ - "Design, UX, booking and payment flow for customers", - "Dashboard for managers with calendar view, manual bookings, slots management", - "Ability to generate invoices, booking recaps for managers", - "Sending emails to customers and managers about bookings", - "Online deployment, maintenance of the service", - ], - technologies: vec!["Angular", "NestJS", "GraphQL", "Rust", "Stripe"], - }, - Job { - company: "Yaakadev", - title: "Full-Stack developer", - dates: "April 2021 - July 2021", - logo: Logo { - file: "/icons/yaakadev.png", - transparent_background: false, - }, - description: vec![ - "Maintenance of existing projects for clients", - "Design, development and deployment of multiple projects from scratch:", - ], - accomplishments: vec![ - "Admin dashboard of a local merchants solution", - "Calendar planning application with filtering and custom views", - "Intranet to upload and download documents", - ], - technologies: vec!["NodeJS", "ExpressJS", "Angular", "MongoDB", "CI/CD"], - }, - Job { - company: "Epitech", - title: "Teaching assistant (AER)", - dates: "February 2020 - April 2021, September 2022 - February 2023", - logo: Logo { - file: "/icons/epitech.png", - transparent_background: true, - }, - description: vec![ - "Pedagogical supervision of three classes of students.", - "Conducting educational activities throughout the school year.", - ], - accomplishments: vec![ - "Start of projects", - "Technical help and guidance", - "Proctoring exams", - "Grading students on their work", - ], - technologies: vec!["C", "C++", "Haskell", "Rust", "Web and mobile development"], - }, - Job { - company: "Ubiscale", - title: "Embedded developer", - dates: "August 2019 - December 2019", - logo: Logo { - file: "/icons/ubiscale.png", - transparent_background: true, - }, - description: vec!["Creation of a home Wifi gateway for an IoT object."], - accomplishments: vec![ - "Research, reverse engineering of existing products", - "Design and implementation.", - ], - technologies: vec!["C on a ESP8266 controller", "Wi-Fi", "Bluetooth"], - }, - ] - } -} diff --git a/src/types/network.rs b/src/types/network.rs deleted file mode 100644 index 7307f25..0000000 --- a/src/types/network.rs +++ /dev/null @@ -1,52 +0,0 @@ -use rocket::http::uri::Absolute; -use rocket::uri; -pub enum Icon { - Email, - Github, - Linkedin, - Mastodon, - Telegram, - Twitter, -} - -pub struct Network { - pub name: &'static str, - pub uri: Absolute<'static>, - pub icon: Icon, -} -impl Network { - pub fn new() -> Vec<Self> { - vec![ - Network { - name: "Twitter", - uri: uri!("https://twitter.com/philippeloctaux"), - icon: Icon::Twitter, - }, - Network { - name: "Telegram", - uri: uri!("https://t.me/philippeloctaux"), - icon: Icon::Telegram, - }, - Network { - name: "Mastodon", - uri: uri!("https://mastodon.social/@philt3r"), - icon: Icon::Mastodon, - }, - Network { - name: "GitHub", - uri: uri!("https://github.com/x4m3"), - icon: Icon::Github, - }, - Network { - name: "LinkedIn", - uri: uri!("https://linkedin.com/in/philippeloctaux"), - icon: Icon::Linkedin, - }, - Network { - name: "Email", - uri: uri!("https://philippeloctaux.com/email"), - icon: Icon::Email, - }, - ] - } -} diff --git a/src/types/project.rs b/src/types/project.rs deleted file mode 100644 index e34c81e..0000000 --- a/src/types/project.rs +++ /dev/null @@ -1,247 +0,0 @@ -use crate::types::{Link, Logo}; -use rocket::uri; - -pub enum Position { - Left, - Right, -} - -pub struct Image { - pub file: &'static str, - pub position: Position, -} -pub enum ProjectLink { - Available(Link), - NotAvailable, -} - -pub struct ProjectWithImage { - pub name: &'static str, - pub tagline: &'static str, - pub dates: &'static str, - - pub description: Vec<&'static str>, - pub accomplishments: Vec<&'static str>, - pub technologies: Vec<&'static str>, - - pub link: Option<ProjectLink>, - pub logo: Option<Logo>, - pub image: Image, -} - -#[derive(Default)] -pub struct ProjectWithoutImage { - pub name: &'static str, - pub tagline: &'static str, - pub dates: &'static str, - - pub description: Vec<&'static str>, - pub accomplishments: Vec<&'static str>, - pub technologies: Vec<&'static str>, - - pub link: Option<ProjectLink>, - pub logo: Option<Logo>, -} - -pub enum ProjectKind { - WithImage(ProjectWithImage), - WithoutImage((ProjectWithoutImage, ProjectWithoutImage)), -} - -impl ProjectKind { - pub fn new() -> Vec<Self> { - vec![ - ProjectKind::WithoutImage(( - ProjectWithoutImage { - name: "ezidam", - tagline: "Identity and Access Management system", - dates: "January - July 2023", - - description: vec![ - "A simple identity and access management system for SMEs or personal use.", - "Low maintenance required, easy to deploy and to backup.", - ], - accomplishments: vec![ - "Users management", - "Roles management", - "Assign users to roles and the other way around", - "OAuth2 / OIDC applications (code flow)", - "Multi-Factor Authentication (TOTP)", - "Password reset (via email or backup token)", - "Simple administration panel", - "Good security measures for users and administrators", - ], - technologies: vec![ - "Rust", - "SQLite", - "OAuth2 / OIDC", - "TOTP", - "SMTP", - "Docker", - ], - - logo: Some(Logo { - file: "/icons/ezidam.png", - transparent_background: true, - }), - ..Default::default() - }, - ProjectWithoutImage { - name: "pass4thewin", - tagline: "Password manager", - dates: "November 2020 - January 2021", - - description: vec![ - "Port of passwordstore, the standard unix password manager on the Windows platform.", - "Warning! Unfinished command line application, may cause data corruption when using existing passwords.", - ], - accomplishments: vec![ - "Creation of a store", - "List secrets", - "Decrypt secret", - "Insert or generate secrets", - "Edit existing secrets", - "Synchronisation with git", - "TOTP support", - ], - technologies: vec![ - "Windows", - "Rust", - "OpenPGP", - "libgit2", - ], - - link: Some(ProjectLink::Available(Link { - uri: uri!("https://github.com/x4m3/pass4thewin"), - label: "Source code", - })), - ..Default::default() - }, - )), - ProjectKind::WithImage( - - ProjectWithImage{ - name: "NaviaRent", - tagline: "Epitech Innovative Project", - dates: "September 2020 - January 2023", - - description: vec!["A B2B platform helping rentals of standup paddle boards."], - accomplishments: vec![ - "DevOps of all software in the NaviaRent stack", - "Creation of the iOS application", - "Contributions to the Android application", - "Contributions to the backend server", - "Creation and contributions to the web client", - "Server administration, backups", - ], - technologies: vec![ - "NodeJS", - "Angular", - "Kotlin", - "SwiftUI", - "Docker", - "GitLab CI/CD", - "Raspberry Pi", - "ESP32", - ], - - logo: Some(Logo { - file: "/icons/naviarent.png", - transparent_background: false, - }), - image: Image { - file: "/images/naviarent.jpg", - position: Position::Right, - }, - link: Some(ProjectLink::NotAvailable), - }, - ), - ProjectKind::WithoutImage(( - - ProjectWithoutImage{ - name: "epitok", - tagline: "Presence system at Epitech", - dates: "June 2020 - September 2020", - - description: vec![ - "A library and web client to simplify students presence at Epitech.", - "Students are handed a piece of paper with a 6 digits number (called a \"token\") to verify their presence at school events.", - "Teachers use epitok to scan student cards with QR codes on them instead of printing and handing tokens to students.", - ], - accomplishments: vec![ - "Reverse engineering of a partially documented web API", - "Design, conception", - "User experience", - "Improvements based of usage of the application", - ], - technologies: vec![ - "Rust", - "HTML", - "Bootstrap", - "jQuery", - "Docker", - ], - - link: Some(ProjectLink::Available(Link { - uri: uri!("https://github.com/x4m3/epitok"), - label: "Source code", - })), - ..Default::default() - }, - ProjectWithoutImage{ - name: "epi.today", - tagline: "Calendar for Epitech", - dates: "December 2019 - February 2020", - - description: vec![ - "A viewer of the Epitech intranet calendar.", - "Students and teachers glance at their planning without the need to go on the school's intranet.", - ], - accomplishments: vec![], - technologies: vec![ - "TypeScript", - "HTML", - "Bootstrap", - "Docker", - ], - - link: Some(ProjectLink::Available(Link { - uri: uri!("https://github.com/x4m3/epi.today"), - label: "Source code", - })), - ..Default::default() - }, - )), - - ProjectKind::WithImage( - - ProjectWithImage{ - name: "canvas.place", - tagline: "Timelapse", - dates: "April 2017 - January 2020", - - description: vec![ - "canvas.place is a shared place to express creativity.", - "People from all over the world share one single canvas to paint on.", - "I created and maintained a timelapse of the virtual canvas." - ], - accomplishments: vec![], - technologies: vec!["FFmpeg", "Shell scripting", "nginx"], - - logo: Some(Logo { - file: "/icons/canvas.png", - transparent_background: false, - }), - image: Image { - file: "/images/canvas.png", - position: Position::Left, - }, - link: Some(ProjectLink::Available(Link { - uri: uri!("https://timelapse.canvas.place"), - label: "Website", - })), - }, - ) - ] - } -} diff --git a/src/types/talk.rs b/src/types/talk.rs deleted file mode 100644 index d2e985c..0000000 --- a/src/types/talk.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::types::Link; -use rocket::uri; - -pub struct Talk { - pub title: &'static str, - pub date: &'static str, - pub location: &'static str, - pub link: Link, -} - -impl Talk { - pub fn new() -> Vec<Self> { - vec![ - Talk { - title: "Vim", - date: "February 2023", - location: "Epitech Rennes", - link: Link { - uri: uri!("https://philippeloctaux.com/pub/talks/vim.pdf"), - label: "Slides", - }, - }, - Talk { - title: "CLion", - date: "March 2021", - location: "Epitech Rennes", - link: Link { - uri: uri!("https://philippeloctaux.com/pub/talks/clion.pdf"), - label: "Slides", - }, - }, - Talk { - title: "git & devops 2", - date: "February 2021", - location: "Epitech Rennes", - link: Link { - uri: uri!("https://philippeloctaux.com/pub/talks/git-devops2.pdf"), - label: "Slides", - }, - }, - Talk { - title: "pass4thewin", - date: "February 2021", - location: "Epitech Rennes", - link: Link { - uri: uri!("https://philippeloctaux.com/pub/talks/pass4thewin.pdf"), - label: "Slides", - }, - }, - Talk { - title: "git & devops", - date: "May 2020", - location: "Epitech Rennes", - link: Link { - uri: uri!("https://philippeloctaux.com/pub/talks/git-devops.pdf"), - label: "Slides", - }, - }, - Talk { - title: "git gud", - date: "May 2019", - location: "Epitech Rennes", - link: Link { - uri: uri!("https://philippeloctaux.com/pub/talks/git-tek.pdf"), - label: "Slides", - }, - }, - ] - } -} diff --git a/tailwind.config.cjs b/tailwind.config.cjs deleted file mode 100644 index 9836a4e..0000000 --- a/tailwind.config.cjs +++ /dev/null @@ -1,15 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: { - files: ["./templates/**/*.html"], - }, - theme: { - extend: { - height: { - almostscreen: '90vh', - halfscreen: '60vh', - } - }, - }, - plugins: [], -} diff --git a/templates/base.html b/templates/base.html deleted file mode 100644 index 0f81623..0000000 --- a/templates/base.html +++ /dev/null @@ -1,26 +0,0 @@ -<html lang="en"> -<head> - <meta charset="utf-8"/> - <meta name="viewport" content="width=device-width"/> - <title>{{ title|title }} - - - - - - - - - - - -
-{% block container %}{% endblock %} -
-
-
-

© 2015 - {{ year }} Philippe Loctaux

-
-
- - diff --git a/templates/content.html b/templates/content.html deleted file mode 100644 index 2f9c2fb..0000000 --- a/templates/content.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "base.html" %} - -{% block container %} -
-

{{ title }}

-
- {% block content %}{% endblock %} -
-
-{% endblock %} diff --git a/templates/icons/calendar.html b/templates/icons/calendar.html deleted file mode 100644 index 4ab9864..0000000 --- a/templates/icons/calendar.html +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/templates/icons/email.html b/templates/icons/email.html deleted file mode 100644 index 2f043f7..0000000 --- a/templates/icons/email.html +++ /dev/null @@ -1,14 +0,0 @@ - - - diff --git a/templates/icons/github.html b/templates/icons/github.html deleted file mode 100644 index ed83dee..0000000 --- a/templates/icons/github.html +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/templates/icons/link.html b/templates/icons/link.html deleted file mode 100644 index dcbe31f..0000000 --- a/templates/icons/link.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/templates/icons/linkedin.html b/templates/icons/linkedin.html deleted file mode 100644 index 3159cb8..0000000 --- a/templates/icons/linkedin.html +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/templates/icons/location.html b/templates/icons/location.html deleted file mode 100644 index 75c6baa..0000000 --- a/templates/icons/location.html +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/templates/icons/map.html b/templates/icons/map.html deleted file mode 100644 index 7951df7..0000000 --- a/templates/icons/map.html +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/templates/icons/mastodon.html b/templates/icons/mastodon.html deleted file mode 100644 index c871737..0000000 --- a/templates/icons/mastodon.html +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/templates/icons/telegram.html b/templates/icons/telegram.html deleted file mode 100644 index cb50586..0000000 --- a/templates/icons/telegram.html +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/templates/icons/twitter.html b/templates/icons/twitter.html deleted file mode 100644 index 6b2710d..0000000 --- a/templates/icons/twitter.html +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/templates/pages/404.html b/templates/pages/404.html deleted file mode 100644 index 2febece..0000000 --- a/templates/pages/404.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "content.html" %} - -{% block content %} -

This page could not be found.

-{% endblock %} diff --git a/templates/pages/email.html b/templates/pages/email.html deleted file mode 100644 index 6bcec6b..0000000 --- a/templates/pages/email.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "content.html" %} - -{% block content %} -

Send an email if you want to work with me, propose a project idea, or just to say hi!

- - - -

- If you want to encrypt your message, I have a - pgp key at your disposal. -

-

- I also have a Keybase account, but I do not check it often. -

-{% endblock %} diff --git a/templates/pages/root.html b/templates/pages/root.html deleted file mode 100644 index 78240ed..0000000 --- a/templates/pages/root.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "base.html" %} - -{% block container %} -{% include "pages/root/hero-image.html" %} -
- {% include "pages/root/whoami.html" %} -
- {% include "pages/root/www.html" %} - {% include "pages/root/jobs.html" %} - {% include "pages/root/projects.html" %} - {% include "pages/root/talks.html" %} - {% include "pages/root/friends.html" %} -
-
-{% endblock %} \ No newline at end of file diff --git a/templates/pages/root/friends.html b/templates/pages/root/friends.html deleted file mode 100644 index 68dca92..0000000 --- a/templates/pages/root/friends.html +++ /dev/null @@ -1,24 +0,0 @@ -
-

Friends

-

Folks I worked with, or I like what they do.

- - -
diff --git a/templates/pages/root/hero-content.html b/templates/pages/root/hero-content.html deleted file mode 100644 index f73cb16..0000000 --- a/templates/pages/root/hero-content.html +++ /dev/null @@ -1,8 +0,0 @@ -
-
-

Philippe Loctaux

-

- Developer of all sorts. Epitech alumni, class of 2023. -

-
-
diff --git a/templates/pages/root/hero-image.html b/templates/pages/root/hero-image.html deleted file mode 100644 index 3dc97b4..0000000 --- a/templates/pages/root/hero-image.html +++ /dev/null @@ -1,76 +0,0 @@ - - -
- {% let wallpaper_uri -%} - {% match wallpaper %} - {% when Some with (wallpaper) %} - {% let wallpaper_uri = wallpaper.file -%} - {% when None %} - {% let wallpaper_uri = "" -%} - {% endmatch %} - -
- - {% include "pages/root/hero-content.html" %} - - {% match wallpaper %} - {% when Some with (wallpaper) %} - -
- -
-
- {% include "icons/map.html" %} - See more! -
-
- - -
-
- {% include "icons/location.html" %} - - {{ wallpaper.location.precise }} - -
-
- - -
-
- {% include "icons/calendar.html" %} - {{ wallpaper.date }} -
-
-
- {% when None %} - {% endmatch %} -
-
diff --git a/templates/pages/root/jobs.html b/templates/pages/root/jobs.html deleted file mode 100644 index ab0c305..0000000 --- a/templates/pages/root/jobs.html +++ /dev/null @@ -1,64 +0,0 @@ -
-

Professional Experiences

- -
- {% for job in jobs %} - -
- -
- - -
-
- {% let css -%} - {# If css updates make sure to update in both places! #} - {% if job.logo.transparent_background == true -%} - {% let css = "h-16 w-16 rounded-xl mr-4 p-2 bg-white" -%} - {% else -%} - {% let css = "h-16 w-16 rounded-xl mr-4" -%} - {% endif -%} - {{ job.company }} logo -
-
{{ job.company }}
-
{{ job.dates }}
-
-
-

{{ job.title }}

-
- - -
- - {% for desc in job.description %} -

{{ desc }}

- {% endfor %} - - -
-
    - {% for accomplishment in job.accomplishments %} -
  • {{ accomplishment }}
  • - {% endfor %} -
-
- - -
-
- Technologies -
-
    - {% for technology in job.technologies %} -
  • {{ technology }}
  • - {% endfor %} -
-
-
- -
-
- - {% endfor %} -
-
diff --git a/templates/pages/root/project-with-image.html b/templates/pages/root/project-with-image.html deleted file mode 100644 index db236c6..0000000 --- a/templates/pages/root/project-with-image.html +++ /dev/null @@ -1,108 +0,0 @@ -{% let css_image_position -%} -{% let css_image_position_corner -%} -{# If css updates make sure to update in both places! #} -{% match project.image.position %} - -{% when Position::Left %} -{% let css_image_position = "2xl:flex items-stretch justify-between" -%} -{% let css_image_position_corner = "flex w-full 2xl:w-1/2 grow rounded-t-2xl object-cover 2xl:rounded-tr-none 2xl:rounded-l-2xl" -%} - -{% when Position::Right %} -{% let css_image_position = "2xl:flex items-stretch justify-between 2xl:flex-row-reverse" -%} -{% let css_image_position_corner = "flex w-full 2xl:w-1/2 grow rounded-t-2xl object-cover 2xl:rounded-tl-none 2xl:rounded-r-2xl" -%} - -{% endmatch %} -
- - - {{ project.name }} image - -
- - -
-
- {% match project.logo %} - {% when Some with (logo) %} - - {% let css_logo -%} - {# If css updates make sure to update in both places! #} - {% if logo.transparent_background == true -%} - {% let css_logo = "h-16 w-16 rounded-xl mr-4 p-2 bg-white" -%} - {% else -%} - {% let css_logo = "h-16 w-16 rounded-xl mr-4" -%} - {% endif -%} - - - {% when None %} - {% endmatch %} - -
-
{{ project.name }}
-
{{ project.dates }}
-
-
-

{{ project.tagline }}

-
- - -
- - {% for desc in project.description %} -

{{ desc }}

- {% endfor %} - - -
-
    - {% for accomplishment in project.accomplishments %} -
  • {{ accomplishment }}
  • - {% endfor %} -
-
- - -
-
- Technologies -
-
    - {% for technology in project.technologies %} -
  • {{ technology }}
  • - {% endfor %} -
-
-
- - - {% match project.link %} - - {% when Some with (link) %} - {% match link %} - - {% when ProjectLink::Available(project_link) %} - -
- {% include "icons/link.html" %} - {{ project_link.label }} -
-
- - - {% when ProjectLink::NotAvailable %} - - No longer available - - - {% endmatch %} - - {% when None %} - {% endmatch %} - -
- -
diff --git a/templates/pages/root/project-without-image.html b/templates/pages/root/project-without-image.html deleted file mode 100644 index 77c5ceb..0000000 --- a/templates/pages/root/project-without-image.html +++ /dev/null @@ -1,91 +0,0 @@ -
- -
- - -
-
- {% match project.logo %} - {% when Some with (logo) %} - - {% let css_logo -%} - {# If css updates make sure to update in both places! #} - {% if logo.transparent_background == true -%} - {% let css_logo = "h-16 w-16 rounded-xl mr-4 p-2 bg-white" -%} - {% else -%} - {% let css_logo = "h-16 w-16 rounded-xl mr-4" -%} - {% endif -%} - - - {% when None %} - {% endmatch %} - -
-
{{ project.name }}
-
{{ project.dates }}
-
-
-

{{ project.tagline }}

-
- - -
- - {% for desc in project.description %} -

{{ desc }}

- {% endfor %} - - -
-
    - {% for accomplishment in project.accomplishments %} -
  • {{ accomplishment }}
  • - {% endfor %} -
-
- - -
-
- Technologies -
-
    - {% for technology in project.technologies %} -
  • {{ technology }}
  • - {% endfor %} -
-
-
- - - {% match project.link %} - - {% when Some with (link) %} - {% match link %} - - {% when ProjectLink::Available(project_link) %} - -
- {% include "icons/link.html" %} - {{ project_link.label }} -
-
- - - {% when ProjectLink::NotAvailable %} - - No longer available - - - {% endmatch %} - - {% when None %} - {% endmatch %} - -
- -
diff --git a/templates/pages/root/projects.html b/templates/pages/root/projects.html deleted file mode 100644 index 9ca5f57..0000000 --- a/templates/pages/root/projects.html +++ /dev/null @@ -1,23 +0,0 @@ -
-

Projects

- -
- - {% for project in projects %} - - {% match project %} - {% when ProjectKind::WithoutImage((project1, project2)) %} -
- {% let project = project1 -%} - {% include "pages/root/project-without-image.html" %} - - {% let project = project2 -%} - {% include "pages/root/project-without-image.html" %} -
- {% when ProjectKind::WithImage(project) %} - {% include "pages/root/project-with-image.html" %} - {% endmatch %} - - {% endfor %} -
-
diff --git a/templates/pages/root/talks.html b/templates/pages/root/talks.html deleted file mode 100644 index d24b3dd..0000000 --- a/templates/pages/root/talks.html +++ /dev/null @@ -1,46 +0,0 @@ -
-

Talks

-

- Giving a talk is the opportunity to share what I know, and helps me - reduce my fear of public speaking. -

- -
- - {% for talk in talks %} -
- - -

{{talk.title}}

- - -
-
- {% include "icons/calendar.html" %} - {{talk.date}} -
-
- - -
-
- {% include "icons/location.html" %} - {{talk.location}} -
-
- - - -
- {% include "icons/link.html" %} - {{talk.link.label}} -
-
-
- {% endfor %} -
-
diff --git a/templates/pages/root/whoami.html b/templates/pages/root/whoami.html deleted file mode 100644 index 9812f1f..0000000 --- a/templates/pages/root/whoami.html +++ /dev/null @@ -1,39 +0,0 @@ -
-
- Phil -
- -
-

About Phil

-

Developer of all sorts

- -
-

- I got into computer science by learning about the Linux kernel and administrating servers. -

-

- After high school, I became a student at Epitech and learned to - tackle technical concepts and apply them quickly by working - on small projects. -

-

- During my studies at Epitech, I had the opportunity to be a - teacher. My role was to assist students with technical problems - in their projects. -

-

- Now I have experience in software engineering, full-stack web - and mobile development, system administration and CI/CD, as well - as embedded development. -

-

- My goal is to use my knowledge and experience to make software - helping its users accomplish their needs. -

-
-
-
diff --git a/templates/pages/root/www.html b/templates/pages/root/www.html deleted file mode 100644 index 8bc6833..0000000 --- a/templates/pages/root/www.html +++ /dev/null @@ -1,39 +0,0 @@ - diff --git a/templates/pages/wallpapers.html b/templates/pages/wallpapers.html deleted file mode 100644 index 4609e28..0000000 --- a/templates/pages/wallpapers.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "content.html" %} - -{% block content %} -

Pictures I took around the world

- - - - -
- - - -{% for wallpaper in wallpapers %} - -{% endfor %} -{% endblock %}