summary refs log tree commit diff
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2024-08-20 18:41:40 +0300
committerVika <vika@fireburn.ru>2024-08-20 18:41:40 +0300
commit7e403c00af4956a3996e5570eb0aa578745c520d (patch)
tree8e0b26774c119e47a9adc20f0619dbc450c6ef2f
parente6e4de9e15833e4042be5cd71944e9b8929346d4 (diff)
Send posts made in the post composer
-rw-r--r--Cargo.lock1095
-rw-r--r--Cargo.toml9
-rw-r--r--default.nix4
-rw-r--r--src/lib.rs71
-rw-r--r--src/micropub.rs81
-rw-r--r--src/util.rs27
6 files changed, 1218 insertions, 69 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7797e18..83a0b96 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,16 @@
 version = 3
 
 [[package]]
+name = "Inflector"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
+dependencies = [
+ "lazy_static",
+ "regex",
+]
+
+[[package]]
 name = "addr2line"
 version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -18,6 +28,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
 [[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "ast_node"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9184f2b369b3e8625712493c89b785881f27eedc6cde480a81883cef78868b2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "swc_macros_common",
+ "syn",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "auto_impl"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "autocfg"
 version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -39,21 +92,48 @@ dependencies = [
 ]
 
 [[package]]
+name = "better_scoped_tls"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "794edcc9b3fb07bb4aecaa11f093fd45663b4feadb782d68303a2268bc2701de"
+dependencies = [
+ "scoped-tls",
+]
+
+[[package]]
 name = "bitflags"
 version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
 
 [[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
 name = "bowl"
 version = "0.1.0"
 dependencies = [
  "futures",
- "glib",
+ "gio 0.20.1",
+ "glib 0.20.1",
  "gtk4",
+ "kittybox-indieauth",
+ "kittybox-util",
  "libadwaita",
+ "libsecret",
  "log",
+ "microformats",
  "relm4",
+ "serde_json",
+ "serde_urlencoded",
+ "soup3",
+ "thiserror",
  "tokio",
  "tracing",
  "tracker",
@@ -66,6 +146,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
 
 [[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
 name = "bytes"
 version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -79,7 +165,7 @@ checksum = "e8a0ea147c94108c9613235388f540e4d14c327f7081c9e471fc8ee8a2533e69"
 dependencies = [
  "bitflags",
  "cairo-sys-rs",
- "glib",
+ "glib 0.20.1",
  "libc",
 ]
 
@@ -89,9 +175,9 @@ version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "428290f914b9b86089f60f5d8a9f6e440508e1bcff23b25afd51502b0a2da88f"
 dependencies = [
- "glib-sys",
+ "glib-sys 0.20.1",
  "libc",
- "system-deps",
+ "system-deps 7.0.2",
 ]
 
 [[package]]
@@ -105,6 +191,16 @@ dependencies = [
 
 [[package]]
 name = "cfg-expr"
+version = "0.15.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
+dependencies = [
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cfg-expr"
 version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "345c78335be0624ed29012dc10c49102196c6882c12dde65d9f35b02da2aada8"
@@ -120,6 +216,57 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
+name = "cpufeatures"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+ "serde",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
 name = "equivalent"
 version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -148,12 +295,32 @@ dependencies = [
 ]
 
 [[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
 name = "fragile"
 version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
 
 [[package]]
+name = "from_variant"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4"
+dependencies = [
+ "proc-macro2",
+ "swc_macros_common",
+ "syn",
+]
+
+[[package]]
 name = "futures"
 version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -249,8 +416,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8730751991b97419fc3f0c2dca2c9e45b48edf46e48e0f965964ecf33889812f"
 dependencies = [
  "gdk-pixbuf-sys",
- "gio",
- "glib",
+ "gio 0.20.1",
+ "glib 0.20.1",
  "libc",
 ]
 
@@ -260,11 +427,11 @@ version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1ffbf649fd5b1c8c0f0feeb015b7533c3ef92da2887fb95ddd338bc2b1644a7c"
 dependencies = [
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.20.1",
+ "glib-sys 0.20.1",
+ "gobject-sys 0.20.1",
  "libc",
- "system-deps",
+ "system-deps 7.0.2",
 ]
 
 [[package]]
@@ -276,8 +443,8 @@ dependencies = [
  "cairo-rs",
  "gdk-pixbuf",
  "gdk4-sys",
- "gio",
- "glib",
+ "gio 0.20.1",
+ "glib 0.20.1",
  "libc",
  "pango",
 ]
@@ -290,13 +457,23 @@ checksum = "a67576c8ec012156d7f680e201a807b4432a77babb3157e0555e990ab6bcd878"
 dependencies = [
  "cairo-sys-rs",
  "gdk-pixbuf-sys",
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.20.1",
+ "glib-sys 0.20.1",
+ "gobject-sys 0.20.1",
  "libc",
  "pango-sys",
  "pkg-config",
- "system-deps",
+ "system-deps 7.0.2",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
 ]
 
 [[package]]
@@ -320,6 +497,24 @@ checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
 
 [[package]]
 name = "gio"
+version = "0.19.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c49f117d373ffcc98a35d114db5478bc223341cff53e39a5d6feced9e2ddffe"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-util",
+ "gio-sys 0.19.8",
+ "glib 0.19.9",
+ "libc",
+ "pin-project-lite",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gio"
 version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dcacaa37401cad0a95aadd266bc39c72a131d454fc012f6dfd217f891d76cc52"
@@ -328,8 +523,8 @@ dependencies = [
  "futures-core",
  "futures-io",
  "futures-util",
- "gio-sys",
- "glib",
+ "gio-sys 0.20.1",
+ "glib 0.20.1",
  "libc",
  "pin-project-lite",
  "smallvec",
@@ -337,19 +532,54 @@ dependencies = [
 
 [[package]]
 name = "gio-sys"
+version = "0.19.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cd743ba4714d671ad6b6234e8ab2a13b42304d0e13ab7eba1dcdd78a7d6d4ef"
+dependencies = [
+ "glib-sys 0.19.8",
+ "gobject-sys 0.19.8",
+ "libc",
+ "system-deps 6.2.2",
+ "windows-sys",
+]
+
+[[package]]
+name = "gio-sys"
 version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5237611e97e9b86ab5768adc3eef853ae713ea797aa3835404acdfacffc9fb38"
 dependencies = [
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.20.1",
+ "gobject-sys 0.20.1",
  "libc",
- "system-deps",
+ "system-deps 7.0.2",
  "windows-sys",
 ]
 
 [[package]]
 name = "glib"
+version = "0.19.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39650279f135469465018daae0ba53357942a5212137515777d5fdca74984a44"
+dependencies = [
+ "bitflags",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-task",
+ "futures-util",
+ "gio-sys 0.19.8",
+ "glib-macros 0.19.9",
+ "glib-sys 0.19.8",
+ "gobject-sys 0.19.8",
+ "libc",
+ "memchr",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "glib"
 version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b19429cb83fcbf1f00b31ae3a123fab5cd3761bdd15b0cc07905804742f0d0e4"
@@ -360,10 +590,10 @@ dependencies = [
  "futures-executor",
  "futures-task",
  "futures-util",
- "gio-sys",
- "glib-macros",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.20.1",
+ "glib-macros 0.20.1",
+ "glib-sys 0.20.1",
+ "gobject-sys 0.20.1",
  "libc",
  "log",
  "memchr",
@@ -372,6 +602,19 @@ dependencies = [
 
 [[package]]
 name = "glib-macros"
+version = "0.19.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4429b0277a14ae9751350ad9b658b1be0abb5b54faa5bcdf6e74a3372582fad7"
+dependencies = [
+ "heck",
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "glib-macros"
 version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "960349f56469b75794157e93bf04f0bc8a622d0a6612d6a8f8d7eac41e0e1ee1"
@@ -385,12 +628,33 @@ dependencies = [
 
 [[package]]
 name = "glib-sys"
+version = "0.19.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c2dc18d3a82b0006d470b13304fbbb3e0a9bd4884cf985a60a7ed733ac2c4a5"
+dependencies = [
+ "libc",
+ "system-deps 6.2.2",
+]
+
+[[package]]
+name = "glib-sys"
 version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "44edae63bea922f18f7e63977ee60a257ec27c4613aff1a6a9bb572ad0d88269"
 dependencies = [
  "libc",
- "system-deps",
+ "system-deps 7.0.2",
+]
+
+[[package]]
+name = "gobject-sys"
+version = "0.19.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e697e252d6e0416fd1d9e169bda51c0f1c926026c39ca21fbe8b1bb5c3b8b9e"
+dependencies = [
+ "glib-sys 0.19.8",
+ "libc",
+ "system-deps 6.2.2",
 ]
 
 [[package]]
@@ -399,9 +663,9 @@ version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fa3d1dcd8a1eb2e7c22be3d5e792b14b186f3524f79b25631730f9a8c169d49a"
 dependencies = [
- "glib-sys",
+ "glib-sys 0.20.1",
  "libc",
- "system-deps",
+ "system-deps 7.0.2",
 ]
 
 [[package]]
@@ -410,7 +674,7 @@ version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "80aac87f74e81c0e13433e892a047237abdc37945c86887f5eed905038356e69"
 dependencies = [
- "glib",
+ "glib 0.20.1",
  "graphene-sys",
  "libc",
 ]
@@ -421,10 +685,10 @@ version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cc2f91ecd32989efad60326cc20a8fb252bd2852239a08e4e70cde8c100de9ca"
 dependencies = [
- "glib-sys",
+ "glib-sys 0.20.1",
  "libc",
  "pkg-config",
- "system-deps",
+ "system-deps 7.0.2",
 ]
 
 [[package]]
@@ -435,7 +699,7 @@ checksum = "1f3cf2091e1af185b347b3450817d93dea6fe435df7abd4c2cd7fb5bcb4cfda8"
 dependencies = [
  "cairo-rs",
  "gdk4",
- "glib",
+ "glib 0.20.1",
  "graphene-rs",
  "gsk4-sys",
  "libc",
@@ -450,12 +714,12 @@ checksum = "6aa69614a26d8760c186c3690f1b0fbb917572ca23ef83137445770ceddf8cde"
 dependencies = [
  "cairo-sys-rs",
  "gdk4-sys",
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.20.1",
+ "gobject-sys 0.20.1",
  "graphene-sys",
  "libc",
  "pango-sys",
- "system-deps",
+ "system-deps 7.0.2",
 ]
 
 [[package]]
@@ -469,8 +733,8 @@ dependencies = [
  "futures-channel",
  "gdk-pixbuf",
  "gdk4",
- "gio",
- "glib",
+ "gio 0.20.1",
+ "glib 0.20.1",
  "graphene-rs",
  "gsk4",
  "gtk4-macros",
@@ -500,14 +764,14 @@ dependencies = [
  "cairo-sys-rs",
  "gdk-pixbuf-sys",
  "gdk4-sys",
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.20.1",
+ "glib-sys 0.20.1",
+ "gobject-sys 0.20.1",
  "graphene-sys",
  "gsk4-sys",
  "libc",
  "pango-sys",
- "system-deps",
+ "system-deps 7.0.2",
 ]
 
 [[package]]
@@ -529,6 +793,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
 
 [[package]]
+name = "hstr"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dae404c0c5d4e95d4858876ab02eecd6a196bb8caa42050dfa809938833fc412"
+dependencies = [
+ "hashbrown",
+ "new_debug_unreachable",
+ "once_cell",
+ "phf",
+ "rustc-hash",
+ "triomphe",
+]
+
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
 name = "indexmap"
 version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -539,6 +827,24 @@ dependencies = [
 ]
 
 [[package]]
+name = "is-macro"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2069faacbe981460232f880d26bf3c7634e322d49053aa48c27e3ae642f728f1"
+dependencies = [
+ "Inflector",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
 name = "js-sys"
 version = "0.3.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -548,14 +854,45 @@ dependencies = [
 ]
 
 [[package]]
+name = "kittybox-indieauth"
+version = "0.2.0"
+source = "git+https://git.vikanezrimaya.xyz/kittybox#b96e44f3526a6da09b5fd09617802190fa8fc134"
+dependencies = [
+ "data-encoding",
+ "rand",
+ "serde",
+ "sha2",
+ "url",
+]
+
+[[package]]
+name = "kittybox-util"
+version = "0.2.0"
+source = "git+https://git.vikanezrimaya.xyz/kittybox#b96e44f3526a6da09b5fd09617802190fa8fc134"
+dependencies = [
+ "async-trait",
+ "futures-util",
+ "serde",
+ "serde_json",
+ "url",
+ "uuid",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
 name = "libadwaita"
 version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2ff9c222b5c783729de45185f07b2fec2d43a7f9c63961e777d3667e20443878"
 dependencies = [
  "gdk4",
- "gio",
- "glib",
+ "gio 0.20.1",
+ "glib 0.20.1",
  "gtk4",
  "libadwaita-sys",
  "libc",
@@ -569,20 +906,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1c44d8bdbad31d6639e1f20cc9c1424f1a8e02d751fc28d44659bf743fb9eca6"
 dependencies = [
  "gdk4-sys",
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.20.1",
+ "glib-sys 0.20.1",
+ "gobject-sys 0.20.1",
  "gtk4-sys",
  "libc",
  "pango-sys",
- "system-deps",
+ "system-deps 7.0.2",
 ]
 
 [[package]]
 name = "libc"
-version = "0.2.157"
+version = "0.2.158"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "374af5f94e54fa97cf75e945cce8a6b201e88a1a07e688b47dfd2a59c66dbd86"
+checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
 
 [[package]]
 name = "libpanel"
@@ -592,8 +929,8 @@ checksum = "d7b5907dc844dc9cd243d810881e081d7e56088f28c08ac37fdc49d86e390238"
 dependencies = [
  "futures-core",
  "gdk4",
- "gio",
- "glib",
+ "gio 0.20.1",
+ "glib 0.20.1",
  "gtk4",
  "libadwaita",
  "libc",
@@ -606,13 +943,39 @@ version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6ddf5ff571cfd22c5cc9451a7897d6356905f3c37cb653360ac47b70e18faebb"
 dependencies = [
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.20.1",
+ "glib-sys 0.20.1",
+ "gobject-sys 0.20.1",
  "gtk4-sys",
  "libadwaita-sys",
  "libc",
- "system-deps",
+ "system-deps 7.0.2",
+]
+
+[[package]]
+name = "libsecret"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50c6ccddc706a38eca477b4d7857acd6c76c7d6fba5d47b4b2e7d800e5a17194"
+dependencies = [
+ "gio 0.19.8",
+ "glib 0.19.9",
+ "libc",
+ "libsecret-sys",
+]
+
+[[package]]
+name = "libsecret-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a1af48e61f1c8e77e9705296f346e45b637754a92348a79b4c62df84d0654c2"
+dependencies = [
+ "gio-sys 0.19.8",
+ "glib-sys 0.19.8",
+ "gobject-sys 0.19.8",
+ "libc",
+ "pkg-config",
+ "system-deps 6.2.2",
 ]
 
 [[package]]
@@ -647,6 +1010,44 @@ dependencies = [
 ]
 
 [[package]]
+name = "microformats"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed8bbf237d3068b89f1e445b9789bd0e5731638a018227722348cfc84b967fb8"
+dependencies = [
+ "lazy_static",
+ "microformats-types",
+ "regex",
+ "serde",
+ "serde_json",
+ "swc_common",
+ "swc_html_ast",
+ "swc_html_codegen",
+ "swc_html_parser",
+ "swc_html_visit",
+ "thiserror",
+ "time",
+ "tracing",
+ "tracing-unwrap",
+ "url",
+]
+
+[[package]]
+name = "microformats-types"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d4e0d8cf1d664df99cf57e6bcff23e4ee3e8676da1ede5c15ecb398783fe72a"
+dependencies = [
+ "lazy_static",
+ "regex",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "time",
+ "url",
+]
+
+[[package]]
 name = "miniz_oxide"
 version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -677,6 +1078,46 @@ dependencies = [
 ]
 
 [[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
+[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
 name = "object"
 version = "0.36.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -697,8 +1138,8 @@ version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5764e5a174a5a0ec054fe5962ce6d4fc7052e2d0dcc23bbc77202b40a4a403d3"
 dependencies = [
- "gio",
- "glib",
+ "gio 0.20.1",
+ "glib 0.20.1",
  "libc",
  "pango-sys",
 ]
@@ -709,10 +1150,10 @@ version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fd317e1de76b14b3d3efe05518c08b360327f1ab7fec150473a89ffcad4b072d"
 dependencies = [
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.20.1",
+ "gobject-sys 0.20.1",
  "libc",
- "system-deps",
+ "system-deps 7.0.2",
 ]
 
 [[package]]
@@ -739,6 +1180,30 @@ dependencies = [
 ]
 
 [[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "phf"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
+dependencies = [
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
 name = "pin-project-lite"
 version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -757,6 +1222,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
 
 [[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
 name = "proc-macro-crate"
 version = "3.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -784,6 +1264,36 @@ dependencies = [
 ]
 
 [[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
 name = "redox_syscall"
 version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -793,6 +1303,35 @@ dependencies = [
 ]
 
 [[package]]
+name = "regex"
+version = "1.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
 name = "relm4"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -835,6 +1374,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
 
 [[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
 name = "rustc_version"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -844,6 +1389,18 @@ dependencies = [
 ]
 
 [[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
 name = "scopeguard"
 version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -876,6 +1433,18 @@ dependencies = [
 ]
 
 [[package]]
+name = "serde_json"
+version = "1.0.125"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
 name = "serde_spanned"
 version = "0.6.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -885,6 +1454,29 @@ dependencies = [
 ]
 
 [[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
 name = "shlex"
 version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -900,6 +1492,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "siphasher"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
+[[package]]
 name = "slab"
 version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -925,6 +1523,32 @@ dependencies = [
 ]
 
 [[package]]
+name = "soup3"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b84ccd1f4aee0854a16b0b489ba843798e2eb4cdcddd4a61248f7db9ce8b6df1"
+dependencies = [
+ "futures-channel",
+ "gio 0.20.1",
+ "glib 0.20.1",
+ "libc",
+ "soup3-sys",
+]
+
+[[package]]
+name = "soup3-sys"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8869997193d52a61a1db48627bdaa57343f76e2c5132ee6d351245a6ab30631e"
+dependencies = [
+ "gio-sys 0.20.1",
+ "glib-sys 0.20.1",
+ "gobject-sys 0.20.1",
+ "libc",
+ "system-deps 7.0.2",
+]
+
+[[package]]
 name = "spin"
 version = "0.9.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -934,6 +1558,185 @@ dependencies = [
 ]
 
 [[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "string_enum"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05e383308aebc257e7d7920224fa055c632478d92744eca77f99be8fa1545b90"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "swc_macros_common",
+ "syn",
+]
+
+[[package]]
+name = "swc_atoms"
+version = "0.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb6567e4e67485b3e7662b486f1565bdae54bd5b9d6b16b2ba1a9babb1e42125"
+dependencies = [
+ "hstr",
+ "once_cell",
+ "rustc-hash",
+ "serde",
+]
+
+[[package]]
+name = "swc_common"
+version = "0.33.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2f9706038906e66f3919028f9f7a37f3ed552f1b85578e93f4468742e2da438"
+dependencies = [
+ "ast_node",
+ "better_scoped_tls",
+ "cfg-if",
+ "either",
+ "from_variant",
+ "new_debug_unreachable",
+ "num-bigint",
+ "once_cell",
+ "rustc-hash",
+ "serde",
+ "siphasher",
+ "swc_atoms",
+ "swc_eq_ignore_macros",
+ "swc_visit",
+ "tracing",
+ "unicode-width",
+ "url",
+]
+
+[[package]]
+name = "swc_eq_ignore_macros"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "swc_html_ast"
+version = "0.33.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59957df8048be691db04e6e358b29c6ff1274cd60ee2b4c2141e1d90b598d24e"
+dependencies = [
+ "is-macro",
+ "serde",
+ "string_enum",
+ "swc_atoms",
+ "swc_common",
+]
+
+[[package]]
+name = "swc_html_codegen"
+version = "0.42.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4c28899c6d01596124686dae5d139412488f4066d013a6c5691e497f5e9a98f"
+dependencies = [
+ "auto_impl",
+ "bitflags",
+ "rustc-hash",
+ "swc_atoms",
+ "swc_common",
+ "swc_html_ast",
+ "swc_html_codegen_macros",
+ "swc_html_utils",
+]
+
+[[package]]
+name = "swc_html_codegen_macros"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e593a6cbb3a49230fbab3171d4493f7d0fb1e20a34d9a9f9e972550690408ba8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "swc_macros_common",
+ "syn",
+]
+
+[[package]]
+name = "swc_html_parser"
+version = "0.39.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06cda5fcfd7c979a473be215cb7263e13bb7707bec46d0b6fbf364d203eea2d"
+dependencies = [
+ "swc_atoms",
+ "swc_common",
+ "swc_html_ast",
+ "swc_html_utils",
+]
+
+[[package]]
+name = "swc_html_utils"
+version = "0.18.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "406d1fcf69915a6726065060a9e85e7f36d66239708901d9bd0ad4d4b4e935a8"
+dependencies = [
+ "once_cell",
+ "serde",
+ "serde_json",
+ "swc_atoms",
+ "swc_common",
+]
+
+[[package]]
+name = "swc_html_visit"
+version = "0.33.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cffd11c6331e830a6f954597edd612d9393b6a4edb6e88004e5d7e84ee73b570"
+dependencies = [
+ "serde",
+ "swc_atoms",
+ "swc_common",
+ "swc_html_ast",
+ "swc_visit",
+]
+
+[[package]]
+name = "swc_macros_common"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "swc_visit"
+version = "0.5.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "043d11fe683dcb934583ead49405c0896a5af5face522e4682c16971ef7871b9"
+dependencies = [
+ "either",
+ "swc_visit_macros",
+]
+
+[[package]]
+name = "swc_visit_macros"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92807d840959f39c60ce8a774a3f83e8193c658068e6d270dbe0a05e40e90b41"
+dependencies = [
+ "Inflector",
+ "proc-macro2",
+ "quote",
+ "swc_macros_common",
+ "syn",
+]
+
+[[package]]
 name = "syn"
 version = "2.0.75"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -946,11 +1749,24 @@ dependencies = [
 
 [[package]]
 name = "system-deps"
+version = "6.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
+dependencies = [
+ "cfg-expr 0.15.8",
+ "heck",
+ "pkg-config",
+ "toml",
+ "version-compare",
+]
+
+[[package]]
+name = "system-deps"
 version = "7.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "070a0a5e7da2d24be457809c4b3baa57a835fd2829ad8b86f9a049052fe71031"
 dependencies = [
- "cfg-expr",
+ "cfg-expr 0.16.0",
  "heck",
  "pkg-config",
  "toml",
@@ -964,6 +1780,72 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
 
 [[package]]
+name = "thiserror"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "time"
+version = "0.3.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
 name = "tokio"
 version = "1.39.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1071,6 +1953,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "tracing-unwrap"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4e33415be97f5ae70322d6fefc696bbc08887d8835400d6c77f059469b30354"
+dependencies = [
+ "tracing",
+]
+
+[[package]]
 name = "tracker"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1091,18 +1982,79 @@ dependencies = [
 ]
 
 [[package]]
+name = "triomphe"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
+[[package]]
 name = "unicode-ident"
 version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
 [[package]]
+name = "unicode-normalization"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
+
+[[package]]
+name = "url"
+version = "2.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "uuid"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
+
+[[package]]
 name = "version-compare"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
 
 [[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1253,3 +2205,24 @@ checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
 dependencies = [
  "memchr",
 ]
+
+[[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 2260672..8d581ff 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,10 +6,19 @@ edition = "2021"
 [dependencies]
 adw = { version = "0.7.0", package = "libadwaita", features = ["v1_5"] }
 futures = "0.3.30"
+gio = { version = "0.20.1", features = ["v2_80"] }
 glib = { version = "0.20.1", features = ["log"] }
 gtk = { version = "0.9.0", package = "gtk4", features = ["gnome_46", "v4_14"] }
+kittybox-indieauth = { git = "https://git.vikanezrimaya.xyz/kittybox", version = "0.2.0" }
+kittybox-util = { git = "https://git.vikanezrimaya.xyz/kittybox", version = "0.2.0" }
+libsecret = { version = "0.5.0", features = ["v0_21_2"] }
 log = { version = "0.4.22", features = ["std"] }
+microformats = "0.9.1"
 relm4 = { version = "0.9.0", features = ["all", "gnome_46", "libpanel"] }
+serde_json = "1.0.125"
+serde_urlencoded = "0.7.1"
+soup3 = "0.7.0"
+thiserror = "1.0.63"
 tokio = { version = "1.39.3", features = ["full", "tracing"] }
 tracing = { version = "0.1.40", features = ["log"] }
 tracker = "0.2.2"
diff --git a/default.nix b/default.nix
index 24c43eb..8a55d17 100644
--- a/default.nix
+++ b/default.nix
@@ -1,6 +1,6 @@
 { craneLib, lib
 , pkg-config
-, gtk4, libadwaita, libpanel
+, gtk4, libadwaita, libpanel, libsoup_3, libsecret
 }:
 
 let
@@ -23,7 +23,7 @@ let
 
     # cargoExtraArgs can be used to inject features
 
-    buildInputs = [ gtk4 libadwaita libpanel ];
+    buildInputs = [ gtk4 libadwaita libpanel libsoup_3 libsecret ];
     nativeBuildInputs = [ pkg-config ];
 
     meta = with lib.meta; {
diff --git a/src/lib.rs b/src/lib.rs
index 5ee80c2..f5cb19e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,6 +5,8 @@ use gtk::GridLayoutChild;
 use relm4::{gtk, prelude::{AsyncComponent, AsyncComponentParts}, AsyncComponentSender, RelmWidgetExt};
 
 mod widgets;
+pub mod micropub;
+pub mod util;
 pub const APPLICATION_ID: &str = "xyz.vikanezrimaya.kittybox.Bowl";
 
 #[tracker::track]
@@ -13,6 +15,7 @@ pub struct PostComposerModel {
     /// Busy guard for generating the summary using an LLM.
     /// Makes the summary field read-only and blocks the Smart Summary button.
     #[no_eq] ai_summary_busy_guard: Option<gtk::gio::ApplicationBusyGuard>,
+    #[no_eq] submit_busy_guard: Option<gtk::gio::ApplicationBusyGuard>,
 
     #[do_not_track] name_buffer: gtk::EntryBuffer,
     #[do_not_track] summary_buffer: gtk::EntryBuffer,
@@ -20,6 +23,8 @@ pub struct PostComposerModel {
 
     #[do_not_track] wide_layout: gtk::GridLayout,
     #[do_not_track] narrow_layout: gtk::BoxLayout,
+
+    #[do_not_track] micropub: Arc<micropub::Client>,
 }
 
 impl PostComposerModel {
@@ -59,7 +64,7 @@ impl AsyncComponent for PostComposerModel {
     /// The type of the messages that this component can send.
     type Output = ();
     /// The type of data with which this component will be initialized.
-    type Init = ();
+    type Init = micropub::Client;
     /// The type of the command outputs that this component can receive.
     type CommandOutput = PostComposerCommandOutput;
  
@@ -74,6 +79,8 @@ impl AsyncComponent for PostComposerModel {
                         send_button = gtk::Button {
                             set_label: "Post",
                             connect_clicked => Self::Input::Submit,
+                            #[track = "model.changed(Self::submit_busy_guard())"]
+                            set_sensitive: model.submit_busy_guard.is_none(),
                         },
 
                         bar = adw::HeaderBar::new() {
@@ -106,6 +113,8 @@ impl AsyncComponent for PostComposerModel {
                         gtk::Entry {
                             set_hexpand: true,
                             set_buffer: &model.name_buffer,
+                            #[track = "model.changed(Self::submit_busy_guard())"]
+                            set_sensitive: model.submit_busy_guard.is_none(),
                         },
 
                         #[name = "summary_label"]
@@ -125,15 +134,15 @@ impl AsyncComponent for PostComposerModel {
                             gtk::Entry {
                                 set_hexpand: true,
                                 set_buffer: &model.summary_buffer,
-                                #[track = "model.changed(Self::ai_summary_busy_guard())"]
-                                set_sensitive: model.ai_summary_busy_guard.is_none(),
+                                #[track = "model.changed(Self::ai_summary_busy_guard() | Self::submit_busy_guard())"]
+                                set_sensitive: model.ai_summary_busy_guard.is_none() && model.submit_busy_guard.is_none(),
                             },
                             #[name = "ai_summary_button"]
                             gtk::Button {
                                 connect_clicked => Self::Input::AiGenSummaryBegin,
 
-                                #[track = "model.changed(Self::ai_summary_busy_guard())"]
-                                set_sensitive: model.ai_summary_busy_guard.is_none(),
+                                #[track = "model.changed(Self::ai_summary_busy_guard() | Self::submit_busy_guard())"]
+                                set_sensitive: model.ai_summary_busy_guard.is_none() && model.submit_busy_guard.is_none(),
                                 set_tooltip: "Smart Summary\nAsk a language model to summarize the content of your post in a single sentence.",
 
                                 gtk::Stack {
@@ -191,6 +200,9 @@ impl AsyncComponent for PostComposerModel {
                             set_right_margin: 8,
                             set_top_margin: 8,
                             set_bottom_margin: 8,
+
+                            #[track = "model.changed(Self::submit_busy_guard())"]
+                            set_sensitive: model.submit_busy_guard.is_none(),
                         },
                     },
 
@@ -221,6 +233,7 @@ impl AsyncComponent for PostComposerModel {
     ) -> AsyncComponentParts<Self> {
         let model = PostComposerModel {
             ai_summary_busy_guard: None,
+            submit_busy_guard: None,
 
             name_buffer: gtk::EntryBuffer::default(),
             summary_buffer: gtk::EntryBuffer::default(),
@@ -229,6 +242,8 @@ impl AsyncComponent for PostComposerModel {
             wide_layout: gtk::GridLayout::new(),
             narrow_layout: gtk::BoxLayout::new(gtk::Orientation::Vertical),
 
+            micropub: Arc::new(init),
+
             tracker: Default::default()
         };
         let widgets = view_output!();        
@@ -293,7 +308,51 @@ impl AsyncComponent for PostComposerModel {
                 self.summary_buffer.insert_text(self.summary_buffer.length(), text);
             },
             PostComposerInput::Submit => {
-                log::warn!("Submitting posts is not yet implemented.");
+                self.set_submit_busy_guard(
+                    Some(relm4::main_adw_application().mark_busy())
+                );
+                // Update view to lock the interface up
+                self.update_view(widgets, sender.clone());
+                self.reset();
+
+                use microformats::types::{Item, Class, KnownClass, PropertyValue};
+                let mut mf2 = Item::new(vec![Class::Known(KnownClass::Entry)]);
+                if self.name_buffer.length() > 0 {
+                    let proplist = mf2.properties.entry("name".to_owned()).or_default();
+                    proplist.push(PropertyValue::Plain(self.name_buffer.text().into()));
+                }
+                if self.summary_buffer.length() > 0 {
+                    let proplist = mf2.properties.entry("summary".to_owned()).or_default();
+                    proplist.push(PropertyValue::Plain(self.summary_buffer.text().into()));
+                }
+
+                // TODO: tags
+
+                {
+                    let proplist = mf2.properties.entry("content".to_owned()).or_default();
+                    proplist.push(PropertyValue::Plain(self.content_buffer.text(
+                        &self.content_buffer.start_iter(),
+                        &self.content_buffer.end_iter(),
+                        false
+                    ).into()));
+                }
+
+                log::warn!("sending post: {:?}", &mf2);
+                match self.micropub.send_post(mf2).await {
+                    Ok(location) => {
+                        self.name_buffer.set_text("");
+                        self.summary_buffer.set_text("");
+                        // TODO: tags
+                        self.content_buffer.set_text("");
+                        // TODO: display toast!
+                        log::warn!("post submitted: {}", location);
+                    },
+                    Err(err) => {
+                        // TODO: display error dialog
+                        log::warn!("error sending the post: {}", err);
+                    }
+                }
+                self.set_submit_busy_guard(None);
             },
         }
 
diff --git a/src/micropub.rs b/src/micropub.rs
new file mode 100644
index 0000000..e855459
--- /dev/null
+++ b/src/micropub.rs
@@ -0,0 +1,81 @@
+use soup::prelude::*;
+pub use kittybox_util::micropub::{Error as MicropubError, Config, QueryType};
+
+#[derive(Debug)]
+pub struct Client {
+    micropub: String,
+    access_token: String,
+
+    //http: soup::Session,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+    #[error("glib error: {0}")]
+    Glib(#[from] glib::Error),
+    #[error("json serialization error: {0}")]
+    Json(#[from] serde_json::Error),
+    #[error("micropub error: {0}")]
+    Micropub(#[from] MicropubError),
+    #[error("micropub server did not return a location: header")]
+    NoLocationHeader
+}
+
+impl Client {
+    pub fn new(uri: glib::Uri, token: String) -> Self {
+        Self {
+            micropub: uri.to_string(),
+            access_token: token,
+
+            //http: soup::Session::new()
+        }
+    }
+
+    pub async fn config(&self) -> Result<Config, Error> {
+        let uri = glib::Uri::parse(&self.micropub, glib::UriFlags::NONE).unwrap();
+        let uri = super::util::append_query(
+            &uri, [("q".to_string(), "config".to_string())]
+        );
+        
+        let exch = soup::Message::from_uri("GET", &uri);
+        let headers = exch.request_headers().expect("SoupMessage with no headers");
+        // TODO: create a SoupAuth subclass that allows pasting in a token
+        headers.append("Authorization", &format!("Bearer {}", self.access_token));
+
+        let http = soup::Session::new();
+        let body = http.send_and_read_future(&exch, glib::Priority::DEFAULT).await?;
+
+        Ok(serde_json::from_slice(&body)?)
+    }
+
+    pub async fn send_post(&self, post: microformats::types::Item) -> Result<glib::Uri, Error> {
+        let uri = glib::Uri::parse(&self.micropub, glib::UriFlags::NONE).unwrap();
+        let exch = soup::Message::from_uri("POST", &uri);
+        let headers = exch.request_headers().expect("SoupMessage with no headers");
+        headers.append("Authorization", &format!("Bearer {}", self.access_token));
+
+        exch.set_request_body_from_bytes(Some("application/json"),
+            Some(&glib::Bytes::from_owned(serde_json::to_vec(&post).unwrap()))
+        );
+
+        let http = soup::Session::new();
+        let body = http.send_and_read_future(&exch, glib::Priority::DEFAULT).await?;
+
+        match exch.status() {
+            soup::Status::Created | soup::Status::Accepted => {
+                let response_headers = exch.response_headers().expect("Successful SoupMessage with no response headers");
+                let location = response_headers.one("Location").ok_or(Error::NoLocationHeader)?;
+
+                Ok(glib::Uri::parse(&location, glib::UriFlags::NONE)?)
+            },
+            soup::Status::InternalServerError | soup::Status::BadGateway | soup::Status::ServiceUnavailable => {
+                todo!("micropub server is down")
+            },
+            _ => {
+                let error = serde_json::from_slice::<MicropubError>(&body)?;
+
+                Err(error.into())
+            }
+        }
+    }
+}
diff --git a/src/util.rs b/src/util.rs
new file mode 100644
index 0000000..c3d5bd7
--- /dev/null
+++ b/src/util.rs
@@ -0,0 +1,27 @@
+use std::borrow::Cow;
+
+pub fn append_query(uri: &glib::Uri, q: impl IntoIterator<Item = (String, String)>) -> glib::Uri {
+    let mut oq: Vec<(Cow<'static, str>, Cow<'static, str>)> = uri.query()
+        .map(|q| serde_urlencoded::from_str(&q).unwrap())
+        .unwrap_or_default();
+    oq.extend(q.into_iter().map(|(k, v)| (k.into(), v.into())));
+    let nq = "?".to_owned() + &serde_urlencoded::to_string(oq).unwrap();
+    uri.parse_relative(&nq, glib::UriFlags::NONE).unwrap()
+}
+
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn test_append_query() -> Result<(), glib::Error> {
+        let uri = glib::Uri::parse("https://fireburn.ru/.kittybox/micropub?test=a", glib::UriFlags::NONE)?;
+        let q = [
+            ("q".to_owned(), "config".to_owned()),
+            ("awoo".to_owned(), "nya".to_owned()),
+        ];
+        assert_eq!(
+            super::append_query(&uri, q).to_string().as_str(),
+            "https://fireburn.ru/.kittybox/micropub?test=a&q=config&awoo=nya"
+        );
+        Ok(())
+    }
+}