Compare commits
25 Commits
letterbox-
...
8e06e18546
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e06e18546 | |||
| 38234d4d18 | |||
| f609a3c122 | |||
| 440a630414 | |||
| ebda258750 | |||
| f766b3d529 | |||
| 96d927d416 | |||
| 60543b7e5d | |||
| 97a7bb6083 | |||
| c493857188 | |||
| 21f344b01c | |||
| 78f6d87c03 | |||
| 6edad4e8f2 | |||
| 8b06950cb8 | |||
| 34417131b0 | |||
| d63e72ad35 | |||
| 33c0a106b7 | |||
| 030d1c2ebe | |||
| e386d7e74e | |||
| 55e38e96a0 | |||
| 7f47fe8de6 | |||
| 3889b855a5 | |||
| 2c1c7abf0a | |||
| 9452a2b014 | |||
| 0df97a7b76 |
40
.github/copilot-instructions.md
vendored
Normal file
40
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
# Copilot/AI Agent Instructions for Letterbox
|
||||
|
||||
## Project Overview
|
||||
- **Letterbox** is a Rust monorepo for a mail/newsreader system with a web frontend and a Rocket/GraphQL backend.
|
||||
- Major crates: `server` (backend, Rocket+async-graphql), `web` (Seed-based WASM frontend), `notmuch` (mail integration), `shared` (common types), `procmail2notmuch` (migration/utility).
|
||||
- Data flows: Email/news data is indexed and queried via the backend, exposed to the frontend via GraphQL. SQLx/Postgres is used for persistence. Notmuch and custom SQL are both used for mail storage/search.
|
||||
|
||||
## Key Workflows
|
||||
- **Development**: Use `dev.sh` to launch a tmux session with live-reloading for both frontend (`trunk serve`) and backend (`cargo watch ... run`).
|
||||
- **Build/Release**: Use `just patch|minor|major` for versioned releases (runs SQLx prepare, bumps versions, pushes). `Makefile`'s `release` target does similar steps.
|
||||
- **Frontend**: In `web/`, use `cargo make serve` and `cargo make watch` for local dev. See `web/README.md` for Seed-specific details.
|
||||
- **Backend**: In `server/`, run with `cargo run` or via the tmux/dev.sh workflow. SQL migrations are in `server/migrations/`.
|
||||
|
||||
## Project Conventions & Patterns
|
||||
- **GraphQL**: All API boundaries are defined in `server/src/graphql.rs`. Use the `Query`, `Mutation`, and `Subscription` roots. Types are defined with `async-graphql` derive macros.
|
||||
- **HTML Sanitization**: See `server/src/lib.rs` for custom HTML/CSS sanitization and transformation logic (e.g., `Transformer` trait, `sanitize_html`).
|
||||
- **Tag/Query Parsing**: The `Query` struct in `server/src/lib.rs` parses user queries into filters for notmuch/newsreader/tantivy.
|
||||
- **Shared Types**: Use the `shared` crate for types and helpers shared between frontend and backend.
|
||||
- **Custom SQL**: Raw SQL queries are in `server/sql/`. Use these for complex queries not handled by SQLx macros.
|
||||
- **Feature Flags**: The `tantivy` feature enables full-text search via Tantivy. Check for `#[cfg(feature = "tantivy")]` in backend code.
|
||||
|
||||
## Integration Points
|
||||
- **Notmuch**: Integrated via the `notmuch` crate for mail indexing/search.
|
||||
- **Postgres**: Used for newsreader and other persistent data (see `server/migrations/`).
|
||||
- **GraphQL**: All client-server communication is via GraphQL endpoints defined in the backend.
|
||||
- **Seed/Trunk**: Frontend is built with Seed (Rust/WASM) and served via Trunk.
|
||||
|
||||
## Examples
|
||||
- To add a new GraphQL query, update `server/src/graphql.rs` and expose it in the `QueryRoot`.
|
||||
- To add a new frontend page, add a module in `web/src/` and register it in the Seed app's router.
|
||||
- To run the full dev environment: `./dev.sh` (requires tmux, trunk, cargo-watch, etc.).
|
||||
|
||||
## References
|
||||
- See `web/README.md` for frontend/Seed workflow details.
|
||||
- See `Justfile` and `Makefile` for release/versioning automation.
|
||||
- See `server/src/lib.rs` and `server/src/graphql.rs` for backend architecture and conventions.
|
||||
- See `server/sql/` for custom SQL queries.
|
||||
|
||||
---
|
||||
If any conventions or workflows are unclear, please ask for clarification or check the referenced files for examples.
|
||||
371
Cargo.lock
generated
371
Cargo.lock
generated
@@ -138,9 +138,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.99"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "anymap"
|
||||
@@ -248,7 +248,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"handlebars",
|
||||
"http 1.3.1",
|
||||
"indexmap 2.11.0",
|
||||
"indexmap 2.11.1",
|
||||
"log",
|
||||
"mime",
|
||||
"multer",
|
||||
@@ -316,7 +316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ecdaff7c9cffa3614a9f9999bf9ee4c3078fe3ce4d6a6e161736b56febf2de"
|
||||
dependencies = [
|
||||
"bytes 1.10.1",
|
||||
"indexmap 2.11.0",
|
||||
"indexmap 2.11.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
@@ -751,7 +751,7 @@ checksum = "6b409ed3b3b89db66e81ed7df221d30a1d78c298e719472cac30b21dfdacf9ce"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"derive_more",
|
||||
"semver 1.0.26",
|
||||
"semver 1.0.27",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -856,11 +856,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.1.12"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0b03af37dad7a14518b7691d81acb0f8222604ad3d1b02f6b4bed5188c0cd5"
|
||||
checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -878,7 +878,7 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e63d2780ac94487eb9f1fea7b0d56300abc9eb488800854ca217f102f5caccca"
|
||||
dependencies = [
|
||||
"semver 1.0.26",
|
||||
"semver 1.0.27",
|
||||
"serde",
|
||||
"serde-untagged",
|
||||
"serde-value",
|
||||
@@ -897,7 +897,7 @@ dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"cargo-util-schemas",
|
||||
"semver 1.0.26",
|
||||
"semver 1.0.27",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.16",
|
||||
@@ -905,9 +905,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.36"
|
||||
version = "1.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54"
|
||||
checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
@@ -985,9 +985,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.47"
|
||||
version = "4.5.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931"
|
||||
checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -995,9 +995,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.47"
|
||||
version = "4.5.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6"
|
||||
checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -1348,8 +1348,8 @@ checksum = "30983955a71311b6268dca9ea211b75136367ab3ed7d27d78617587d7d9beebf"
|
||||
dependencies = [
|
||||
"cssparser",
|
||||
"html5ever",
|
||||
"indexmap 2.11.0",
|
||||
"lru 0.16.0",
|
||||
"indexmap 2.11.1",
|
||||
"lru 0.16.1",
|
||||
"precomputed-hash",
|
||||
"rayon",
|
||||
"reqwest",
|
||||
@@ -1716,22 +1716,23 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "erased-serde"
|
||||
version = "0.4.6"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7"
|
||||
checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
"typeid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2076,7 +2077,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi 0.14.4+wasi-0.2.4",
|
||||
"wasi 0.14.5+wasi-0.2.4",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -2306,7 +2307,7 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"indexmap 2.11.0",
|
||||
"indexmap 2.11.1",
|
||||
"slab",
|
||||
"tokio 1.47.1",
|
||||
"tokio-util",
|
||||
@@ -2325,7 +2326,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http 1.3.1",
|
||||
"indexmap 2.11.0",
|
||||
"indexmap 2.11.1",
|
||||
"slab",
|
||||
"tokio 1.47.1",
|
||||
"tokio-util",
|
||||
@@ -2632,9 +2633,9 @@ checksum = "5c3b1f728c459d27b12448862017b96ad4767b1ec2ec5e6434e99f1577f085b8"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.2.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f"
|
||||
checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
@@ -2822,9 +2823,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.63"
|
||||
version = "0.1.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
||||
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
@@ -3020,9 +3021,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.11.0"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
|
||||
checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.5",
|
||||
@@ -3154,21 +3155,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "letterbox-notmuch"
|
||||
version = "0.17.40"
|
||||
source = "sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/"
|
||||
checksum = "d1b8e5a52c809281162a8d3d2997693ee331001b796307d96a62ccd8f7dd7f9b"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mailparse",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.16",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "letterbox-notmuch"
|
||||
version = "0.17.41"
|
||||
version = "0.17.44"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"log",
|
||||
@@ -3181,14 +3168,28 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "letterbox-notmuch"
|
||||
version = "0.17.44"
|
||||
source = "sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/"
|
||||
checksum = "c134bb4ba84b2d44a2e4fce8369edc2c5a32bd7e00a4a32b2c179148ab4251b2"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mailparse",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.16",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "letterbox-procmail2notmuch"
|
||||
version = "0.17.41"
|
||||
version = "0.17.44"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"letterbox-notmuch 0.17.40",
|
||||
"letterbox-shared 0.17.40",
|
||||
"letterbox-notmuch 0.17.44 (sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/)",
|
||||
"letterbox-shared 0.17.44 (sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/)",
|
||||
"serde",
|
||||
"sqlx",
|
||||
"tokio 1.47.1",
|
||||
@@ -3196,7 +3197,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "letterbox-server"
|
||||
version = "0.17.41"
|
||||
version = "0.17.44"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"anyhow",
|
||||
@@ -3219,8 +3220,8 @@ dependencies = [
|
||||
"html-escape",
|
||||
"html2text",
|
||||
"ical",
|
||||
"letterbox-notmuch 0.17.41",
|
||||
"letterbox-shared 0.17.41",
|
||||
"letterbox-notmuch 0.17.44",
|
||||
"letterbox-shared 0.17.44",
|
||||
"linkify",
|
||||
"lol_html",
|
||||
"mailparse",
|
||||
@@ -3246,12 +3247,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "letterbox-shared"
|
||||
version = "0.17.40"
|
||||
source = "sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/"
|
||||
checksum = "ab6394f886c9563d50d759769d429bb83c150ed940d5b50e8f33deab7e236fde"
|
||||
version = "0.17.44"
|
||||
dependencies = [
|
||||
"build-info",
|
||||
"letterbox-notmuch 0.17.40",
|
||||
"letterbox-notmuch 0.17.44",
|
||||
"regex",
|
||||
"serde",
|
||||
"sqlx",
|
||||
@@ -3261,10 +3260,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "letterbox-shared"
|
||||
version = "0.17.41"
|
||||
version = "0.17.44"
|
||||
source = "sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/"
|
||||
checksum = "6b56f113a3e37cf56af9a73c180689661cf5a501262feb45b518b6ddcd0daaaf"
|
||||
dependencies = [
|
||||
"build-info",
|
||||
"letterbox-notmuch 0.17.41",
|
||||
"letterbox-notmuch 0.17.44 (sparse+https://git.z.xinu.tv/api/packages/wathiede/cargo/)",
|
||||
"regex",
|
||||
"serde",
|
||||
"sqlx",
|
||||
@@ -3274,7 +3275,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "letterbox-web"
|
||||
version = "0.17.41"
|
||||
version = "0.17.44"
|
||||
dependencies = [
|
||||
"build-info",
|
||||
"build-info-build",
|
||||
@@ -3286,7 +3287,7 @@ dependencies = [
|
||||
"graphql_client",
|
||||
"human_format",
|
||||
"itertools",
|
||||
"letterbox-shared 0.17.41",
|
||||
"letterbox-shared 0.17.44",
|
||||
"log",
|
||||
"seed",
|
||||
"seed_hooks",
|
||||
@@ -3296,7 +3297,6 @@ dependencies = [
|
||||
"thiserror 2.0.16",
|
||||
"uuid",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
"wasm-sockets",
|
||||
"web-sys",
|
||||
]
|
||||
@@ -3331,26 +3331,6 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "liblzma"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10bf66f4598dc77ff96677c8e763655494f00ff9c1cf79e2eb5bb07bc31f807d"
|
||||
dependencies = [
|
||||
"liblzma-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "liblzma-sys"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01b9596486f6d60c3bbe644c0e1be1aa6ccc472ad630fe8927b456973d7cb736"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.15"
|
||||
@@ -3359,9 +3339,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"libc",
|
||||
@@ -3410,9 +3390,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.4"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
@@ -3474,9 +3454,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86ea4e65087ff52f3862caff188d489f1fab49a0cb09e01b2e3f1a617b10aaed"
|
||||
checksum = "bfe949189f46fabb938b3a9a0be30fdd93fd8a09260da863399a8cf3db756ec8"
|
||||
dependencies = [
|
||||
"hashbrown 0.15.5",
|
||||
]
|
||||
@@ -3493,6 +3473,16 @@ version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
|
||||
|
||||
[[package]]
|
||||
name = "lzma-rust2"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c60a23ffb90d527e23192f1246b14746e2f7f071cb84476dd879071696c18a4a"
|
||||
dependencies = [
|
||||
"crc",
|
||||
"sha2 0.10.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mac"
|
||||
version = "0.1.1"
|
||||
@@ -3651,16 +3641,6 @@ dependencies = [
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minicov"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
@@ -4214,9 +4194,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.8.1"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
|
||||
checksum = "21e0a3a33733faeaf8651dfee72dd0f388f0c8e5ad496a3478fa5a922f49cfa8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror 2.0.16",
|
||||
@@ -4225,9 +4205,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.8.1"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc"
|
||||
checksum = "bc58706f770acb1dbd0973e6530a3cff4746fb721207feb3a8a6064cd0b6c663"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
@@ -4235,9 +4215,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.8.1"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966"
|
||||
checksum = "6d4f36811dfe07f7b8573462465d5cb8965fffc2e71ae377a33aecf14c2c9a2f"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
@@ -4248,9 +4228,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.8.1"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5"
|
||||
checksum = "42919b05089acbd0a5dcd5405fb304d17d1053847b81163d09c4ad18ce8e8420"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"sha2 0.10.9",
|
||||
@@ -4599,7 +4579,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"socket2 0.6.0",
|
||||
"tracing",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5109,20 +5089,20 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver 1.0.26",
|
||||
"semver 1.0.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.8"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5173,9 +5153,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.4"
|
||||
version = "0.103.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
|
||||
checksum = "b5a37813727b78798e53c2bec3f5e8fe12a6d6f8389bf9ca7802add4c9905ad8"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
@@ -5194,22 +5174,13 @@ version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[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.27"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
|
||||
checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5364,11 +5335,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5379,21 +5351,23 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
version = "1.0.226"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-untagged"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34836a629bcbc6f1afdf0907a744870039b1e14c0561cb26094fa683b158eff3"
|
||||
checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058"
|
||||
dependencies = [
|
||||
"erased-serde",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"typeid",
|
||||
]
|
||||
|
||||
@@ -5419,10 +5393,19 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
name = "serde_core"
|
||||
version = "1.0.226"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.226"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5431,24 +5414,26 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.143"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa 1.0.15",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_path_to_error"
|
||||
version = "0.1.17"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a"
|
||||
checksum = "a30a8abed938137c7183c173848e3c9b3517f5e038226849a4ecc9b21a4b4e2a"
|
||||
dependencies = [
|
||||
"itoa 1.0.15",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5698,7 +5683,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hashbrown 0.15.5",
|
||||
"hashlink",
|
||||
"indexmap 2.11.0",
|
||||
"indexmap 2.11.1",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
@@ -6185,15 +6170,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.21.0"
|
||||
version = "3.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e"
|
||||
checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6666,7 +6651,7 @@ version = "0.22.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
dependencies = [
|
||||
"indexmap 2.11.0",
|
||||
"indexmap 2.11.1",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
@@ -6950,9 +6935,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@@ -7077,16 +7062,6 @@ version = "0.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1"
|
||||
|
||||
[[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.2.0"
|
||||
@@ -7127,9 +7102,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.4+wasi-0.2.4"
|
||||
version = "0.14.5+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88a5f4a424faf49c3c2c344f166f0662341d470ea185e939657aaff130f0ec4a"
|
||||
checksum = "a4494f6290a82f5fe584817a676a34b9d6763e8d9d18204009fb31dceca98fd4"
|
||||
dependencies = [
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.0+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
@@ -7211,30 +7195,6 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"minicov",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-bindgen-test-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-sockets"
|
||||
version = "1.0.0"
|
||||
@@ -7328,15 +7288,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
@@ -7345,15 +7296,15 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.2"
|
||||
version = "0.62.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||
checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link 0.1.3",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
"windows-link 0.2.0",
|
||||
"windows-result 0.4.0",
|
||||
"windows-strings 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7397,8 +7348,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e"
|
||||
dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
"windows-result 0.3.4",
|
||||
"windows-strings 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7410,6 +7361,15 @@ dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f"
|
||||
dependencies = [
|
||||
"windows-link 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.4.2"
|
||||
@@ -7419,6 +7379,15 @@ dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda"
|
||||
dependencies = [
|
||||
"windows-link 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
@@ -7837,9 +7806,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "4.6.1"
|
||||
version = "5.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
|
||||
checksum = "2f852905151ac8d4d06fdca66520a661c09730a74c6d4e2b0f27b436b382e532"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"arbitrary",
|
||||
@@ -7850,8 +7819,8 @@ dependencies = [
|
||||
"flate2",
|
||||
"getrandom 0.3.3",
|
||||
"hmac 0.12.1",
|
||||
"indexmap 2.11.0",
|
||||
"liblzma",
|
||||
"indexmap 2.11.1",
|
||||
"lzma-rust2",
|
||||
"memchr",
|
||||
"pbkdf2",
|
||||
"ppmd-rust",
|
||||
|
||||
@@ -8,7 +8,7 @@ authors = ["Bill Thiede <git@xinu.tv>"]
|
||||
edition = "2021"
|
||||
license = "UNLICENSED"
|
||||
publish = ["xinu"]
|
||||
version = "0.17.41"
|
||||
version = "0.17.44"
|
||||
repository = "https://git.z.xinu.tv/wathiede/letterbox"
|
||||
|
||||
[profile.dev]
|
||||
|
||||
@@ -3,4 +3,11 @@
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
]
|
||||
,
|
||||
"packageRules": [
|
||||
{
|
||||
"matchPackageNames": ["wasm-bindgen"],
|
||||
"enabled": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ futures = "0.3.31"
|
||||
headers = "0.4.0"
|
||||
html-escape = "0.2.13"
|
||||
ical = "0.11"
|
||||
letterbox-notmuch = { path = "../notmuch", version = "0.17.41", registry = "xinu" }
|
||||
letterbox-shared = { path = "../shared", version = "0.17.41", registry = "xinu" }
|
||||
letterbox-notmuch = { path = "../notmuch", version = "0.17.44", registry = "xinu" }
|
||||
letterbox-shared = { path = "../shared", version = "0.17.44", registry = "xinu" }
|
||||
linkify = "0.10.0"
|
||||
lol_html = "2.3.0"
|
||||
mailparse = "0.16.1"
|
||||
@@ -56,7 +56,7 @@ urlencoding = "2.1.3"
|
||||
#xtracing = { git = "http://git-private.h.xinu.tv/wathiede/xtracing.git" }
|
||||
#xtracing = { path = "../../xtracing" }
|
||||
xtracing = { version = "0.3.2", registry = "xinu" }
|
||||
zip = "4.3.0"
|
||||
zip = "5.0.0"
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
use askama::Template;
|
||||
use chrono::{Datelike, Local, LocalResult, TimeZone, Utc};
|
||||
use chrono::{Datelike, LocalResult, TimeZone, Utc};
|
||||
use chrono_tz::Tz;
|
||||
use mailparse::{parse_content_type, parse_mail, MailHeader, MailHeaderMap, ParsedMail};
|
||||
use quick_xml::de::from_str as xml_from_str;
|
||||
@@ -1837,39 +1837,42 @@ pub fn render_ical_summary(ical_data: &str) -> Result<String, ServerError> {
|
||||
}
|
||||
}
|
||||
|
||||
// Always use America/Los_Angeles for Google Calendar events if no TZID is present
|
||||
let event_tz: Tz = tzid
|
||||
.as_deref()
|
||||
.unwrap_or("America/Los_Angeles")
|
||||
.parse()
|
||||
.unwrap_or(chrono_tz::America::Los_Angeles);
|
||||
|
||||
// Parse start/end as chrono DateTime
|
||||
let (local_fmt_start, local_fmt_end, event_days, recurrence_display) =
|
||||
if let Some(dtstart) = dtstart {
|
||||
let tz: Tz = tzid
|
||||
.as_deref()
|
||||
.unwrap_or("UTC")
|
||||
.parse()
|
||||
.unwrap_or(chrono_tz::UTC);
|
||||
let fallback = chrono::DateTime::<chrono::Utc>::from_timestamp(0, 0)
|
||||
.map(|dt| dt.with_timezone(&tz))
|
||||
.map(|dt| dt.with_timezone(&event_tz))
|
||||
.unwrap_or_else(|| {
|
||||
tz.with_ymd_and_hms(1970, 1, 1, 0, 0, 0)
|
||||
event_tz
|
||||
.with_ymd_and_hms(1970, 1, 1, 0, 0, 0)
|
||||
.single()
|
||||
.unwrap_or_else(|| tz.timestamp_opt(0, 0).single().unwrap())
|
||||
.unwrap_or_else(|| event_tz.timestamp_opt(0, 0).single().unwrap())
|
||||
});
|
||||
let start = parse_ical_datetime_tz(dtstart, tz).unwrap_or(fallback);
|
||||
let start = parse_ical_datetime_tz(dtstart, event_tz).unwrap_or(fallback);
|
||||
let end = dtend
|
||||
.and_then(|d| parse_ical_datetime_tz(d, tz))
|
||||
.and_then(|d| parse_ical_datetime_tz(d, event_tz))
|
||||
.unwrap_or(start);
|
||||
let local_start = start.with_timezone(&Local);
|
||||
let local_end = end.with_timezone(&Local);
|
||||
// Use the event's TZ for all calendar grid/highlighting logic
|
||||
let allday =
|
||||
dtstart.len() == 8 && (dtend.map(|s| s.len() == 8).unwrap_or(false));
|
||||
let fmt_start = if allday {
|
||||
local_start.format("%a %b %e, %Y").to_string()
|
||||
start.format("%a %b %e, %Y").to_string()
|
||||
} else {
|
||||
local_start.format("%-I:%M %p %a %b %e, %Y").to_string()
|
||||
start.format("%-I:%M %p %a %b %e, %Y").to_string()
|
||||
};
|
||||
let fmt_end = if allday {
|
||||
local_end.format("%a %b %e, %Y").to_string()
|
||||
end.format("%a %b %e, %Y").to_string()
|
||||
} else {
|
||||
local_end.format("%-I:%M %p %a %b %e, %Y").to_string()
|
||||
end.format("%-I:%M %p %a %b %e, %Y").to_string()
|
||||
};
|
||||
// All calendar grid and event_days logic below uses start/end in event's TZ
|
||||
|
||||
// Recurrence support: parse RRULE and generate event_days accordingly
|
||||
let mut days = vec![];
|
||||
@@ -2144,6 +2147,39 @@ fn parse_ical_datetime_tz(dt: &str, tz: Tz) -> Option<chrono::DateTime<Tz>> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn google_calendar_email_thursday_highlights_thursday() {
|
||||
use mailparse::parse_mail;
|
||||
let raw_email = include_str!("../../server/testdata/google-calendar-example-thursday.eml");
|
||||
let parsed = parse_mail(raw_email.as_bytes()).expect("parse_mail");
|
||||
let mut part_addr = vec![];
|
||||
let body = extract_body(&parsed, &mut part_addr).expect("extract_body");
|
||||
let meta = extract_calendar_metadata_from_mail(&parsed, &body);
|
||||
// Assert detection as Google Calendar
|
||||
assert!(meta.is_google_calendar_event);
|
||||
let html = meta.body_html.expect("body_html");
|
||||
|
||||
// Print event date info for debugging
|
||||
for part in parsed.subparts.iter() {
|
||||
if part.ctype.mimetype == TEXT_CALENDAR {
|
||||
if let Ok(ical) = part.get_body() {
|
||||
println!("ICAL data: {}", ical);
|
||||
if let Some(start) = ical.lines().find(|l| l.starts_with("DTSTART:")) {
|
||||
println!("Start date: {}", start);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("Rendered HTML: {}", html);
|
||||
|
||||
// Look for September 11 (Thursday) being highlighted
|
||||
// The calendar should show Sept 11 highlighted with background:#ffd700 and the correct data-event-day
|
||||
assert!(html.contains(r#"data-event-day="2025-09-11""#));
|
||||
assert!(html.contains(r#"background:#ffd700"#));
|
||||
|
||||
// Since 1:00 AM UTC on Friday 9/12 is 6:00 PM PDT on Thursday 9/11, verify times are correct
|
||||
assert!(html.contains("6:00 PM Thu Sep 11, 2025"));
|
||||
}
|
||||
use super::*;
|
||||
#[test]
|
||||
fn google_calendar_email_3_single_event_metadata() {
|
||||
|
||||
@@ -74,13 +74,7 @@
|
||||
{% for week in all_days|batch(7) %}
|
||||
<tr>
|
||||
{% for day in week %}
|
||||
{% if event_days.contains(day) && today.is_some() && today.unwrap() == day %}
|
||||
<td
|
||||
data-event-day="{{ day.format("%Y-%m-%d") }}"
|
||||
style="background:#ffd700; color:#222; font-weight:bold; border:2px solid #2196f3; border-radius:4px; text-align:center; box-shadow:0 0 0 2px #2196f3;">
|
||||
{{ day.day() }}
|
||||
</td>
|
||||
{% elif event_days.contains(day) %}
|
||||
{% if event_days.contains(day) %}
|
||||
<td
|
||||
data-event-day="{{ day.format("%Y-%m-%d") }}"
|
||||
style="background:#ffd700; color:#222; font-weight:bold; border:1px solid #aaa; border-radius:4px; text-align:center;">
|
||||
|
||||
175
server/testdata/google-calendar-example-thursday.eml
vendored
Normal file
175
server/testdata/google-calendar-example-thursday.eml
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
Return-Path: <couchmoney+caf_=gmail=xinu.tv@gmail.com>
|
||||
Delivered-To: bill@xinu.tv
|
||||
Received: from phx.xinu.tv [74.207.253.222]
|
||||
by nixos-01.h.xinu.tv with IMAP (fetchmail-6.5.1)
|
||||
for <wathiede@localhost> (single-drop); Thu, 11 Sep 2025 12:27:35 -0700 (PDT)
|
||||
Received: from phx.xinu.tv
|
||||
by phx.xinu.tv with LMTP
|
||||
id CqRrBqciw2hiKicAJR8clQ
|
||||
(envelope-from <couchmoney+caf_=gmail=xinu.tv@gmail.com>)
|
||||
for <bill@xinu.tv>; Thu, 11 Sep 2025 12:27:35 -0700
|
||||
X-Original-To: gmail@xinu.tv
|
||||
Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=2a00:1450:4864:20::130; helo=mail-lf1-x130.google.com; envelope-from=couchmoney+caf_=gmail=xinu.tv@gmail.com; receiver=xinu.tv
|
||||
Authentication-Results: phx.xinu.tv;
|
||||
dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20230601 header.b=dc+iKaXd;
|
||||
dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=kf8o8wAd
|
||||
Received: from mail-lf1-x130.google.com (mail-lf1-x130.google.com [IPv6:2a00:1450:4864:20::130])
|
||||
by phx.xinu.tv (Postfix) with ESMTPS id D7E2D80037
|
||||
for <gmail@xinu.tv>; Thu, 11 Sep 2025 12:27:33 -0700 (PDT)
|
||||
Received: by mail-lf1-x130.google.com with SMTP id 2adb3069b0e04-55f716e25d9so1141446e87.1
|
||||
for <gmail@xinu.tv>; Thu, 11 Sep 2025 12:27:33 -0700 (PDT)
|
||||
ARC-Seal: i=2; a=rsa-sha256; t=1757618852; cv=pass;
|
||||
d=google.com; s=arc-20240605;
|
||||
b=MZ+1JfQuPR9luCCxiZNUeqSEpjt1vLuM3bTRCaal/W0NBxkCH0y5v9WfPR0KJ2BPb1
|
||||
Rtnt/5ayDtmsLf8l6yTTVsBlFYW70ehqXWMD10MMcDEMvnib4KKDAacGaSmijAK4cYGq
|
||||
FOU9CGNY986OMXMk54TD9NF3fkKDIKcAoh81D6at5/DE3Puuxofq0vZmtmVqQBNKG169
|
||||
REkhcDpkXTMs/4rJpmZwXp2HbjD84avusBwSlYIQUWsBgO4g7THHjoR4Uk56cek9aEds
|
||||
ip8IkTO6KRFe6u8FebQsZ/Q9sSAK3pheMExWFVMha9Y0XhACVOZiV600zRCPS9MNHhYw
|
||||
XEaA==
|
||||
ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605;
|
||||
h=to:from:subject:date:message-id:auto-submitted:sender:reply-to
|
||||
:mime-version:dkim-signature:dkim-signature:delivered-to;
|
||||
bh=mVNsDGUAhSGrAIoTy8PIfvCBxBB4yaBy/VZH8i3gPl4=;
|
||||
fh=WnbwIlqFRbBot/H7TyqablNBDgXRuegsgjC3piothTI=;
|
||||
b=aYMo5f7VI2b4CiAvLELRJ9zM3dF7ZH8FEqmoAtCcfPHrT9kLLCnriuyXG1R6sC3eoR
|
||||
++boT29xoScVroIlfcI77Ty7N5X1fawOABkVDWWt7z5w4WhiesT0klxw5nINj9hnLBiK
|
||||
22nrMevpRpFtmuDO7cle78lSAFZoZuyv+aXCK9RnLKvIm2JuXRrvU8LivxbbpNB4gNl0
|
||||
hE1jsGuZm1SOJ54SRLwwa4HpSiOJV2x2txTtPCzmvE/LZvNESPjfi3Y2u7gaR87OzkNs
|
||||
gNi5Xoc+D908zBsmcYKpUYiQcPL79s3DfNwYFIs/rR8Z2xgaHbFD/YmqRUmCEeNLv7o2
|
||||
RR8g==;
|
||||
darn=xinu.tv
|
||||
ARC-Authentication-Results: i=2; mx.google.com;
|
||||
dkim=pass header.i=@google.com header.s=20230601 header.b=dc+iKaXd;
|
||||
dkim=pass header.i=@gmail.com header.s=20230601 header.b=kf8o8wAd;
|
||||
spf=pass (google.com: domain of tconvertino@gmail.com designates 209.85.220.73 as permitted sender) smtp.mailfrom=tconvertino@gmail.com;
|
||||
dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com;
|
||||
dara=pass header.i=@gmail.com
|
||||
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
|
||||
d=1e100.net; s=20230601; t=1757618852; x=1758223652;
|
||||
h=to:from:subject:date:message-id:auto-submitted:sender:reply-to
|
||||
:mime-version:dkim-signature:dkim-signature:delivered-to
|
||||
:x-forwarded-for:x-forwarded-to:x-gm-message-state:from:to:cc
|
||||
:subject:date:message-id:reply-to;
|
||||
bh=mVNsDGUAhSGrAIoTy8PIfvCBxBB4yaBy/VZH8i3gPl4=;
|
||||
b=GKJkb+LmE79XIMEhHRvoCodKS+GBTOCShzMe06Q+zKxUZFHi6XMg8GqteuXQO9LVbw
|
||||
nPUVN4QO2Hvqch0xzjbc0ryyMOD0u7HqpDUAEZCzamFXIfsX6hZXKLhFqy4YomtsG3os
|
||||
TCOWBGLqwu7KalfOVg2p+csOR68i0mGyBII1sKcL9vUv9kIQJZxQKHGkuIc48cf6tbUB
|
||||
L+mkVbMwXLSbpuTJszPmIVZV5o0K52KN+2QoLcmXGfw0mUOnjNI0oSovdbPg4SSDZ3cw
|
||||
iIsC9vjvtCSFS3pf+Fp807s+Zjh5P6xeSxGU57qhC+HT9kTzIioh5EqKnGqcskDTqrI1
|
||||
uCiQ==
|
||||
X-Forwarded-Encrypted: i=2; AJvYcCUfSSA2sT31daRt2+W7dAD9YPx1gqa4JFpVuqCtxVtjqbKfKhOX/EcDQiECQ4BEWjmAP+IqTQ==@xinu.tv
|
||||
X-Gm-Message-State: AOJu0Ywn7D0BjTaGiM/UFG0WhGuyYGfpLijg+ouhrOaGZzSREyTcRa37
|
||||
XA3bzQ/LKTpzWhhh01GMwnigmELbWdIVr/BeRLVCuJdh+m+JBMgnAjBTIDs9RF3/xfR7rpG7VOB
|
||||
6k+ugF+8QRKB4BcL2t8MvfJD03CkrzuhhvUtFTRHopcSZrkqzh8GOJayq42VveQ==
|
||||
X-Received: by 2002:a05:6512:3b24:b0:55f:6580:818c with SMTP id 2adb3069b0e04-57050fe2fa3mr165340e87.46.1757618851553;
|
||||
Thu, 11 Sep 2025 12:27:31 -0700 (PDT)
|
||||
X-Forwarded-To: gmail@xinu.tv
|
||||
X-Forwarded-For: couchmoney@gmail.com gmail@xinu.tv
|
||||
Delivered-To: couchmoney@gmail.com
|
||||
Received: by 2002:a05:6504:d09:b0:2c3:f6c4:ad72 with SMTP id c9csp3388833lty;
|
||||
Thu, 11 Sep 2025 12:27:29 -0700 (PDT)
|
||||
X-Received: by 2002:a05:6602:36ce:b0:889:b536:779b with SMTP id ca18e2360f4ac-8903378d714mr78653239f.7.1757618849269;
|
||||
Thu, 11 Sep 2025 12:27:29 -0700 (PDT)
|
||||
ARC-Seal: i=1; a=rsa-sha256; t=1757618849; cv=none;
|
||||
d=google.com; s=arc-20240605;
|
||||
b=Ln2bufZfSNhR/NmMPrG2QFdtvupjJtLDQnFvsL8HTPn+Dlrt5ff+6k6Wpupab/5mS7
|
||||
hXjtVD0jnryGUiM5h+SNjxwzNPM3PBoueTpAzzBkjHQqMxJVpspgsGJUVOWAVRBWtWo
|
||||
39qFyoP0vhzGRWDAuAFV+4VDhsvH7GL8lTrZCSMzrngTadmEdJ5haUIQOa50KFUn5HrK
|
||||
1r12gayb+TaGaWfQfDo0Me689T8MQnS0ITUuzgvFxfgHZBz3h+IPnC0hrlhdziGovETo
|
||||
GvHzgCCtiVzu6rop6VMLjLuAYmmT9+jZ3GjSRb+078C9cJR17YpguOC14Cyv4od1Tf7y
|
||||
RFiQ==;
|
||||
dara=google.com
|
||||
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605;
|
||||
h=to:from:subject:date:message-id:auto-submitted:sender:reply-to
|
||||
:mime-version:dkim-signature:dkim-signature;
|
||||
bh=mVNsDGUAhSGrAIoTy8PIfvCBxBB4yaBy/VZH8i3gPl4=;
|
||||
fh=mbzrMIWIgWMC0ni1xEx+ViW4J0RLAdLdPT2cX81nTlk=;
|
||||
b=JRkHr3CKSkCrafdLzBRtaBOGNl3/0ZSTtgubaNXtvhAiIqRqiQYocfLnVM6N/9sH7O
|
||||
byTXYaRoaRLw/35WM+QTFGP3zUGRkM3eO4UVS/utVIss1IVLDjfmZHalqLYl8RokW5br
|
||||
89Z/xYIyjTE7WUdy6uMSrExCNm5VWjO/qcMKsE5s5oDbXdSLaUYxLTurICM3LQksGkCY
|
||||
wiAWaDDqK14+uhEhW5AyEnebDSYhL9U8UadIv+eK6Ng9q1kwOUzxICRQXEyUtnKhaDKJ
|
||||
eZ1Qe1mp1CjCulr+I15fz3VwUJ6W1cv6cytcxPbu4p5GPn2gb2hS1eR81HVTL6V1Sp5G
|
||||
NdDQ==;
|
||||
dara=google.com
|
||||
ARC-Authentication-Results: i=1; mx.google.com;
|
||||
dkim=pass header.i=@google.com header.s=20230601 header.b=dc+iKaXd;
|
||||
dkim=pass header.i=@gmail.com header.s=20230601 header.b=kf8o8wAd;
|
||||
spf=pass (google.com: domain of tconvertino@gmail.com designates 209.85.220.73 as permitted sender) smtp.mailfrom=tconvertino@gmail.com;
|
||||
dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com;
|
||||
dara=pass header.i=@gmail.com
|
||||
Received: from mail-sor-f73.google.com (mail-sor-f73.google.com. [209.85.220.73])
|
||||
by mx.google.com with SMTPS id ca18e2360f4ac-88f2ea1122asor117632339f.3.2025.09.11.12.27.29
|
||||
for <couchmoney@gmail.com>
|
||||
(Google Transport Security);
|
||||
Thu, 11 Sep 2025 12:27:29 -0700 (PDT)
|
||||
Received-SPF: pass (google.com: domain of tconvertino@gmail.com designates 209.85.220.73 as permitted sender) client-ip=209.85.220.73;
|
||||
Authentication-Results: mx.google.com;
|
||||
dkim=pass header.i=@google.com header.s=20230601 header.b=dc+iKaXd;
|
||||
dkim=pass header.i=@gmail.com header.s=20230601 header.b=kf8o8wAd;
|
||||
spf=pass (google.com: domain of tconvertino@gmail.com designates 209.85.220.73 as permitted sender) smtp.mailfrom=tconvertino@gmail.com;
|
||||
dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com;
|
||||
dara=pass header.i=@gmail.com
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
|
||||
d=google.com; s=20230601; t=1757618849; x=1758223649; dara=google.com;
|
||||
h=to:from:subject:date:message-id:auto-submitted:sender:reply-to
|
||||
:mime-version:from:to:cc:subject:date:message-id:reply-to;
|
||||
bh=mVNsDGUAhSGrAIoTy8PIfvCBxBB4yaBy/VZH8i3gPl4=;
|
||||
b=dc+iKaXdFyqu6K0MIgk848QuwpQXvwzwlEVkxmjuCWvn9DzanMbYn5QJRyRTKilRna
|
||||
BZ7gJSPriHUHcJd4fVKgGuCaQg0TxenCwm+0R64oB1xcDLfonayo/nCrFqEcCLHNmi7x
|
||||
lTyWGJ0rLw6nKazxtcCdIbDhVgiE7/fXNI89w6XFp6pcKLl48yFIoCG1f6uY4iQ7QqNU
|
||||
hLHzjmlzjTi58xFLao7SizZ0lr7E5cHXKHp1Ls/hkDzzcY0Y+O5+3r+NQw4MtpHTcY6/
|
||||
kQlg6OhyMx8PTu4cuepQKXLHV4aFaNJbDQTp8wew4xPIgi7pm2p6hb6C3GgwY6ptOvLd
|
||||
wuag==
|
||||
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
|
||||
d=gmail.com; s=20230601; t=1757618849; x=1758223649; dara=google.com;
|
||||
h=to:from:subject:date:message-id:auto-submitted:sender:reply-to
|
||||
:mime-version:from:to:cc:subject:date:message-id:reply-to;
|
||||
bh=mVNsDGUAhSGrAIoTy8PIfvCBxBB4yaBy/VZH8i3gPl4=;
|
||||
b=kf8o8wAd5DSU/NC7SDiuIoohCu+/7wTjWyQqDYbBjUFGaBaYdj6aD5JWNQ1KEA2W8o
|
||||
E+Qy2ymyrzodKa1eOsQX2UDAYKOKpdxMWvx1u19+SC3Dp8DP4puRMrL2ObiSEMLCuOvz
|
||||
Mxmkd+ZUP72EhVuQwK1iSm04/cjQaMsSiPhvSBaxXMaaarwlKeOoCoIo+qC/Z9emiBBv
|
||||
Gk0sQcLA+CByvsxuvD9GInSA0rdoZ0ijhSb0Y475Hieam1QQqy/fhe8lgujzhXNFoIbR
|
||||
5EA9GE0VV9PDoNanaT+u954YeOFBL2YZ5gm2gHltw8tBI98LKnC42Pa3qyMznBa2dI2Q
|
||||
A0RQ==
|
||||
X-Google-Smtp-Source: AGHT+IGmC5/03nTVMeYJBoq1R/BiA19iH0DFaZyyImB3W8mtgjdn+XqIFK1fC8aTwWRXQmsr71Xo0cmkgx6hjPvicQ/d
|
||||
MIME-Version: 1.0
|
||||
X-Received: by 2002:a05:6602:380d:b0:887:4c93:f12c with SMTP id
|
||||
ca18e2360f4ac-8903596aca3mr58994639f.17.1757618848817; Thu, 11 Sep 2025
|
||||
12:27:28 -0700 (PDT)
|
||||
Reply-To: tconvertino@gmail.com
|
||||
Sender: Google Calendar <calendar-notification@google.com>
|
||||
Auto-Submitted: auto-generated
|
||||
Message-ID: <calendar-01d5e8a0-fad7-450b-9758-a16472bf2aa8@google.com>
|
||||
Date: Thu, 11 Sep 2025 19:27:28 +0000
|
||||
Subject: Canceled event: Scout Babysits @ Thu Sep 11, 2025 6pm - 9pm (PDT) (Family)
|
||||
From: tconvertino@gmail.com
|
||||
To: couchmoney@gmail.com
|
||||
Content-Type: multipart/mixed; boundary="000000000000226b77063e8b878d"
|
||||
|
||||
--000000000000226b77063e8b878d
|
||||
Content-Type: text/calendar; charset="UTF-8"; method=CANCEL
|
||||
Content-Transfer-Encoding: 7bit
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Google Inc//Google Calendar 70.9054//EN
|
||||
VERSION:2.0
|
||||
CALSCALE:GREGORIAN
|
||||
METHOD:CANCEL
|
||||
X-GOOGLE-CALID:g66m0feuqsao8l1c767pvvcg4k@group.calendar.google.com
|
||||
BEGIN:VEVENT
|
||||
DTSTART:20250912T010000Z
|
||||
DTEND:20250912T040000Z
|
||||
DTSTAMP:20250911T192728Z
|
||||
UID:4ang6172d1t7782sn2hmi30fgi@google.com
|
||||
CREATED:20250901T224707Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20250911T192728Z
|
||||
LOCATION:
|
||||
SEQUENCE:1
|
||||
STATUS:CANCELLED
|
||||
SUMMARY:Scout Babysits
|
||||
TRANSP:OPAQUE
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
--000000000000226b77063e8b878d--
|
||||
@@ -12,7 +12,7 @@ version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
build-info = "0.0.41"
|
||||
letterbox-notmuch = { path = "../notmuch", version = "0.17.41", registry = "xinu" }
|
||||
letterbox-notmuch = { path = "../notmuch", version = "0.17.44", registry = "xinu" }
|
||||
regex = "1.11.1"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
sqlx = "0.8.5"
|
||||
|
||||
@@ -12,7 +12,7 @@ version.workspace = true
|
||||
build-info-build = "0.0.41"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.50"
|
||||
#wasm-bindgen-test = "0.3.50"
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1.7"
|
||||
@@ -33,7 +33,7 @@ wasm-bindgen = "=0.2.100"
|
||||
uuid = { version = "1.16.0", features = [
|
||||
"js",
|
||||
] } # direct dep to set js feature, prevents Rng issues
|
||||
letterbox-shared = { path = "../shared/", version = "0.17.41", registry = "xinu" }
|
||||
letterbox-shared = { path = "../shared/", version = "0.17.44", registry = "xinu" }
|
||||
seed_hooks = { version = "0.4.1", registry = "xinu" }
|
||||
strum_macros = "0.27.1"
|
||||
gloo-console = "0.3.0"
|
||||
|
||||
@@ -1058,6 +1058,8 @@ fn message_render(msg: &ShowThreadQueryThreadOnEmailThreadMessages, open: bool)
|
||||
},
|
||||
) => div![
|
||||
C!["view-part-text-html"],
|
||||
// If there isn't any HTML tags, treat more like plain text
|
||||
IF!(!(contents.contains('<') && contents.contains('>')) => C!["whitespace-pre-line"]),
|
||||
raw![contents],
|
||||
IF!(!msg.attachments.is_empty() => render_attachements(&msg.attachments)),
|
||||
view_content_tree(&content_tree),
|
||||
|
||||
Reference in New Issue
Block a user