diff --git a/Cargo.lock b/Cargo.lock
index 938ce6e..e3f6632 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8,12 +8,14 @@ version = "0.1.0"
dependencies = [
"aardvark-doc",
"aardvark-node",
- "ashpd",
+ "ashpd 0.9.2",
"futures-util",
"gettext-rs",
"gtk4",
"libadwaita",
+ "oo7",
"sourceview5",
+ "thiserror 2.0.12",
"tracing",
"tracing-subscriber",
]
@@ -30,7 +32,6 @@ dependencies = [
"loro",
"p2panda-core",
"thiserror 2.0.12",
- "tokio",
"tracing",
]
@@ -40,6 +41,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
+ "chrono",
"ciborium",
"p2panda-core",
"p2panda-discovery",
@@ -48,6 +50,7 @@ dependencies = [
"p2panda-stream",
"p2panda-sync",
"serde",
+ "sqlx",
"tokio",
"tokio-stream",
"tracing",
@@ -177,7 +180,26 @@ dependencies = [
"serde_repr",
"tracing",
"url",
- "zbus",
+ "zbus 4.4.0",
+]
+
+[[package]]
+name = "ashpd"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df"
+dependencies = [
+ "async-fs",
+ "async-net",
+ "enumflags2",
+ "futures-channel",
+ "futures-util",
+ "rand 0.9.0",
+ "serde",
+ "serde_repr",
+ "tracing",
+ "url",
+ "zbus 5.6.0",
]
[[package]]
@@ -373,6 +395,15 @@ dependencies = [
"syn 2.0.100",
]
+[[package]]
+name = "atoi"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "atomic-polyfill"
version = "1.0.3"
@@ -406,16 +437,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
-name = "backoff"
-version = "0.4.0"
+name = "backon"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
+checksum = "fd0b50b1b78dbadd44ab18b3c794e496f3a139abb9fbc27d9c94c4eebbb96496"
dependencies = [
- "futures-core",
- "getrandom 0.2.15",
- "instant",
- "pin-project-lite",
- "rand 0.8.5",
+ "fastrand",
+ "gloo-timers",
+ "tokio",
]
[[package]]
@@ -468,6 +497,9 @@ name = "bitflags"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+dependencies = [
+ "serde",
+]
[[package]]
name = "bitmaps"
@@ -480,9 +512,9 @@ dependencies = [
[[package]]
name = "blake3"
-version = "1.6.1"
+version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "675f87afced0413c9bb02843499dbbd3882a237645883f71a2b59644a6d2f753"
+checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0"
dependencies = [
"arrayref",
"arrayvec",
@@ -625,8 +657,10 @@ checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
dependencies = [
"android-tzdata",
"iana-time-zone",
+ "js-sys",
"num-traits",
"serde",
+ "wasm-bindgen",
"windows-link",
]
@@ -789,6 +823,15 @@ dependencies = [
"crossbeam-utils",
]
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
+dependencies = [
+ "crossbeam-utils",
+]
+
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
@@ -922,6 +965,7 @@ checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
dependencies = [
"const-oid",
"der_derive",
+ "pem-rfc7468",
"zeroize",
]
@@ -1010,6 +1054,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
+ "const-oid",
"crypto-common",
"subtle",
]
@@ -1045,6 +1090,12 @@ dependencies = [
"litrs",
]
+[[package]]
+name = "dotenvy"
+version = "0.15.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
+
[[package]]
name = "ed25519"
version = "2.2.3"
@@ -1076,6 +1127,9 @@ name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+dependencies = [
+ "serde",
+]
[[package]]
name = "embedded-io"
@@ -1189,6 +1243,17 @@ dependencies = [
"windows-sys 0.59.0",
]
+[[package]]
+name = "etcetera"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
+dependencies = [
+ "cfg-if",
+ "home",
+ "windows-sys 0.48.0",
+]
+
[[package]]
name = "event-listener"
version = "5.4.0"
@@ -1268,6 +1333,21 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
[[package]]
name = "form_urlencoded"
version = "1.2.1"
@@ -1347,6 +1427,17 @@ dependencies = [
"futures-util",
]
+[[package]]
+name = "futures-intrusive"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
+dependencies = [
+ "futures-core",
+ "lock_api",
+ "parking_lot",
+]
+
[[package]]
name = "futures-io"
version = "0.3.31"
@@ -1655,6 +1746,18 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
+[[package]]
+name = "gloo-timers"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+]
+
[[package]]
name = "gobject-sys"
version = "0.20.9"
@@ -1836,6 +1939,15 @@ dependencies = [
"foldhash",
]
+[[package]]
+name = "hashlink"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
+dependencies = [
+ "hashbrown 0.15.2",
+]
+
[[package]]
name = "heapless"
version = "0.7.17"
@@ -1889,35 +2001,10 @@ dependencies = [
[[package]]
name = "hickory-proto"
-version = "0.24.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248"
-dependencies = [
- "async-trait",
- "cfg-if",
- "data-encoding",
- "enum-as-inner 0.6.1",
- "futures-channel",
- "futures-io",
- "futures-util",
- "idna",
- "ipnet",
- "once_cell",
- "rand 0.8.5",
- "thiserror 1.0.69",
- "tinyvec",
- "tokio",
- "tracing",
- "url",
-]
-
-[[package]]
-name = "hickory-proto"
-version = "0.25.0-alpha.5"
+version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d00147af6310f4392a31680db52a3ed45a2e0f68eb18e8c3fe5537ecc96d9e2"
+checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502"
dependencies = [
- "async-recursion",
"async-trait",
"cfg-if",
"data-encoding",
@@ -1929,6 +2016,7 @@ dependencies = [
"ipnet",
"once_cell",
"rand 0.9.0",
+ "ring",
"thiserror 2.0.12",
"tinyvec",
"tokio",
@@ -1938,13 +2026,13 @@ dependencies = [
[[package]]
name = "hickory-resolver"
-version = "0.25.0-alpha.5"
+version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5762f69ebdbd4ddb2e975cd24690bf21fe6b2604039189c26acddbc427f12887"
+checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a"
dependencies = [
"cfg-if",
"futures-util",
- "hickory-proto 0.25.0-alpha.5",
+ "hickory-proto",
"ipconfig",
"moka",
"once_cell",
@@ -1957,6 +2045,15 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "hkdf"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
+dependencies = [
+ "hmac",
+]
+
[[package]]
name = "hmac"
version = "0.12.1"
@@ -1982,6 +2079,15 @@ version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a8575493d277c9092b988c780c94737fb9fd8651a1001e16bee3eccfc1baedb"
+[[package]]
+name = "home"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
[[package]]
name = "hostname"
version = "0.4.0"
@@ -2369,14 +2475,14 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]]
name = "iroh"
-version = "0.33.0"
+version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4ffd6af2e000f04972068c0318e0d8fa90ee9cfcb2bc6124db38591500e0278"
+checksum = "37432887a6836e7a832fccb121b5f0ee6cd953c506f99b0278bdbedf8dee0e88"
dependencies = [
"aead",
"anyhow",
"atomic-waker",
- "backoff",
+ "backon",
"bytes",
"cfg_aliases",
"concurrent-queue",
@@ -2392,14 +2498,13 @@ dependencies = [
"instant",
"iroh-base",
"iroh-metrics",
- "iroh-net-report",
"iroh-quinn",
"iroh-quinn-proto",
"iroh-quinn-udp",
"iroh-relay",
"n0-future",
"netdev",
- "netwatch 0.3.0",
+ "netwatch",
"pin-project",
"pkarr",
"portmapper",
@@ -2413,6 +2518,7 @@ dependencies = [
"smallvec",
"strum",
"stun-rs",
+ "surge-ping",
"thiserror 2.0.12",
"time",
"tokio",
@@ -2428,15 +2534,14 @@ dependencies = [
[[package]]
name = "iroh-base"
-version = "0.33.0"
+version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "011d271a95b41218d22bdaf3352f29ef1dd7d6be644ca8543941655bec5f3d35"
+checksum = "3cd952d9e25e521d6aeb5b79f2fe32a0245da36aae3569e50f6010b38a5f0923"
dependencies = [
"curve25519-dalek",
"data-encoding",
"derive_more",
"ed25519-dalek",
- "getrandom 0.2.15",
"postcard",
"rand_core 0.6.4",
"serde",
@@ -2459,9 +2564,9 @@ dependencies = [
[[package]]
name = "iroh-gossip"
-version = "0.33.0"
+version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3d4c7e330bf3d29576d443003e31a2d30d97b29ee13521af2634926d831c01d"
+checksum = "71a9d638618fb6a4dac68d59cb694774478ea039bc9afca4709e242726591be1"
dependencies = [
"anyhow",
"async-channel",
@@ -2490,9 +2595,9 @@ dependencies = [
[[package]]
name = "iroh-metrics"
-version = "0.31.0"
+version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "571d177e20f0848a643a2c0f662be0e08968f8743b0776941f83a2152b87a180"
+checksum = "c0f7cd1ffe3b152a5f4f4c1880e01e07d96001f20e02cc143cb7842987c616b3"
dependencies = [
"erased_set",
"serde",
@@ -2501,35 +2606,6 @@ dependencies = [
"tracing",
]
-[[package]]
-name = "iroh-net-report"
-version = "0.33.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d2652f42eadc63458e36c0a422569f338639dc0b5bb469db0eb4a382b4e295c"
-dependencies = [
- "anyhow",
- "bytes",
- "cfg_aliases",
- "derive_more",
- "hickory-resolver",
- "iroh-base",
- "iroh-metrics",
- "iroh-quinn",
- "iroh-relay",
- "n0-future",
- "netwatch 0.3.0",
- "portmapper",
- "rand 0.8.5",
- "reqwest",
- "rustls",
- "surge-ping",
- "thiserror 2.0.12",
- "tokio",
- "tokio-util",
- "tracing",
- "url",
-]
-
[[package]]
name = "iroh-quinn"
version = "0.13.0"
@@ -2588,9 +2664,9 @@ dependencies = [
[[package]]
name = "iroh-relay"
-version = "0.33.0"
+version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c930ccc4dfd0196b531344e3d0f83a0f82c45b170406e04a2491cba571faec5b"
+checksum = "40d2d7b50d999922791c6c14c25e13f55711e182618cb387bafa0896ffe0b930"
dependencies = [
"anyhow",
"bytes",
@@ -2691,6 +2767,9 @@ name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+dependencies = [
+ "spin",
+]
[[package]]
name = "leb128"
@@ -2735,6 +2814,23 @@ version = "0.2.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+[[package]]
+name = "libm"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a25169bd5913a4b437588a7e3d127cd6e90127b60e0ffbd834a38f1599e016b8"
+
+[[package]]
+name = "libsqlite3-sys"
+version = "0.30.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
+dependencies = [
+ "cc",
+ "pkg-config",
+ "vcpkg",
+]
+
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
@@ -2810,15 +2906,17 @@ dependencies = [
"cfg-if",
"generator 0.8.4",
"scoped-tls",
+ "serde",
+ "serde_json",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "loro"
-version = "1.4.6"
+version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc3e0312d28cdec90cbf31fb5f4aa1ff15d4d30218845fe771f3cba5545e1056"
+checksum = "c2d4331ca4f9279ef08e9367b1e0f2be184ae411c195bb8ed2058b347fcf2351"
dependencies = [
"enum-as-inner 0.6.1",
"fxhash",
@@ -2832,9 +2930,9 @@ dependencies = [
[[package]]
name = "loro-common"
-version = "1.4.6"
+version = "1.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8a7a44070a295743eea2182f2ea82bc504b182a2b0d791cbada83d9472a1baf"
+checksum = "3068d052d47fccaa42f794a8f088670bcf72225470bec613778407a56e9f57a9"
dependencies = [
"arbitrary",
"enum-as-inner 0.6.1",
@@ -2850,9 +2948,9 @@ dependencies = [
[[package]]
name = "loro-delta"
-version = "1.3.1"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3a4afa7e170424f830726868613b4510b37f985a2287b8fdeb7ef6be7e2ad36"
+checksum = "26b837794343ce8cf7992f23b5d75495d0cff583203e3b3ca6fdfbaa2132c5dc"
dependencies = [
"arrayvec",
"enum-as-inner 0.5.1",
@@ -2862,9 +2960,9 @@ dependencies = [
[[package]]
name = "loro-internal"
-version = "1.4.6"
+version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82e6dc34f0581659062db756afc1c7c98a3e66214cf6460aa45df447e9b95860"
+checksum = "6dff6d085f2d2b679c8a45e94a3879229b48321fd9262fc5012c907623f8a13f"
dependencies = [
"append-only-bytes",
"arref",
@@ -2879,6 +2977,7 @@ dependencies = [
"im",
"itertools 0.12.1",
"leb128",
+ "loom 0.7.2",
"loro-common",
"loro-delta",
"loro-kv-store",
@@ -2897,15 +2996,17 @@ dependencies = [
"serde_json",
"smallvec",
"thiserror 1.0.69",
+ "thread_local",
"tracing",
+ "wasm-bindgen",
"xxhash-rust",
]
[[package]]
name = "loro-kv-store"
-version = "1.4.6"
+version = "1.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a8095ac01b61d14093a0a09bd8e4862d782e81162457cb718308e42704385f7"
+checksum = "e062c6054447e21d4503636f0de7eef0d59af8595d14d3d74013888fe03bbd86"
dependencies = [
"bytes",
"ensure-cov",
@@ -2982,6 +3083,16 @@ dependencies = [
"regex-automata 0.1.10",
]
+[[package]]
+name = "md-5"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
+dependencies = [
+ "cfg-if",
+ "digest",
+]
+
[[package]]
name = "md5"
version = "0.7.0"
@@ -3181,57 +3292,22 @@ dependencies = [
[[package]]
name = "netwatch"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "304c0c1b348830b016039f2cb1c5ac8217084a78875262c5594925dd08aa77fc"
-dependencies = [
- "anyhow",
- "atomic-waker",
- "bytes",
- "derive_more",
- "futures-lite",
- "futures-sink",
- "futures-util",
- "iroh-quinn-udp",
- "libc",
- "netdev",
- "netlink-packet-core",
- "netlink-packet-route 0.19.0",
- "netlink-sys",
- "once_cell",
- "rtnetlink 0.13.1",
- "rtnetlink 0.14.1",
- "serde",
- "socket2",
- "thiserror 2.0.12",
- "time",
- "tokio",
- "tokio-util",
- "tracing",
- "windows 0.58.0",
- "wmi",
-]
-
-[[package]]
-name = "netwatch"
-version = "0.3.0"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64da82edf903649e6cb6a77b5a6f7fe01387d8865065d411d139018510880302"
+checksum = "0b7879c2cfdf30d92f2be89efa3169b3d78107e3ab7f7b9a37157782569314e1"
dependencies = [
- "anyhow",
"atomic-waker",
"bytes",
+ "cfg_aliases",
"derive_more",
- "futures-lite",
- "futures-sink",
- "futures-util",
"iroh-quinn-udp",
+ "js-sys",
"libc",
+ "n0-future",
"netdev",
"netlink-packet-core",
"netlink-packet-route 0.19.0",
"netlink-sys",
- "once_cell",
"rtnetlink 0.13.1",
"rtnetlink 0.14.1",
"serde",
@@ -3241,7 +3317,9 @@ dependencies = [
"tokio",
"tokio-util",
"tracing",
- "windows 0.58.0",
+ "web-sys",
+ "windows 0.59.0",
+ "windows-result 0.3.1",
"wmi",
]
@@ -3337,10 +3415,28 @@ dependencies = [
]
[[package]]
-name = "num-complex"
-version = "0.4.6"
+name = "num-bigint-dig"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
+checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
+dependencies = [
+ "byteorder",
+ "lazy_static",
+ "libm",
+ "num-integer",
+ "num-iter",
+ "num-traits",
+ "rand 0.8.5",
+ "serde",
+ "smallvec",
+ "zeroize",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
dependencies = [
"num-traits",
]
@@ -3389,6 +3485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
+ "libm",
]
[[package]]
@@ -3464,6 +3561,37 @@ name = "once_cell"
version = "1.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
+dependencies = [
+ "critical-section",
+ "portable-atomic",
+]
+
+[[package]]
+name = "oo7"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cb23d3ec3527d65a83be1c1795cb883c52cfa57147d42acc797127df56fc489"
+dependencies = [
+ "ashpd 0.11.0",
+ "async-fs",
+ "async-io",
+ "async-lock",
+ "blocking",
+ "endi",
+ "futures-lite",
+ "futures-util",
+ "getrandom 0.3.1",
+ "num",
+ "num-bigint-dig",
+ "openssl",
+ "rand 0.9.0",
+ "serde",
+ "tracing",
+ "zbus 5.6.0",
+ "zbus_macros 5.6.0",
+ "zeroize",
+ "zvariant 5.5.1",
+]
[[package]]
name = "opaque-debug"
@@ -3471,12 +3599,50 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
+[[package]]
+name = "openssl"
+version = "0.10.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
+dependencies = [
+ "bitflags 2.9.0",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
[[package]]
name = "openssl-probe"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
+[[package]]
+name = "openssl-sys"
+version = "0.9.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
[[package]]
name = "ordered-stream"
version = "0.2.0"
@@ -3495,8 +3661,8 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "p2panda-core"
-version = "0.3.0"
-source = "git+https://github.com/p2panda/p2panda?rev=f3a016324b69beac45cf20a792fe6890cb1a21e3#f3a016324b69beac45cf20a792fe6890cb1a21e3"
+version = "0.3.1"
+source = "git+https://github.com/p2panda/p2panda?rev=085a57206aeae70142176c0777ed2febc7b98664#085a57206aeae70142176c0777ed2febc7b98664"
dependencies = [
"blake3",
"ciborium",
@@ -3505,23 +3671,23 @@ dependencies = [
"rand 0.8.5",
"serde",
"serde_bytes",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
]
[[package]]
name = "p2panda-discovery"
-version = "0.3.0"
-source = "git+https://github.com/p2panda/p2panda?rev=f3a016324b69beac45cf20a792fe6890cb1a21e3#f3a016324b69beac45cf20a792fe6890cb1a21e3"
+version = "0.3.1"
+source = "git+https://github.com/p2panda/p2panda?rev=085a57206aeae70142176c0777ed2febc7b98664#085a57206aeae70142176c0777ed2febc7b98664"
dependencies = [
"anyhow",
"base32",
"flume",
"futures-buffered",
"futures-lite",
- "hickory-proto 0.24.4",
+ "hickory-proto",
"iroh",
"iroh-base",
- "netwatch 0.2.0",
+ "netwatch",
"socket2",
"tokio",
"tokio-util",
@@ -3530,8 +3696,8 @@ dependencies = [
[[package]]
name = "p2panda-net"
-version = "0.3.0"
-source = "git+https://github.com/p2panda/p2panda?rev=f3a016324b69beac45cf20a792fe6890cb1a21e3#f3a016324b69beac45cf20a792fe6890cb1a21e3"
+version = "0.3.1"
+source = "git+https://github.com/p2panda/p2panda?rev=085a57206aeae70142176c0777ed2febc7b98664#085a57206aeae70142176c0777ed2febc7b98664"
dependencies = [
"anyhow",
"async-trait",
@@ -3542,13 +3708,13 @@ dependencies = [
"iroh-base",
"iroh-gossip",
"iroh-quinn",
- "netwatch 0.2.0",
+ "netwatch",
"p2panda-core",
"p2panda-discovery",
"p2panda-sync",
"rand 0.8.5",
"serde",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
"tokio",
"tokio-stream",
"tokio-util",
@@ -3557,18 +3723,21 @@ dependencies = [
[[package]]
name = "p2panda-store"
-version = "0.3.0"
-source = "git+https://github.com/p2panda/p2panda?rev=f3a016324b69beac45cf20a792fe6890cb1a21e3#f3a016324b69beac45cf20a792fe6890cb1a21e3"
+version = "0.3.1"
+source = "git+https://github.com/p2panda/p2panda?rev=085a57206aeae70142176c0777ed2febc7b98664#085a57206aeae70142176c0777ed2febc7b98664"
dependencies = [
+ "ciborium",
+ "hex",
"p2panda-core",
+ "sqlx",
"thiserror 2.0.12",
"trait-variant",
]
[[package]]
name = "p2panda-stream"
-version = "0.3.0"
-source = "git+https://github.com/p2panda/p2panda?rev=f3a016324b69beac45cf20a792fe6890cb1a21e3#f3a016324b69beac45cf20a792fe6890cb1a21e3"
+version = "0.3.1"
+source = "git+https://github.com/p2panda/p2panda?rev=085a57206aeae70142176c0777ed2febc7b98664#085a57206aeae70142176c0777ed2febc7b98664"
dependencies = [
"ciborium",
"futures-channel",
@@ -3577,20 +3746,20 @@ dependencies = [
"p2panda-store",
"pin-project",
"pin-utils",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
]
[[package]]
name = "p2panda-sync"
-version = "0.3.0"
-source = "git+https://github.com/p2panda/p2panda?rev=f3a016324b69beac45cf20a792fe6890cb1a21e3#f3a016324b69beac45cf20a792fe6890cb1a21e3"
+version = "0.3.1"
+source = "git+https://github.com/p2panda/p2panda?rev=085a57206aeae70142176c0777ed2febc7b98664#085a57206aeae70142176c0777ed2febc7b98664"
dependencies = [
"async-trait",
"futures",
"p2panda-core",
"p2panda-store",
"serde",
- "thiserror 1.0.69",
+ "thiserror 2.0.12",
"tokio",
"tokio-util",
]
@@ -3664,6 +3833,15 @@ dependencies = [
"serde",
]
+[[package]]
+name = "pem-rfc7468"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
+dependencies = [
+ "base64ct",
+]
+
[[package]]
name = "percent-encoding"
version = "2.3.1"
@@ -3782,6 +3960,17 @@ dependencies = [
"z32",
]
+[[package]]
+name = "pkcs1"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
+dependencies = [
+ "der",
+ "pkcs8",
+ "spki",
+]
+
[[package]]
name = "pkcs8"
version = "0.10.2"
@@ -3874,11 +4063,10 @@ checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
[[package]]
name = "portmapper"
-version = "0.3.1"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5469b29e6ce2a27bfc9382720b5f0768993afec9e53b133d8248c8b09406156a"
+checksum = "247dcb75747c53cc433d6d8963a064187eec4a676ba13ea33143f1c9100e754f"
dependencies = [
- "anyhow",
"base64",
"bytes",
"derive_more",
@@ -3887,7 +4075,7 @@ dependencies = [
"igd-next",
"iroh-metrics",
"libc",
- "netwatch 0.3.0",
+ "netwatch",
"num_enum",
"rand 0.8.5",
"serde",
@@ -4295,6 +4483,26 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "rsa"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b"
+dependencies = [
+ "const-oid",
+ "digest",
+ "num-bigint-dig",
+ "num-integer",
+ "num-traits",
+ "pkcs1",
+ "pkcs8",
+ "rand_core 0.6.4",
+ "signature",
+ "spki",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "rtnetlink"
version = "0.13.1"
@@ -4591,9 +4799,9 @@ dependencies = [
[[package]]
name = "serde_columnar"
-version = "0.3.10"
+version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d4e3c0e46450edf7da174b610b9143eb8ca22059ace5016741fc9e20b88d1e7"
+checksum = "5910a00acc21b3f106b9e3977cabf8d4c15b62ea585664f08ec6fedb118d88e0"
dependencies = [
"itertools 0.11.0",
"postcard",
@@ -4604,9 +4812,9 @@ dependencies = [
[[package]]
name = "serde_columnar_derive"
-version = "0.3.5"
+version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42c5d47942b2a7e76118b697fc0f94516a5d8366a3c0fee8d0e2b713e952e306"
+checksum = "44cea1995b758f1b344f484e77a02d9d85c8a62c9ce0e5f1850e27e2f7eebbc9"
dependencies = [
"darling",
"proc-macro2",
@@ -4731,6 +4939,7 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
+ "digest",
"rand_core 0.6.4",
]
@@ -4773,9 +4982,9 @@ dependencies = [
[[package]]
name = "socket2"
-version = "0.5.8"
+version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
+checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
dependencies = [
"libc",
"windows-sys 0.52.0",
@@ -4835,6 +5044,199 @@ dependencies = [
"der",
]
+[[package]]
+name = "sqlx"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3c3a85280daca669cfd3bcb68a337882a8bc57ec882f72c5d13a430613a738e"
+dependencies = [
+ "sqlx-core",
+ "sqlx-macros",
+ "sqlx-mysql",
+ "sqlx-postgres",
+ "sqlx-sqlite",
+]
+
+[[package]]
+name = "sqlx-core"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f743f2a3cea30a58cd479013f75550e879009e3a02f616f18ca699335aa248c3"
+dependencies = [
+ "base64",
+ "bytes",
+ "chrono",
+ "crc",
+ "crossbeam-queue",
+ "either",
+ "event-listener",
+ "futures-core",
+ "futures-intrusive",
+ "futures-io",
+ "futures-util",
+ "hashbrown 0.15.2",
+ "hashlink",
+ "indexmap",
+ "log",
+ "memchr",
+ "once_cell",
+ "percent-encoding",
+ "serde",
+ "serde_json",
+ "sha2",
+ "smallvec",
+ "thiserror 2.0.12",
+ "tokio",
+ "tokio-stream",
+ "tracing",
+ "url",
+]
+
+[[package]]
+name = "sqlx-macros"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f4200e0fde19834956d4252347c12a083bdcb237d7a1a1446bffd8768417dce"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "sqlx-core",
+ "sqlx-macros-core",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "sqlx-macros-core"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7"
+dependencies = [
+ "dotenvy",
+ "either",
+ "heck 0.5.0",
+ "hex",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "sha2",
+ "sqlx-core",
+ "sqlx-mysql",
+ "sqlx-postgres",
+ "sqlx-sqlite",
+ "syn 2.0.100",
+ "tempfile",
+ "tokio",
+ "url",
+]
+
+[[package]]
+name = "sqlx-mysql"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0afdd3aa7a629683c2d750c2df343025545087081ab5942593a5288855b1b7a7"
+dependencies = [
+ "atoi",
+ "base64",
+ "bitflags 2.9.0",
+ "byteorder",
+ "bytes",
+ "chrono",
+ "crc",
+ "digest",
+ "dotenvy",
+ "either",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-util",
+ "generic-array",
+ "hex",
+ "hkdf",
+ "hmac",
+ "itoa",
+ "log",
+ "md-5",
+ "memchr",
+ "once_cell",
+ "percent-encoding",
+ "rand 0.8.5",
+ "rsa",
+ "serde",
+ "sha1",
+ "sha2",
+ "smallvec",
+ "sqlx-core",
+ "stringprep",
+ "thiserror 2.0.12",
+ "tracing",
+ "whoami",
+]
+
+[[package]]
+name = "sqlx-postgres"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0bedbe1bbb5e2615ef347a5e9d8cd7680fb63e77d9dafc0f29be15e53f1ebe6"
+dependencies = [
+ "atoi",
+ "base64",
+ "bitflags 2.9.0",
+ "byteorder",
+ "chrono",
+ "crc",
+ "dotenvy",
+ "etcetera",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "hex",
+ "hkdf",
+ "hmac",
+ "home",
+ "itoa",
+ "log",
+ "md-5",
+ "memchr",
+ "once_cell",
+ "rand 0.8.5",
+ "serde",
+ "serde_json",
+ "sha2",
+ "smallvec",
+ "sqlx-core",
+ "stringprep",
+ "thiserror 2.0.12",
+ "tracing",
+ "whoami",
+]
+
+[[package]]
+name = "sqlx-sqlite"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc"
+dependencies = [
+ "atoi",
+ "chrono",
+ "flume",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-intrusive",
+ "futures-util",
+ "libsqlite3-sys",
+ "log",
+ "percent-encoding",
+ "serde",
+ "serde_urlencoded",
+ "sqlx-core",
+ "thiserror 2.0.12",
+ "tracing",
+ "url",
+]
+
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@@ -4847,6 +5249,17 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+[[package]]
+name = "stringprep"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+ "unicode-properties",
+]
+
[[package]]
name = "strsim"
version = "0.11.1"
@@ -5166,9 +5579,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
-version = "1.44.1"
+version = "1.44.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
+checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
dependencies = [
"backtrace",
"bytes",
@@ -5460,6 +5873,12 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "unicode-bidi"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
+
[[package]]
name = "unicode-ident"
version = "1.0.18"
@@ -5475,6 +5894,12 @@ dependencies = [
"tinyvec",
]
+[[package]]
+name = "unicode-properties"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
+
[[package]]
name = "unicode-xid"
version = "0.2.6"
@@ -5557,6 +5982,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
[[package]]
name = "version-compare"
version = "0.2.0"
@@ -5603,6 +6034,12 @@ dependencies = [
"wit-bindgen-rt",
]
+[[package]]
+name = "wasite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
+
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
@@ -5725,6 +6162,16 @@ dependencies = [
"rustls-pki-types",
]
+[[package]]
+name = "whoami"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7"
+dependencies = [
+ "redox_syscall",
+ "wasite",
+]
+
[[package]]
name = "widestring"
version = "1.1.0"
@@ -6393,9 +6840,42 @@ dependencies = [
"uds_windows",
"windows-sys 0.52.0",
"xdg-home",
- "zbus_macros",
- "zbus_names",
- "zvariant",
+ "zbus_macros 4.4.0",
+ "zbus_names 3.0.0",
+ "zvariant 4.2.0",
+]
+
+[[package]]
+name = "zbus"
+version = "5.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2522b82023923eecb0b366da727ec883ace092e7887b61d3da5139f26b44da58"
+dependencies = [
+ "async-broadcast",
+ "async-executor",
+ "async-io",
+ "async-lock",
+ "async-process",
+ "async-recursion",
+ "async-task",
+ "async-trait",
+ "blocking",
+ "enumflags2",
+ "event-listener",
+ "futures-core",
+ "futures-lite",
+ "hex",
+ "nix 0.29.0",
+ "ordered-stream",
+ "serde",
+ "serde_repr",
+ "tracing",
+ "uds_windows",
+ "windows-sys 0.59.0",
+ "winnow",
+ "zbus_macros 5.6.0",
+ "zbus_names 4.2.0",
+ "zvariant 5.5.1",
]
[[package]]
@@ -6408,7 +6888,22 @@ dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
- "zvariant_utils",
+ "zvariant_utils 2.1.0",
+]
+
+[[package]]
+name = "zbus_macros"
+version = "5.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05d2e12843c75108c00c618c2e8ef9675b50b6ec095b36dc965f2e5aed463c15"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+ "zbus_names 4.2.0",
+ "zvariant 5.5.1",
+ "zvariant_utils 3.2.0",
]
[[package]]
@@ -6419,7 +6914,19 @@ checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
dependencies = [
"serde",
"static_assertions",
- "zvariant",
+ "zvariant 4.2.0",
+]
+
+[[package]]
+name = "zbus_names"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97"
+dependencies = [
+ "serde",
+ "static_assertions",
+ "winnow",
+ "zvariant 5.5.1",
]
[[package]]
@@ -6488,6 +6995,20 @@ name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
[[package]]
name = "zerovec"
@@ -6522,7 +7043,22 @@ dependencies = [
"serde",
"static_assertions",
"url",
- "zvariant_derive",
+ "zvariant_derive 4.2.0",
+]
+
+[[package]]
+name = "zvariant"
+version = "5.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "557e89d54880377a507c94cd5452f20e35d14325faf9d2958ebeadce0966c1b2"
+dependencies = [
+ "endi",
+ "enumflags2",
+ "serde",
+ "url",
+ "winnow",
+ "zvariant_derive 5.5.1",
+ "zvariant_utils 3.2.0",
]
[[package]]
@@ -6535,7 +7071,20 @@ dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
- "zvariant_utils",
+ "zvariant_utils 2.1.0",
+]
+
+[[package]]
+name = "zvariant_derive"
+version = "5.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "757779842a0d242061d24c28be589ce392e45350dfb9186dfd7a042a2e19870c"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+ "zvariant_utils 3.2.0",
]
[[package]]
@@ -6548,3 +7097,17 @@ dependencies = [
"quote",
"syn 2.0.100",
]
+
+[[package]]
+name = "zvariant_utils"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde",
+ "static_assertions",
+ "syn 2.0.100",
+ "winnow",
+]
diff --git a/aardvark-app/Cargo.toml b/aardvark-app/Cargo.toml
index 9cdfe10..9d78782 100644
--- a/aardvark-app/Cargo.toml
+++ b/aardvark-app/Cargo.toml
@@ -17,9 +17,15 @@ sourceview = { package = "sourceview5", version = "0.9" }
tracing = "0.1"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
ashpd = { version = "0.9", default-features = false, features = ["tracing", "async-std"] }
+thiserror = { version = "2.0" }
futures-util = "0.3"
+oo7 = { version = "0.4", default-features = false, features = [
+ "openssl_crypto",
+ "async-std",
+ "tracing",
+] }
[dependencies.adw]
package = "libadwaita"
version = "0.7"
-features = ["v1_6"]
+features = ["v1_6"]
\ No newline at end of file
diff --git a/aardvark-app/data/resources/icons/scalable/actions/down-smaller-symbolic.svg b/aardvark-app/data/resources/icons/scalable/actions/down-smaller-symbolic.svg
new file mode 100644
index 0000000..0d073be
--- /dev/null
+++ b/aardvark-app/data/resources/icons/scalable/actions/down-smaller-symbolic.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/aardvark-app/data/resources/icons/scalable/actions/join-document-symbolic.svg b/aardvark-app/data/resources/icons/scalable/actions/join-document-symbolic.svg
new file mode 100644
index 0000000..975067b
--- /dev/null
+++ b/aardvark-app/data/resources/icons/scalable/actions/join-document-symbolic.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/aardvark-app/data/resources/resources.gresource.xml b/aardvark-app/data/resources/resources.gresource.xml
index 2b7e3e0..2ffd3ac 100644
--- a/aardvark-app/data/resources/resources.gresource.xml
+++ b/aardvark-app/data/resources/resources.gresource.xml
@@ -1,5 +1,7 @@
+ icons/scalable/actions/down-smaller-symbolic.svg
+ icons/scalable/actions/join-document-symbolic.svg
diff --git a/aardvark-app/src/application.rs b/aardvark-app/src/application.rs
index 59f243a..890cb52 100644
--- a/aardvark-app/src/application.rs
+++ b/aardvark-app/src/application.rs
@@ -23,9 +23,12 @@ use adw::prelude::*;
use adw::subclass::prelude::*;
use gettextrs::gettext;
use gtk::{gio, glib, glib::Properties};
+use std::{cell::OnceCell, fs};
+use tracing::error;
use crate::AardvarkWindow;
use crate::config;
+use crate::secret;
use crate::system_settings::SystemSettings;
mod imp {
@@ -35,7 +38,7 @@ mod imp {
#[properties(wrapper_type = super::AardvarkApplication)]
pub struct AardvarkApplication {
#[property(get)]
- pub service: Service,
+ pub service: OnceCell,
#[property(get)]
pub system_settings: SystemSettings,
}
@@ -55,17 +58,36 @@ mod imp {
obj.setup_gactions();
obj.set_accels_for_action("app.quit", &["q"]);
obj.set_accels_for_action("app.new-window", &["n"]);
+
+ // FIXME: Don't block on loading the identity
+ glib::MainContext::new().block_on(async move {
+ let private_key = secret::get_or_create_identity()
+ .await
+ .expect("Unable to get or create identity");
+
+ let mut data_path = glib::user_data_dir();
+ data_path.push("Aardvark");
+ data_path.push(private_key.public_key().to_string());
+ if let Err(error) = fs::create_dir_all(&data_path) {
+ error!("Failed to create data directory: {error}");
+ }
+ let data_dir = gio::File::for_path(data_path);
+
+ self.service
+ .set(Service::new(&private_key, &data_dir))
+ .unwrap();
+ });
}
}
impl ApplicationImpl for AardvarkApplication {
fn startup(&self) {
- self.service.startup();
+ self.obj().service().startup();
self.parent_startup();
}
fn shutdown(&self) {
- self.service.shutdown();
+ self.obj().service().shutdown();
self.parent_shutdown();
}
@@ -116,7 +138,7 @@ impl AardvarkApplication {
}
fn new_window(&self) {
- let window = AardvarkWindow::new(self, &self.imp().service);
+ let window = AardvarkWindow::new(self, &self.service());
window.present();
}
diff --git a/aardvark-app/src/connection_popover/mod.rs b/aardvark-app/src/connection_popover/mod.rs
index b291bfb..80fd71e 100644
--- a/aardvark-app/src/connection_popover/mod.rs
+++ b/aardvark-app/src/connection_popover/mod.rs
@@ -118,9 +118,10 @@ mod imp {
let author: Author = binding.source().unwrap().downcast().unwrap();
if is_online {
Some("Online".to_string())
- //Some(format_last_seen(&glib::DateTime::now_local().unwrap()))
+ } else if let Some(last_seen) = author.last_seen() {
+ Some(format_last_seen(&last_seen))
} else {
- Some(format_last_seen(&author.last_seen().unwrap()))
+ Some("Never seen".to_string())
}
})
.build();
diff --git a/aardvark-app/src/main.rs b/aardvark-app/src/main.rs
index 1d82a03..7b56ba1 100644
--- a/aardvark-app/src/main.rs
+++ b/aardvark-app/src/main.rs
@@ -23,6 +23,8 @@ mod components;
mod config;
mod connection_popover;
mod open_dialog;
+mod open_popover;
+mod secret;
mod system_settings;
mod textbuffer;
mod window;
@@ -37,9 +39,12 @@ use tracing_subscriber::prelude::*;
use self::application::AardvarkApplication;
use self::config::*;
use self::connection_popover::ConnectionPopover;
+use self::open_popover::OpenPopover;
use self::textbuffer::AardvarkTextBuffer;
use self::window::AardvarkWindow;
+pub use self::config::APP_ID;
+
fn main() -> glib::ExitCode {
setup_logging();
diff --git a/aardvark-app/src/open_popover/mod.rs b/aardvark-app/src/open_popover/mod.rs
new file mode 100644
index 0000000..6060026
--- /dev/null
+++ b/aardvark-app/src/open_popover/mod.rs
@@ -0,0 +1,362 @@
+/* Copyright 2025 The Aardvark Developers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+use adw::subclass::prelude::*;
+use gettextrs::gettext;
+use gtk::prelude::*;
+use gtk::{glib, glib::clone, glib::closure_local};
+
+use crate::system_settings::ClockFormat;
+use crate::{AardvarkApplication, AardvarkWindow, open_dialog::OpenDialog};
+use aardvark_doc::{document::Document, documents::Documents};
+
+mod imp {
+ use super::*;
+ use adw::prelude::AdwDialogExt;
+ use glib::subclass::Signal;
+ use std::sync::LazyLock;
+
+ #[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)]
+ #[properties(wrapper_type = super::OpenPopover)]
+ #[template(resource = "/org/p2panda/aardvark/open_popover/open_popover.ui")]
+ pub struct OpenPopover {
+ #[template_child]
+ search_entry: TemplateChild,
+ #[template_child]
+ listbox: TemplateChild,
+ #[template_child]
+ stack: TemplateChild,
+ #[template_child]
+ no_results_page: TemplateChild,
+ #[template_child]
+ document_list_page: TemplateChild,
+ #[template_child]
+ open_document_button: TemplateChild,
+ #[property(get = Self::model, set = Self::set_model, type = Option)]
+ model: gtk::FilterListModel,
+ }
+
+ #[glib::object_subclass]
+ impl ObjectSubclass for OpenPopover {
+ const NAME: &'static str = "AardvarkOpenPopover";
+ type Type = super::OpenPopover;
+ type ParentType = gtk::Popover;
+
+ fn class_init(klass: &mut Self::Class) {
+ klass.bind_template();
+ }
+
+ fn instance_init(obj: &glib::subclass::InitializingObject) {
+ obj.init_template();
+ }
+ }
+
+ #[glib::derived_properties]
+ impl ObjectImpl for OpenPopover {
+ fn signals() -> &'static [Signal] {
+ static SIGNALS: LazyLock> = LazyLock::new(|| {
+ vec![
+ // The user has activated a document in the document list.
+ Signal::builder("document-activated")
+ .param_types([Document::static_type()])
+ .build(),
+ ]
+ });
+ SIGNALS.as_ref()
+ }
+
+ fn constructed(&self) {
+ self.parent_constructed();
+
+ // TODO: We should also match the document id with a more complex filter
+ let filter = gtk::StringFilter::builder()
+ .expression(gtk::PropertyExpression::new(
+ Document::static_type(),
+ gtk::Expression::NONE,
+ "name",
+ ))
+ .ignore_case(true)
+ .match_mode(gtk::StringFilterMatchMode::Substring)
+ .build();
+ self.model.set_filter(Some(&filter));
+
+ self.search_entry
+ .connect_search_changed(move |search_entry| {
+ filter.set_search(Some(&search_entry.text()));
+ });
+
+ self.model.connect_items_changed(clone!(
+ #[weak(rename_to = this)]
+ self,
+ move |model, _, _, _| {
+ if model.n_items() > 0 {
+ this.stack.set_visible_child(&*this.document_list_page);
+ } else {
+ this.stack.set_visible_child(&*this.no_results_page);
+ }
+ }
+ ));
+
+ self.listbox.connect_row_activated(clone!(
+ #[weak(rename_to = this)]
+ self,
+ move |_, row| {
+ let document: Document = this
+ .model
+ .item(row.index() as u32)
+ .unwrap()
+ .downcast()
+ .unwrap();
+ this.obj()
+ .emit_by_name::<()>("document-activated", &[&document]);
+ this.search_entry.set_text("");
+ this.obj().popdown();
+ }
+ ));
+
+ self.listbox.bind_model(Some(&self.model), |document| {
+ let document = document.downcast_ref::().unwrap();
+ let row = adw::ActionRow::builder()
+ .selectable(false)
+ .activatable(true)
+ .build();
+
+ document
+ .bind_property("name", &row, "title")
+ .sync_create()
+ .transform_to(|_, title: Option| {
+ if let Some(title) = title {
+ Some(title)
+ } else {
+ Some(gettext("Empty document"))
+ }
+ })
+ .build();
+
+ document
+ .bind_property("last-accessed", &row, "subtitle")
+ .sync_create()
+ .transform_to(|binding, last_accessed: Option| {
+ let document: Document = binding.source().unwrap().downcast().unwrap();
+ if let Some(last_accessed) = last_accessed {
+ Some(format_last_accessed(&last_accessed))
+ } else if document.subscribed() {
+ Some(gettext("Currently open"))
+ } else {
+ Some(gettext("Never accessed"))
+ }
+ })
+ .build();
+
+ row.upcast()
+ });
+
+ self.open_document_button.connect_clicked(clone!(
+ #[weak(rename_to = this)]
+ self,
+ move |_| {
+ let dialog = OpenDialog::new();
+ let window = this
+ .obj()
+ .root()
+ .and_then(|w| w.downcast::().ok())
+ .expect("Toplevel window needs to be a AardvarkWindow");
+
+ this.obj().popdown();
+ dialog.present(Some(&window));
+
+ let service = window.service();
+ dialog.connect_open(clone!(
+ #[weak]
+ this,
+ #[weak]
+ service,
+ move |_, document_id| {
+ let document = service
+ .documents()
+ .by_id(document_id)
+ .unwrap_or_else(|| Document::new(&service, Some(document_id)));
+
+ this.obj()
+ .emit_by_name::<()>("document-activated", &[&document]);
+ }
+ ));
+ }
+ ));
+ }
+ }
+
+ impl OpenPopover {
+ fn model(&self) -> Option {
+ if let Some(model) = self.model.model() {
+ model.downcast().ok()
+ } else {
+ None
+ }
+ }
+
+ fn set_model(&self, model: Option<&Documents>) {
+ self.model.set_model(model);
+ }
+ }
+
+ impl WidgetImpl for OpenPopover {}
+ impl PopoverImpl for OpenPopover {}
+}
+
+glib::wrapper! {
+ pub struct OpenPopover(ObjectSubclass)
+ @extends gtk::Widget, gtk::Popover;
+}
+
+impl OpenPopover {
+ pub fn new>(model: &P) -> Self {
+ glib::Object::builder().property("model", model).build()
+ }
+
+ /// Connect to the signal emitted when a user clicks a document in the document list.
+ pub fn connect_document_activated(
+ &self,
+ f: F,
+ ) -> glib::SignalHandlerId {
+ self.connect_closure(
+ "document-activated",
+ true,
+ closure_local!(move |obj: Self, document: Document| {
+ f(&obj, &document);
+ }),
+ )
+ }
+}
+
+// This was copied from Fractal
+// See: https://gitlab.gnome.org/World/fractal/-/blob/main/src/session/model/user_sessions_list/user_session.rs#L258
+fn format_last_accessed(datetime: &glib::DateTime) -> String {
+ let datetime = datetime.to_local().unwrap();
+ let clock_format = AardvarkApplication::default()
+ .system_settings()
+ .clock_format();
+ let use_24 = clock_format == ClockFormat::TwentyFourHours;
+
+ // This was ported from Nautilus and simplified for our use case.
+ // See: https://gitlab.gnome.org/GNOME/nautilus/-/blob/1c5bd3614a35cfbb49de087bc10381cdef5a218f/src/nautilus-file.c#L5001
+ let now = glib::DateTime::now_local().unwrap();
+ let format;
+ let days_ago = {
+ let today_midnight =
+ glib::DateTime::from_local(now.year(), now.month(), now.day_of_month(), 0, 0, 0f64)
+ .expect("constructing GDateTime works");
+
+ let date = glib::DateTime::from_local(
+ datetime.year(),
+ datetime.month(),
+ datetime.day_of_month(),
+ 0,
+ 0,
+ 0f64,
+ )
+ .expect("constructing GDateTime works");
+
+ today_midnight.difference(&date).as_days()
+ };
+
+ // Show only the time if date is on today
+ if days_ago == 0 {
+ if use_24 {
+ // Translators: Time in 24h format, i.e. "23:04".
+ // Do not change the time format as it will follow the system settings.
+ // See `man strftime` or the documentation of g_date_time_format for the available specifiers:
+ format = gettext("Last accessed at %H:%M");
+ } else {
+ // Translators: Time in 12h format, i.e. "11:04 PM".
+ // Do not change the time format as it will follow the system settings.
+ // See `man strftime` or the documentation of g_date_time_format for the available specifiers:
+ format = gettext("Last accessed at %I:%M %p");
+ }
+ }
+ // Show the word "Yesterday" and time if date is on yesterday
+ else if days_ago == 1 {
+ if use_24 {
+ // Translators: this a time in 24h format, i.e. "Last seen yesterday at 23:04".
+ // Do not change the time format as it will follow the system settings.
+ // See `man strftime` or the documentation of g_date_time_format for the available specifiers:
+ // xgettext:no-c-format
+ format = gettext("Last accessed yesterday at %H:%M");
+ } else {
+ // Translators: this is a time in 12h format, i.e. "Last seen Yesterday at 11:04
+ // PM".
+ // Do not change the time format as it will follow the system settings.
+ // See `man strftime` or the documentation of g_date_time_format for the available specifiers:
+ // xgettext:no-c-format
+ format = gettext("Last accessed yesterday at %I:%M %p");
+ }
+ }
+ // Show a week day and time if date is in the last week
+ else if days_ago > 1 && days_ago < 7 {
+ if use_24 {
+ // Translators: this is the name of the week day followed by a time in 24h
+ // format, i.e. "Last seen Monday at 23:04".
+ // Do not change the time format as it will follow the system settings.
+ // See `man strftime` or the documentation of g_date_time_format for the available specifiers:
+ // xgettext:no-c-format
+ format = gettext("Last accessed %A at %H:%M");
+ } else {
+ // Translators: this is the week day name followed by a time in 12h format, i.e.
+ // "Last seen Monday at 11:04 PM".
+ // Do not change the time format as it will follow the system settings.
+ // See `man strftime` or the documentation of g_date_time_format for the available specifiers:
+ // xgettext:no-c-format
+ format = gettext("Last accessed %A at %I:%M %p");
+ }
+ } else if datetime.year() == now.year() {
+ if use_24 {
+ // Translators: this is the month and day and the time in 24h format, i.e. "Last
+ // seen February 3 at 23:04".
+ // Do not change the time format as it will follow the system settings.
+ // See `man strftime` or the documentation of g_date_time_format for the available specifiers:
+ // xgettext:no-c-format
+ format = gettext("Last accessed %B %-e at %H:%M");
+ } else {
+ // Translators: this is the month and day and the time in 12h format, i.e. "Last
+ // seen February 3 at 11:04 PM".
+ // Do not change the time format as it will follow the system settings.
+ // See `man strftime` or the documentation of g_date_time_format for the available specifiers:
+ // xgettext:no-c-format
+ format = gettext("Last accessed %B %-e at %I:%M %p");
+ }
+ } else if use_24 {
+ // Translators: this is the full date and the time in 24h format, i.e. "Last
+ // seen February 3 2015 at 23:04".
+ // Do not change the time format as it will follow the system settings.
+ // See `man strftime` or the documentation of g_date_time_format for the available specifiers:
+ // xgettext:no-c-format
+ format = gettext("Last accessed %B %-e %Y at %H:%M");
+ } else {
+ // Translators: this is the full date and the time in 12h format, i.e. "Last
+ // seen February 3 2015 at 11:04 PM".
+ // Do not change the time format as it will follow the system settings.
+ // See `man strftime` or the documentation of g_date_time_format for the available specifiers:
+ // xgettext:no-c-format
+ format = gettext("Last accessed %B %-e %Y at %I:%M %p");
+ }
+
+ datetime
+ .format(&format)
+ .expect("formatting GDateTime works")
+ .into()
+}
diff --git a/aardvark-app/src/open_popover/open_popover.ui b/aardvark-app/src/open_popover/open_popover.ui
new file mode 100644
index 0000000..7af5845
--- /dev/null
+++ b/aardvark-app/src/open_popover/open_popover.ui
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aardvark-app/src/secret.rs b/aardvark-app/src/secret.rs
new file mode 100644
index 0000000..f81e03d
--- /dev/null
+++ b/aardvark-app/src/secret.rs
@@ -0,0 +1,78 @@
+/* Copyright 2025 The Aardvark Developers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+use std::collections::HashMap;
+use thiserror::Error;
+use tracing::info;
+
+use crate::APP_ID;
+use aardvark_doc::identity::{IdentityError, PrivateKey};
+
+const XDG_SCHEMA: &'static str = "xdg:schema";
+
+fn attributes() -> HashMap<&'static str, String> {
+ HashMap::from([(XDG_SCHEMA, APP_ID.to_owned())])
+}
+
+#[derive(Debug, Error)]
+pub enum Error {
+ #[error("Secret Service error: {0}")]
+ Service(oo7::Error),
+ #[error("Format error: {0}")]
+ Format(IdentityError),
+}
+
+impl From for Error {
+ fn from(value: IdentityError) -> Self {
+ Error::Format(value)
+ }
+}
+
+impl From for Error {
+ fn from(value: oo7::Error) -> Self {
+ Error::Service(value)
+ }
+}
+
+pub async fn get_or_create_identity() -> Result {
+ let keyring = oo7::Keyring::new().await?;
+
+ keyring.unlock().await?;
+
+ let private_key: PrivateKey =
+ if let Some(item) = keyring.search_items(&attributes()).await?.get(0) {
+ item.unlock().await?;
+ let private_key = PrivateKey::try_from(item.secret().await?.as_bytes())?;
+ info!("Found existing identity: {}", private_key.public_key());
+
+ private_key
+ } else {
+ let private_key = PrivateKey::new();
+ keyring
+ .create_item("Aardvark", &attributes(), private_key.as_bytes(), true)
+ .await?;
+
+ info!(
+ "No existing identity found. Create new identity: {}",
+ private_key.public_key()
+ );
+ private_key
+ };
+
+ Ok(private_key)
+}
diff --git a/aardvark-app/src/style.css b/aardvark-app/src/style.css
index 9a9d3ed..e891fba 100644
--- a/aardvark-app/src/style.css
+++ b/aardvark-app/src/style.css
@@ -42,3 +42,20 @@
font-weight: 700;
font-size: 9px;
}
+.open-popover contents {
+ padding: 0px;
+ min-width: 300px;
+}
+
+.open-popover .search {
+ margin: 6px;
+}
+
+.open-popover row {
+ border-radius: 6px;
+ margin: 6px;
+}
+
+.open-popover .open-document {
+ margin: 12px;
+}
diff --git a/aardvark-app/src/textbuffer.rs b/aardvark-app/src/textbuffer.rs
index 33782d3..b7bdadc 100644
--- a/aardvark-app/src/textbuffer.rs
+++ b/aardvark-app/src/textbuffer.rs
@@ -97,6 +97,9 @@ mod imp {
move |values| {
let pos: i32 = values.get(1).unwrap().get().unwrap();
let text: &str = values.get(2).unwrap().get().unwrap();
+ if buffer.inhibit_text_change() {
+ return None;
+ }
let mut pos_iter = buffer.iter_at_offset(pos);
buffer.set_inhibit_text_change(true);
@@ -119,6 +122,10 @@ mod imp {
move |values| {
let start: i32 = values.get(1).unwrap().get().unwrap();
let end: i32 = values.get(2).unwrap().get().unwrap();
+ if buffer.inhibit_text_change() {
+ return None;
+ }
+
let mut start = buffer.iter_at_offset(start);
let mut end = buffer.iter_at_offset(end);
buffer.set_inhibit_text_change(true);
@@ -136,34 +143,49 @@ mod imp {
impl TextBufferImpl for AardvarkTextBuffer {
fn insert_text(&self, iter: &mut gtk::TextIter, new_text: &str) {
+ if self.obj().inhibit_text_change() {
+ self.parent_insert_text(iter, new_text);
+ return;
+ }
+ let Some(document) = self.obj().document() else {
+ self.parent_insert_text(iter, new_text);
+ return;
+ };
+
let offset = iter.offset();
+ self.obj().set_inhibit_text_change(true);
+ let result = document.insert_text(offset, new_text);
+ self.obj().set_inhibit_text_change(false);
- if !self.inhibit_text_change.get() {
- if let Some(document) = self.document.borrow().as_ref() {
- if let Err(error) = document.insert_text(offset, new_text) {
- error!("Failed to submit changes to the document: {error}");
- }
- }
+ // Only insert text into the buffer when the document was successfully updated
+ if let Err(error) = result {
+ error!("Failed to submit changes to the document: {error}");
} else {
- // Only insert text received from the CRDT document
info!("inserting new text {} at pos {}", new_text, offset);
-
self.parent_insert_text(iter, new_text);
}
}
fn delete_range(&self, start: &mut gtk::TextIter, end: &mut gtk::TextIter) {
+ if self.obj().inhibit_text_change() {
+ self.parent_delete_range(start, end);
+ return;
+ }
+ let Some(document) = self.obj().document() else {
+ self.parent_delete_range(start, end);
+ return;
+ };
+
let offset_start = start.offset();
let offset_end = end.offset();
+ self.obj().set_inhibit_text_change(true);
+ let result = document.delete_range(offset_start, offset_end);
+ self.obj().set_inhibit_text_change(false);
- if !self.inhibit_text_change.get() {
- if let Some(document) = self.document.borrow().as_ref() {
- if let Err(error) = document.delete_range(offset_start, offset_end) {
- error!("Failed to submit changes to the document: {error}")
- }
- }
+ // Only delete text from the buffer when the document was successfully updated
+ if let Err(error) = result {
+ error!("Failed to submit changes to the document: {error}");
} else {
- // Only delete text received from the CRDT document
info!(
"deleting range at start {} end {}",
offset_start, offset_end
@@ -187,6 +209,10 @@ impl AardvarkTextBuffer {
glib::Object::builder().build()
}
+ fn inhibit_text_change(&self) -> bool {
+ self.imp().inhibit_text_change.get()
+ }
+
fn set_inhibit_text_change(&self, inhibit_text_change: bool) {
self.imp().inhibit_text_change.set(inhibit_text_change);
}
diff --git a/aardvark-app/src/ui-resources.gresource.xml b/aardvark-app/src/ui-resources.gresource.xml
index aa0365b..8a9d39d 100644
--- a/aardvark-app/src/ui-resources.gresource.xml
+++ b/aardvark-app/src/ui-resources.gresource.xml
@@ -2,6 +2,7 @@
open_dialog/open_dialog.ui
+ open_popover/open_popover.ui
window.ui
components/zoom_level_selector.ui
gtk/help-overlay.ui
diff --git a/aardvark-app/src/window.rs b/aardvark-app/src/window.rs
index f91d833..c9e86d6 100644
--- a/aardvark-app/src/window.rs
+++ b/aardvark-app/src/window.rs
@@ -24,16 +24,13 @@ use aardvark_doc::{
document::{Document, DocumentId},
service::Service,
};
-use adw::prelude::AdwDialogExt;
-use adw::subclass::prelude::*;
-use gtk::prelude::*;
+
+use adw::{prelude::*, subclass::prelude::*};
use gtk::{gdk, gio, glib, glib::clone};
-use sourceview::*;
use crate::{
- AardvarkApplication, AardvarkTextBuffer, ConnectionPopover,
+ AardvarkApplication, AardvarkTextBuffer, ConnectionPopover, OpenPopover,
components::{MultilineEntry, ZoomLevelSelector},
- open_dialog::OpenDialog,
};
const BASE_TEXT_FONT_SIZE: f64 = 24.0;
@@ -49,7 +46,9 @@ mod imp {
#[template_child]
pub text_view: TemplateChild,
#[template_child]
- pub open_dialog_document_button: TemplateChild,
+ pub open_popover_button: TemplateChild,
+ #[template_child]
+ pub open_popover: TemplateChild,
#[template_child]
pub toast_overlay: TemplateChild,
#[template_child]
@@ -83,6 +82,7 @@ mod imp {
fn class_init(klass: &mut Self::Class) {
ZoomLevelSelector::static_type();
MultilineEntry::static_type();
+ OpenPopover::static_type();
klass.bind_template();
@@ -151,7 +151,7 @@ mod imp {
self.font_size.set(BASE_TEXT_FONT_SIZE);
self.obj().set_font_scale(0.0);
gtk::style_context_add_provider_for_display(
- &self.obj().display(),
+ >k::Widget::display(self.obj().upcast_ref()),
&self.css_provider,
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
@@ -159,64 +159,63 @@ mod imp {
let scroll_controller =
gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL);
scroll_controller.set_propagation_phase(gtk::PropagationPhase::Capture);
- let window = self.obj().clone();
- scroll_controller.connect_scroll(move |scroll, _dx, dy| {
- if scroll
- .current_event_state()
- .contains(gdk::ModifierType::CONTROL_MASK)
- {
- if dy < 0.0 {
- window.set_font_scale(window.font_scale() + 1.0);
+ let window = self.obj();
+ scroll_controller.connect_scroll(clone!(
+ #[weak]
+ window,
+ #[upgrade_or]
+ glib::Propagation::Stop,
+ move |scroll, _dx, dy| {
+ if scroll
+ .current_event_state()
+ .contains(gdk::ModifierType::CONTROL_MASK)
+ {
+ if dy < 0.0 {
+ window.set_font_scale(window.font_scale() + 1.0);
+ } else {
+ window.set_font_scale(window.font_scale() - 1.0);
+ }
+ glib::Propagation::Stop
} else {
- window.set_font_scale(window.font_scale() - 1.0);
+ glib::Propagation::Proceed
}
- glib::Propagation::Stop
- } else {
- glib::Propagation::Proceed
}
- });
+ ));
self.obj().add_controller(scroll_controller);
let zoom_gesture = gtk::GestureZoom::new();
- let window = self.obj().clone();
let prev_delta = Cell::new(0.0);
- zoom_gesture.connect_scale_changed(move |_, delta| {
- if prev_delta.get() == delta {
- return;
- }
+ zoom_gesture.connect_scale_changed(clone!(
+ #[weak]
+ window,
+ move |_, delta| {
+ if prev_delta.get() == delta {
+ return;
+ }
- if prev_delta.get() < delta {
- window.set_font_scale(window.font_scale() + delta);
- } else {
- window.set_font_scale(window.font_scale() - delta);
+ if prev_delta.get() < delta {
+ window.set_font_scale(window.font_scale() + delta);
+ } else {
+ window.set_font_scale(window.font_scale() - delta);
+ }
+ prev_delta.set(delta);
}
- prev_delta.set(delta);
- });
+ ));
self.obj().add_controller(zoom_gesture);
- let window = self.obj();
- self.open_dialog_document_button.connect_clicked(clone!(
- #[weak]
- window,
- move |_| {
- let dialog = OpenDialog::new();
-
- dialog.present(Some(&window));
-
- dialog.connect_open(clone!(
- #[weak]
- window,
- move |_, document_id| {
- let app = AardvarkApplication::default();
-
- if let Some(window) = app.window_for_document_id(document_id) {
- window.present();
- } else {
- let document = Document::new(&window.service(), Some(document_id));
- window.imp().set_document(document);
- }
- }
- ));
+ self.open_popover
+ .set_model(self.obj().service().documents());
+
+ self.open_popover.connect_document_activated(clone!(
+ #[weak(rename_to = this)]
+ self,
+ move |_, document| {
+ let app = AardvarkApplication::default();
+ if let Some(window) = app.window_for_document_id(&document.id()) {
+ window.present();
+ } else {
+ this.set_document(document.to_owned());
+ }
}
));
@@ -233,6 +232,11 @@ mod imp {
let document = Document::new(self.service.get().unwrap(), None);
self.set_document(document);
+
+ self.obj().connect_close_request(|window| {
+ window.document().set_subscribed(false);
+ glib::Propagation::Proceed
+ });
}
}
@@ -273,7 +277,13 @@ mod imp {
));
self.connection_button_label
.set_label(&format!("{}", authors.n_items()));
- self.document.replace(Some(document));
+
+ document.set_subscribed(true);
+ let old_document = self.document.replace(Some(document));
+
+ if let Some(old_document) = old_document {
+ old_document.set_subscribed(false);
+ }
self.obj().notify("document");
}
@@ -305,7 +315,7 @@ mod imp {
glib::wrapper! {
pub struct AardvarkWindow(ObjectSubclass)
@extends gtk::Widget, gtk::Window, gtk::ApplicationWindow, adw::ApplicationWindow,
- @implements gio::ActionGroup, gio::ActionMap;
+ @implements gtk::Native, gtk::Root, gio::ActionGroup, gio::ActionMap;
}
impl AardvarkWindow {
diff --git a/aardvark-app/src/window.ui b/aardvark-app/src/window.ui
index 7828cf1..b777ad2 100644
--- a/aardvark-app/src/window.ui
+++ b/aardvark-app/src/window.ui
@@ -39,14 +39,28 @@
+
diff --git a/aardvark-doc/Cargo.toml b/aardvark-doc/Cargo.toml
index 98ea423..b229f1f 100644
--- a/aardvark-doc/Cargo.toml
+++ b/aardvark-doc/Cargo.toml
@@ -13,8 +13,7 @@ anyhow = "1.0.94"
async-channel = "2.3.1"
glib = "0.20"
gio = "0.20"
-loro = "1.3.1"
-p2panda-core = { git = "https://github.com/p2panda/p2panda", rev = "f3a016324b69beac45cf20a792fe6890cb1a21e3", default-features = false }
+loro = "1.5"
+p2panda-core = { git = "https://github.com/p2panda/p2panda", rev = "085a57206aeae70142176c0777ed2febc7b98664", default-features = false }
thiserror = "2.0.11"
-tokio = { version = "1.42.0", features = ["macros", "test-util"] }
tracing = "0.1"
\ No newline at end of file
diff --git a/aardvark-doc/src/author.rs b/aardvark-doc/src/author.rs
index 0a19a10..ee2d91c 100644
--- a/aardvark-doc/src/author.rs
+++ b/aardvark-doc/src/author.rs
@@ -1,10 +1,11 @@
-use std::cell::{Cell, OnceCell};
use std::sync::Mutex;
+use std::{cell::Cell, sync::OnceLock};
use glib::Properties;
use glib::prelude::*;
use glib::subclass::prelude::*;
-use p2panda_core::PublicKey;
+
+use crate::identity::PublicKey;
pub const COLORS: [(&str, &str); 15] = [
("Yellow", "#faf387"),
@@ -77,8 +78,9 @@ mod imp {
#[property(name = "name", get = Self::name, type = String)]
#[property(name = "emoji", get = Self::emoji, type = String)]
#[property(name = "color", get = Self::color, type = String)]
- pub public_key: OnceCell,
- #[property(get)]
+ #[property(get, set, construct_only, type = PublicKey)]
+ public_key: OnceLock,
+ #[property(get, set, construct_only)]
pub last_seen: Mutex