notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 968d9bc2452481a85ea4568181d68e7b005923dc
parent a7012754602a44e5a8da5b25f816afd7f55b379c
Author: William Casarin <jb55@jb55.com>
Date:   Sun, 23 Mar 2025 18:43:31 -0700

dave is alive

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
MCargo.lock | 840++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
MCargo.toml | 7++++++-
Mcrates/notedeck_chrome/Cargo.toml | 1+
Mcrates/notedeck_chrome/src/notedeck.rs | 52+++++++++++++++++++++++++++++++++++++++++++++++-----
Mcrates/notedeck_chrome/src/setup.rs | 4++++
Acrates/notedeck_dave/Cargo.toml | 16++++++++++++++++
Acrates/notedeck_dave/shader.wgsl | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/notedeck_dave/src/avatar.rs | 416+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/notedeck_dave/src/lib.rs | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 1585 insertions(+), 30 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -312,6 +312,43 @@ dependencies = [ ] [[package]] +name = "async-openai" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d76e2f5af19477d6254415acc95ba97c6cc6f3b1e3cb4676b7f0fab8194298" +dependencies = [ + "async-openai-macros", + "backoff", + "base64 0.22.1", + "bytes", + "derive_builder", + "eventsource-stream", + "futures", + "rand 0.8.5", + "reqwest", + "reqwest-eventsource", + "secrecy", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "async-openai-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0289cba6d5143bfe8251d57b4a8cac036adf158525a76533a7082ba65ec76398" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "async-process" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -449,6 +486,20 @@ dependencies = [ ] [[package]] +name = "backoff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +dependencies = [ + "futures-core", + "getrandom 0.2.15", + "instant", + "pin-project-lite", + "rand 0.8.5", + "tokio", +] + +[[package]] name = "backtrace" version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -529,7 +580,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn", "which", @@ -980,7 +1031,7 @@ dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "libc", ] @@ -1071,6 +1122,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" [[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] name = "data-encoding" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1092,6 +1178,37 @@ dependencies = [ ] [[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1379,6 +1496,15 @@ dependencies = [ ] [[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] name = "endi" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1524,6 +1650,17 @@ dependencies = [ ] [[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom", + "pin-project-lite", +] + +[[package]] name = "ewebsock" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1608,12 +1745,21 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -1629,6 +1775,12 @@ dependencies = [ [[package]] name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" @@ -1727,6 +1879,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1784,9 +1942,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -1952,6 +2112,25 @@ dependencies = [ ] [[package]] +name = "h2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] name = "half" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2047,6 +2226,29 @@ dependencies = [ ] [[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2059,6 +2261,80 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c3b1f728c459d27b12448862017b96ad4767b1ec2ec5e6434e99f1577f085b8" [[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] name = "icu_collections" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2177,6 +2453,12 @@ dependencies = [ ] [[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] name = "idna" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2287,6 +2569,12 @@ dependencies = [ ] [[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] name = "is-docker" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2593,7 +2881,7 @@ dependencies = [ "bitflags 2.9.0", "block", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "log", "objc", "paste", @@ -2667,7 +2955,7 @@ dependencies = [ "hexf-parse", "indexmap", "log", - "rustc-hash", + "rustc-hash 1.1.0", "spirv", "strum", "termcolor", @@ -2676,6 +2964,23 @@ dependencies = [ ] [[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework 2.11.1", + "security-framework-sys", + "tempfile", +] + +[[package]] name = "natord" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2922,6 +3227,7 @@ dependencies = [ "egui_extras", "notedeck", "notedeck_columns", + "notedeck_dave", "profiling", "puffin", "puffin_egui", @@ -2967,7 +3273,7 @@ dependencies = [ "puffin_egui", "rfd", "rmpv", - "security-framework", + "security-framework 2.11.1", "serde", "serde_derive", "serde_json", @@ -2987,6 +3293,22 @@ dependencies = [ ] [[package]] +name = "notedeck_dave" +version = "0.3.1" +dependencies = [ + "async-openai", + "bytemuck", + "eframe", + "egui", + "egui-wgpu", + "futures", + "notedeck", + "reqwest", + "tokio", + "tracing", +] + +[[package]] name = "nu-ansi-term" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3371,6 +3693,50 @@ dependencies = [ ] [[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 0.3.2", + "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", +] + +[[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.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3729,17 +4095,71 @@ dependencies = [ ] [[package]] -name = "quote" -version = "1.0.40" +name = "quinn" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.2.0" + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.1", + "rustls", + "socket2", + "thiserror 2.0.12", + "tokio", + "tracing", + "web-time 1.1.0", +] + +[[package]] +name = "quinn-proto" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" +dependencies = [ + "bytes", + "getrandom 0.3.2", + "rand 0.9.0", + "ring", + "rustc-hash 2.1.1", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.12", + "tinyvec", + "tracing", + "web-time 1.1.0", +] + +[[package]] +name = "quinn-udp" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" @@ -3965,6 +4385,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] +name = "reqwest" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tokio-util", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry", +] + +[[package]] +name = "reqwest-eventsource" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632c55746dbb44275691640e7b40c907c16a2dc1a5842aa98aaec90da6ec6bde" +dependencies = [ + "eventsource-stream", + "futures-core", + "futures-timer", + "mime", + "nom", + "pin-project-lite", + "reqwest", + "thiserror 1.0.69", +] + +[[package]] name = "resvg" version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4065,6 +4553,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4115,10 +4609,34 @@ dependencies = [ ] [[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.2.0", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] name = "rustls-pki-types" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +dependencies = [ + "web-time 1.1.0", +] [[package]] name = "rustls-webpki" @@ -4162,6 +4680,15 @@ dependencies = [ ] [[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] name = "scoped-tls" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4231,6 +4758,16 @@ dependencies = [ ] [[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "serde", + "zeroize", +] + +[[package]] name = "security-framework" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4244,6 +4781,19 @@ dependencies = [ ] [[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.10.0", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] name = "security-framework-sys" version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4313,6 +4863,18 @@ 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 = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4459,9 +5021,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", @@ -4498,6 +5060,12 @@ dependencies = [ ] [[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] name = "strum" version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4547,6 +5115,15 @@ dependencies = [ ] [[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] name = "synstructure" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4558,6 +5135,27 @@ dependencies = [ ] [[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] name = "system-deps" version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4776,6 +5374,16 @@ dependencies = [ ] [[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] name = "tokio-rustls" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4798,6 +5406,17 @@ dependencies = [ ] [[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] name = "tokio-tungstenite" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4814,6 +5433,19 @@ dependencies = [ ] [[package]] +name = "tokio-util" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] name = "toml" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4848,6 +5480,33 @@ dependencies = [ ] [[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4933,6 +5592,12 @@ dependencies = [ ] [[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] name = "ttf-parser" version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4984,7 +5649,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" dependencies = [ - "rustc-hash", + "rustc-hash 1.1.0", ] [[package]] @@ -5182,6 +5847,12 @@ 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 = "vec1" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5210,6 +5881,15 @@ dependencies = [ ] [[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5296,6 +5976,19 @@ dependencies = [ ] [[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] name = "wayland-backend" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5510,7 +6203,7 @@ dependencies = [ "parking_lot", "profiling", "raw-window-handle", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", "thiserror 2.0.12", "wgpu-hal", @@ -5550,7 +6243,7 @@ dependencies = [ "profiling", "raw-window-handle", "renderdoc-sys", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", "thiserror 2.0.12", "wasm-bindgen", @@ -5632,8 +6325,8 @@ checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ "windows-implement", "windows-interface", - "windows-result", - "windows-strings", + "windows-result 0.2.0", + "windows-strings 0.1.0", "windows-targets 0.52.6", ] @@ -5660,6 +6353,23 @@ dependencies = [ ] [[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result 0.3.2", + "windows-strings 0.3.1", + "windows-targets 0.53.0", +] + +[[package]] name = "windows-result" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5669,16 +6379,34 @@ dependencies = [ ] [[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] [[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + +[[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5753,7 +6481,7 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", @@ -5761,6 +6489,22 @@ dependencies = [ ] [[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5779,6 +6523,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5797,6 +6547,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5815,12 +6571,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5839,6 +6607,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5857,6 +6631,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5875,6 +6655,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5893,6 +6679,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] name = "winit" version = "0.30.9" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml @@ -5,8 +5,9 @@ members = [ "crates/notedeck", "crates/notedeck_chrome", "crates/notedeck_columns", + "crates/notedeck_dave", - "crates/enostr", "crates/tokenator", + "crates/enostr", "crates/tokenator", "crates/notedeck_dave", ] [workspace.dependencies] @@ -18,6 +19,7 @@ bitflags = "2.5.0" dirs = "5.0.1" eframe = { version = "0.31.1", default-features = false, features = [ "wgpu", "wayland", "x11", "android-game-activity" ] } egui = { version = "0.31.1", features = ["serde"] } +egui-wgpu = "0.31.1" egui_extras = { version = "0.31.1", features = ["all_loaders"] } egui-winit = { version = "0.31.1", features = ["android-game-activity", "clipboard"] } egui_nav = { git = "https://github.com/damus-io/egui-nav", rev = "5e816ac95e20f31dbb243a0d76179eab329a8ac0" } @@ -39,6 +41,7 @@ nostrdb = { git = "https://github.com/damus-io/nostrdb-rs", rev = "7a6af440c12e7 notedeck = { path = "crates/notedeck" } notedeck_chrome = { path = "crates/notedeck_chrome" } notedeck_columns = { path = "crates/notedeck_columns" } +notedeck_dave = { path = "crates/notedeck_dave" } tokenator = { path = "crates/tokenator" } open = "5.3.0" poll-promise = { version = "0.3.0", features = ["tokio"] } @@ -67,6 +70,7 @@ profiling = "1.0" lightning-invoice = "0.33.1" secp256k1 = "0.30.0" hashbrown = "0.15.2" +openai-api-rs = "6.0.3" [profile.small] inherits = 'release' @@ -87,6 +91,7 @@ strip = true # Strip symbols from binary* egui = { git = "https://github.com/damus-io/egui", rev = "93cd1cedc1e8eed2b055e317226838e37a845aad" } eframe = { git = "https://github.com/damus-io/egui", rev = "93cd1cedc1e8eed2b055e317226838e37a845aad" } egui-winit = { git = "https://github.com/damus-io/egui", rev = "93cd1cedc1e8eed2b055e317226838e37a845aad" } +egui-wgpu = { git = "https://github.com/damus-io/egui", rev = "93cd1cedc1e8eed2b055e317226838e37a845aad" } egui_extras = { git = "https://github.com/damus-io/egui", rev = "93cd1cedc1e8eed2b055e317226838e37a845aad" } epaint = { git = "https://github.com/damus-io/egui", rev = "93cd1cedc1e8eed2b055e317226838e37a845aad" } puffin = { git = "https://github.com/jb55/puffin", package = "puffin", rev = "c6a6242adaf90b6292c0f462d2acd34d96d224d2" } diff --git a/crates/notedeck_chrome/Cargo.toml b/crates/notedeck_chrome/Cargo.toml @@ -13,6 +13,7 @@ eframe = { workspace = true } egui_extras = { workspace = true } egui = { workspace = true } notedeck_columns = { workspace = true } +notedeck_dave = { workspace = true } notedeck = { workspace = true } puffin = { workspace = true, optional = true } puffin_egui = { workspace = true, optional = true } diff --git a/crates/notedeck_chrome/src/notedeck.rs b/crates/notedeck_chrome/src/notedeck.rs @@ -4,6 +4,7 @@ use notedeck_chrome::setup::{generate_native_options, setup_chrome}; use notedeck::{DataPath, DataPathType, Notedeck}; use notedeck_columns::Damus; +use notedeck_dave::Dave; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::EnvFilter; @@ -11,6 +12,38 @@ use tracing_subscriber::EnvFilter; //#[cfg(target_arch = "wasm32")] //use wasm_bindgen::prelude::*; +struct Chrome { + active: i32, + apps: Vec<Box<dyn notedeck::App>>, +} + +impl Chrome { + pub fn new() -> Self { + Chrome { + active: 0, + apps: vec![], + } + } + + pub fn add_app(&mut self, app: impl notedeck::App + 'static) { + self.apps.push(Box::new(app)); + } + + pub fn set_active(&mut self, app: i32) { + self.active = app; + } +} + +impl notedeck::App for Chrome { + fn update(&mut self, ctx: &mut notedeck::AppContext, ui: &mut egui::Ui) { + let active = self.active; + self.apps[active as usize].update(ctx, ui); + //for i in 0..self.apps.len() { + // self.apps[i].update(ctx, ui); + //} + } +} + fn setup_logging(path: &DataPath) -> Option<WorkerGuard> { #[allow(unused_variables)] // need guard to live for lifetime of program let (maybe_non_blocking, maybe_guard) = { @@ -78,15 +111,19 @@ async fn main() { Box::new(|cc| { let args: Vec<String> = std::env::args().collect(); let ctx = &cc.egui_ctx; + let mut notedeck = Notedeck::new(ctx, base_path, &args); - setup_chrome(ctx, notedeck.args(), notedeck.theme()); - let damus = Damus::new(&mut notedeck.app_context(), &args); + let mut chrome = Chrome::new(); + let columns = Damus::new(&mut notedeck.app_context(), &args); + let dave = Dave::new(cc.wgpu_render_state.as_ref()); + + setup_chrome(ctx, notedeck.args(), notedeck.theme()); // ensure we recognized all the arguments let completely_unrecognized: Vec<String> = notedeck .unrecognized_args() - .intersection(damus.unrecognized_args()) + .intersection(columns.unrecognized_args()) .cloned() .collect(); assert!( @@ -95,8 +132,13 @@ async fn main() { completely_unrecognized ); - // TODO: move "chrome" frame over Damus app somehow - notedeck.set_app(damus); + chrome.add_app(columns); + chrome.add_app(dave); + + // test dav + chrome.set_active(1); + + notedeck.set_app(chrome); Ok(Box::new(notedeck)) }), diff --git a/crates/notedeck_chrome/src/setup.rs b/crates/notedeck_chrome/src/setup.rs @@ -65,6 +65,8 @@ pub fn generate_native_options(paths: DataPath) -> NativeOptions { }); eframe::NativeOptions { + // for 3d widgets + depth_buffer: 24, window_builder: Some(window_builder), viewport: egui::ViewportBuilder::default().with_icon(std::sync::Arc::new( eframe::icon_data::from_png_bytes(app_icon()).expect("icon"), @@ -80,6 +82,8 @@ fn generate_native_options_with_builder_modifiers( Box::new(move |builder: egui::ViewportBuilder| apply_builder_modifiers(builder)); eframe::NativeOptions { + // for 3d widgets + depth_buffer: 24, window_builder: Some(window_builder), ..Default::default() } diff --git a/crates/notedeck_dave/Cargo.toml b/crates/notedeck_dave/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "notedeck_dave" +edition = "2021" +version.workspace = true + +[dependencies] +async-openai = "0.28.0" +egui = { workspace = true } +notedeck = { workspace = true } +eframe = { workspace = true } +tokio = { workspace = true } +tracing = { workspace = true } +egui-wgpu = { workspace = true } +bytemuck = "1.22.0" +futures = "0.3.31" +reqwest = "0.12.15" diff --git a/crates/notedeck_dave/shader.wgsl b/crates/notedeck_dave/shader.wgsl @@ -0,0 +1,68 @@ +struct VertexOut { + @location(0) color: vec4<f32>, + @builtin(position) position: vec4<f32>, +}; + +struct Uniforms { + @size(16) angle: f32, // pad to 16 bytes +}; + +@group(0) @binding(0) +var<uniform> uniforms: Uniforms; + +@vertex +fn vs_main(@builtin(vertex_index) v_idx: u32) -> VertexOut { + // Cube vertices hardcoded in the shader + var positions = array<vec3<f32>, 8>( + vec3<f32>(-0.5, -0.5, 0.5), // front bottom left + vec3<f32>(0.5, -0.5, 0.5), // front bottom right + vec3<f32>(0.5, 0.5, 0.5), // front top right + vec3<f32>(-0.5, 0.5, 0.5), // front top left + vec3<f32>(-0.5, -0.5, -0.5), // back bottom left + vec3<f32>(0.5, -0.5, -0.5), // back bottom right + vec3<f32>(0.5, 0.5, -0.5), // back top right + vec3<f32>(-0.5, 0.5, -0.5) // back top left + ); + + // Cube indices hardcoded in the shader + var indices = array<u32, 36>( + // front face + 0, 1, 2, 2, 3, 0, + // back face + 4, 5, 6, 6, 7, 4, + // right face + 1, 5, 6, 6, 2, 1, + // left face + 0, 4, 7, 7, 3, 0, + // top face + 3, 2, 6, 6, 7, 3, + // bottom face + 0, 1, 5, 5, 4, 0 + ); + + var out: VertexOut; + var idx = indices[v_idx]; + var pos = positions[idx]; + + // simple rotation around Y axis + var cosA = cos(uniforms.angle); + var sinA = sin(uniforms.angle); + var rotated_x = pos.x * cosA + pos.z * sinA; + var rotated_z = -pos.x * sinA + pos.z * cosA; + + // With proper perspective transformation: + var z_pos = rotated_z - 2.0; // Move cube away from camera + var w = -z_pos; // Set w to -z for perspective division + out.position = vec4<f32>(rotated_x, pos.y, rotated_z, w); + + // simple white shading based on position + var shade = 0.5 + 0.5 * (rotated_z + pos.y); + out.color = vec4<f32>(shade, shade, shade, 1.0); + + return out; +} + +@fragment +fn fs_main(in: VertexOut) -> @location(0) vec4<f32> { + return in.color; +} diff --git a/crates/notedeck_dave/src/avatar.rs b/crates/notedeck_dave/src/avatar.rs @@ -0,0 +1,416 @@ +use std::num::NonZeroU64; + +use eframe::egui_wgpu::{self, wgpu}; +use egui::{Rect, Response}; + +pub struct DaveAvatar { + rotation: Quaternion, +} + +// A simple quaternion implementation +struct Quaternion { + x: f32, + y: f32, + z: f32, + w: f32, +} + +impl Quaternion { + // Create identity quaternion + fn identity() -> Self { + Self { + x: 0.0, + y: 0.0, + z: 0.0, + w: 1.0, + } + } + + // Create from axis-angle representation + fn from_axis_angle(axis: [f32; 3], angle: f32) -> Self { + let half_angle = angle * 0.5; + let s = half_angle.sin(); + Self { + x: axis[0] * s, + y: axis[1] * s, + z: axis[2] * s, + w: half_angle.cos(), + } + } + + // Multiply two quaternions (combines rotations) + fn multiply(&self, other: &Self) -> Self { + Self { + x: self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y, + y: self.w * other.y - self.x * other.z + self.y * other.w + self.z * other.x, + z: self.w * other.z + self.x * other.y - self.y * other.x + self.z * other.w, + w: self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z, + } + } + + // Convert quaternion to 4x4 matrix (for 3D transformation with homogeneous coordinates) + fn to_matrix4(&self) -> [f32; 16] { + // Normalize quaternion + let magnitude = + (self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w).sqrt(); + let x = self.x / magnitude; + let y = self.y / magnitude; + let z = self.z / magnitude; + let w = self.w / magnitude; + + let x2 = x * x; + let y2 = y * y; + let z2 = z * z; + let xy = x * y; + let xz = x * z; + let yz = y * z; + let wx = w * x; + let wy = w * y; + let wz = w * z; + + // Row-major 3x3 rotation matrix components + let m00 = 1.0 - 2.0 * (y2 + z2); + let m01 = 2.0 * (xy - wz); + let m02 = 2.0 * (xz + wy); + + let m10 = 2.0 * (xy + wz); + let m11 = 1.0 - 2.0 * (x2 + z2); + let m12 = 2.0 * (yz - wx); + + let m20 = 2.0 * (xz - wy); + let m21 = 2.0 * (yz + wx); + let m22 = 1.0 - 2.0 * (x2 + y2); + + // Convert 3x3 rotation matrix to 4x4 transformation matrix + // Note: This is column-major for WGPU + [ + m00, m10, m20, 0.0, m01, m11, m21, 0.0, m02, m12, m22, 0.0, 0.0, 0.0, 0.0, 1.0, + ] + } +} + +// Matrix utilities for perspective projection +fn perspective_matrix(fovy_radians: f32, aspect: f32, near: f32, far: f32) -> [f32; 16] { + let f = 1.0 / (fovy_radians / 2.0).tan(); + let nf = 1.0 / (near - far); + + // Column-major for WGPU + [ + f / aspect, + 0.0, + 0.0, + 0.0, + 0.0, + f, + 0.0, + 0.0, + 0.0, + 0.0, + (far + near) * nf, + -1.0, + 0.0, + 0.0, + 2.0 * far * near * nf, + 0.0, + ] +} + +// Combine two 4x4 matrices (column-major) +fn matrix_multiply(a: &[f32; 16], b: &[f32; 16]) -> [f32; 16] { + let mut result = [0.0; 16]; + + for row in 0..4 { + for col in 0..4 { + let mut sum = 0.0; + for i in 0..4 { + sum += a[row + i * 4] * b[i + col * 4]; + } + result[row + col * 4] = sum; + } + } + + result +} + +impl DaveAvatar { + pub fn new(wgpu_render_state: &egui_wgpu::RenderState) -> Self { + let device = &wgpu_render_state.device; + + // Create shader module with improved shader code + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("cube_shader"), + source: wgpu::ShaderSource::Wgsl( + r#" +struct Uniforms { + model_view_proj: mat4x4<f32>, +}; + +@group(0) @binding(0) +var<uniform> uniforms: Uniforms; + +struct VertexOutput { + @builtin(position) position: vec4<f32>, + @location(0) color: vec4<f32>, +}; + +@vertex +fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput { + // Define cube vertices (-0.5 to 0.5 in each dimension) + var positions = array<vec3<f32>, 8>( + vec3<f32>(-0.5, -0.5, -0.5), // 0: left bottom back + vec3<f32>(0.5, -0.5, -0.5), // 1: right bottom back + vec3<f32>(-0.5, 0.5, -0.5), // 2: left top back + vec3<f32>(0.5, 0.5, -0.5), // 3: right top back + vec3<f32>(-0.5, -0.5, 0.5), // 4: left bottom front + vec3<f32>(0.5, -0.5, 0.5), // 5: right bottom front + vec3<f32>(-0.5, 0.5, 0.5), // 6: left top front + vec3<f32>(0.5, 0.5, 0.5) // 7: right top front + ); + + // Define indices for the 12 triangles (6 faces * 2 triangles) + var indices = array<u32, 36>( + // back face (Z-) + 0, 2, 1, 1, 2, 3, + // front face (Z+) + 4, 5, 6, 5, 7, 6, + // left face (X-) + 0, 4, 2, 2, 4, 6, + // right face (X+) + 1, 3, 5, 3, 7, 5, + // bottom face (Y-) + 0, 1, 4, 1, 5, 4, + // top face (Y+) + 2, 6, 3, 3, 6, 7 + ); + + // Define colors for each face + var face_colors = array<vec4<f32>, 6>( + vec4<f32>(1.0, 0.0, 0.0, 1.0), // back: red + vec4<f32>(0.0, 1.0, 0.0, 1.0), // front: green + vec4<f32>(0.0, 0.0, 1.0, 1.0), // left: blue + vec4<f32>(1.0, 1.0, 0.0, 1.0), // right: yellow + vec4<f32>(1.0, 0.0, 1.0, 1.0), // bottom: magenta + vec4<f32>(0.0, 1.0, 1.0, 1.0) // top: cyan + ); + + var output: VertexOutput; + + // Get vertex from indices + let index = indices[vertex_index]; + let position = positions[index]; + + // Determine which face this vertex belongs to + let face_index = vertex_index / 6u; + + // Apply model-view-projection matrix + output.position = uniforms.model_view_proj * vec4<f32>(position, 1.0); + + // Set color based on face + output.color = face_colors[face_index]; + + return output; +} + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { + return in.color; +} +"# + .into(), + ), + }); + + // Create uniform buffer for MVP matrix + let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("cube_uniform_buffer"), + size: 64, // 4x4 matrix of f32 (16 * 4 bytes) + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + // Create bind group layout + let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("cube_bind_group_layout"), + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: NonZeroU64::new(64), + }, + count: None, + }], + }); + + // Create bind group + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("cube_bind_group"), + layout: &bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: uniform_buffer.as_entire_binding(), + }], + }); + + // Create pipeline layout + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("cube_pipeline_layout"), + bind_group_layouts: &[&bind_group_layout], + push_constant_ranges: &[], + }); + + // Create render pipeline + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("cube_pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_main"), + buffers: &[], // No vertex buffer - vertices are in the shader + compilation_options: wgpu::PipelineCompilationOptions::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some("fs_main"), + targets: &[Some(wgpu::ColorTargetState { + format: wgpu_render_state.target_format, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: Some(wgpu::Face::Back), + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: wgpu::TextureFormat::Depth24Plus, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState::default(), + multiview: None, + cache: None, + }); + + // Store resources in renderer + wgpu_render_state + .renderer + .write() + .callback_resources + .insert(CubeRenderResources { + pipeline, + bind_group, + uniform_buffer, + }); + + Self { + rotation: Quaternion::identity(), + } + } +} + +impl DaveAvatar { + pub fn render(&mut self, rect: Rect, ui: &mut egui::Ui) -> Response { + let response = ui.allocate_rect(rect, egui::Sense::drag()); + + // Update rotation based on drag or animation + if response.dragged() { + // Create rotation quaternions based on drag + let x_rotation = + Quaternion::from_axis_angle([1.0, 0.0, 0.0], response.drag_delta().y * 0.01); + let y_rotation = + Quaternion::from_axis_angle([0.0, 1.0, 0.0], response.drag_delta().x * 0.01); + + // Apply rotations (order matters) + self.rotation = y_rotation.multiply(&x_rotation).multiply(&self.rotation); + } else { + // Continuous rotation - reduced speed and simplified axis + let continuous_rotation = Quaternion::from_axis_angle([0.0, 1.0, 0.0], 0.005); + self.rotation = continuous_rotation.multiply(&self.rotation); + } + + // Create model matrix from rotation quaternion + let model_matrix = self.rotation.to_matrix4(); + + // Create projection matrix with proper depth range + // Adjust aspect ratio based on rect dimensions + let aspect = rect.width() / rect.height(); + let projection = perspective_matrix(std::f32::consts::PI / 4.0, aspect, 0.1, 100.0); + + // Create view matrix (move camera back a bit) + let view_matrix = [ + 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -3.0, 1.0, + ]; + + // Combine matrices: projection * view * model + let mv_matrix = matrix_multiply(&view_matrix, &model_matrix); + let mvp_matrix = matrix_multiply(&projection, &mv_matrix); + + // Request continuous rendering + ui.ctx().request_repaint(); + + // Add paint callback + ui.painter().add(egui_wgpu::Callback::new_paint_callback( + rect, + CubeCallback { mvp_matrix }, + )); + + response + } +} + +// Callback implementation +struct CubeCallback { + mvp_matrix: [f32; 16], // Model-View-Projection matrix +} + +impl egui_wgpu::CallbackTrait for CubeCallback { + fn prepare( + &self, + _device: &wgpu::Device, + queue: &wgpu::Queue, + _screen_descriptor: &egui_wgpu::ScreenDescriptor, + _egui_encoder: &mut wgpu::CommandEncoder, + resources: &mut egui_wgpu::CallbackResources, + ) -> Vec<wgpu::CommandBuffer> { + let resources: &CubeRenderResources = resources.get().unwrap(); + + // Update uniform buffer with MVP matrix + queue.write_buffer( + &resources.uniform_buffer, + 0, + bytemuck::cast_slice(&self.mvp_matrix), + ); + + Vec::new() + } + + fn paint( + &self, + _info: egui::PaintCallbackInfo, + render_pass: &mut wgpu::RenderPass, + resources: &egui_wgpu::CallbackResources, + ) { + let resources: &CubeRenderResources = resources.get().unwrap(); + + render_pass.set_pipeline(&resources.pipeline); + render_pass.set_bind_group(0, &resources.bind_group, &[]); + render_pass.draw(0..36, 0..1); // 36 vertices for a cube (6 faces * 2 triangles * 3 vertices) + } +} + +// Simple resources struct +struct CubeRenderResources { + pipeline: wgpu::RenderPipeline, + bind_group: wgpu::BindGroup, + uniform_buffer: wgpu::Buffer, +} diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs @@ -0,0 +1,211 @@ +use async_openai::{ + config::OpenAIConfig, + types::{ + ChatCompletionRequestAssistantMessage, ChatCompletionRequestAssistantMessageContent, + ChatCompletionRequestMessage, ChatCompletionRequestUserMessage, + ChatCompletionRequestUserMessageContent, CreateChatCompletionRequest, + }, + Client, +}; +use futures::StreamExt; +use notedeck::AppContext; +use std::sync::mpsc::{self, Receiver}; + +use avatar::DaveAvatar; +use egui::{Rect, Vec2}; +use egui_wgpu::RenderState; + +mod avatar; + +#[derive(Debug, Clone)] +pub enum Message { + User(String), + Assistant(String), +} + +impl Message { + pub fn to_api_msg(&self) -> ChatCompletionRequestMessage { + match self { + Message::User(msg) => { + ChatCompletionRequestMessage::User(ChatCompletionRequestUserMessage { + name: None, + content: ChatCompletionRequestUserMessageContent::Text(msg.clone()), + }) + } + + Message::Assistant(msg) => { + ChatCompletionRequestMessage::Assistant(ChatCompletionRequestAssistantMessage { + content: Some(ChatCompletionRequestAssistantMessageContent::Text( + msg.clone(), + )), + ..Default::default() + }) + } + } + } +} + +pub struct Dave { + chat: Vec<Message>, + /// A 3d representation of dave. + avatar: Option<DaveAvatar>, + input: String, + pubkey: String, + client: async_openai::Client<OpenAIConfig>, + incoming_tokens: Option<Receiver<String>>, +} + +impl Dave { + pub fn new(render_state: Option<&RenderState>) -> Self { + let mut config = OpenAIConfig::new(); + if let Ok(api_key) = std::env::var("OPENAI_API_KEY") { + config = config.with_api_key(api_key); + } + let client = Client::with_config(config); + + let input = "".to_string(); + let pubkey = "test_pubkey".to_string(); + let avatar = render_state.map(DaveAvatar::new); + + Dave { + client, + pubkey, + avatar, + incoming_tokens: None, + input, + chat: vec![ + Message::User("how do I computer".to_string()), + Message::Assistant("Seriously?".to_string()), + Message::User("ye".to_string()), + ], + } + } + + fn render(&mut self, ui: &mut egui::Ui) { + if let Some(recvr) = &self.incoming_tokens { + if let Ok(token) = recvr.try_recv() { + match self.chat.last_mut() { + Some(Message::Assistant(msg)) => *msg = msg.clone() + " " + &token, + Some(_) => self.chat.push(Message::Assistant(token)), + None => {} + } + } + } + + // Scroll area for chat messages + egui::Frame::new().inner_margin(10.0).show(ui, |ui| { + egui::ScrollArea::vertical() + .stick_to_bottom(true) + .auto_shrink([false; 2]) + .show(ui, |ui| { + ui.vertical(|ui| { + self.render_chat(ui); + + self.inputbox(ui); + }) + }); + }); + + if let Some(avatar) = &mut self.avatar { + let avatar_size = Vec2::splat(400.0); + let pos = Vec2::splat(100.0).to_pos2(); + let pos = Rect::from_min_max(pos, pos + avatar_size); + avatar.render(pos, ui); + } + } + + fn render_chat(&self, ui: &mut egui::Ui) { + for message in &self.chat { + match message { + Message::User(msg) => self.user_chat(msg, ui), + Message::Assistant(msg) => self.system_chat(msg, ui), + } + } + } + + fn inputbox(&mut self, ui: &mut egui::Ui) { + ui.horizontal(|ui| { + ui.add(egui::TextEdit::multiline(&mut self.input)); + if ui.button("Sned").clicked() { + self.send_user_message(ui.ctx()); + self.input.clear(); + } + }); + } + + fn user_chat(&self, msg: &str, ui: &mut egui::Ui) { + ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| { + ui.label(msg); + }); + } + + fn system_chat(&self, msg: &str, ui: &mut egui::Ui) { + ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| { + ui.label(msg); + }); + } + + fn send_user_message(&mut self, ctx: &egui::Context) { + let messages = self.chat.iter().map(|c| c.to_api_msg()).collect(); + let pubkey = self.pubkey.clone(); + let (tx, rx) = mpsc::channel(); + self.incoming_tokens = Some(rx); + let ctx = ctx.clone(); + let client = self.client.clone(); + tokio::spawn(async move { + let mut token_stream = match client + .chat() + .create_stream(CreateChatCompletionRequest { + model: "gpt-4o".to_string(), + stream: Some(true), + messages, + user: Some(pubkey), + ..Default::default() + }) + .await + { + Err(err) => { + tracing::error!("openai chat error: {err}"); + return; + } + + Ok(stream) => stream, + }; + + tracing::info!("got stream!"); + + while let Some(token) = token_stream.next().await { + let token = match token { + Ok(token) => token, + Err(err) => { + tracing::error!("failed to get token: {err}"); + return; + } + }; + let Some(choice) = token.choices.first() else { + return; + }; + let Some(content) = &choice.delta.content else { + return; + }; + tracing::debug!("got token: {content}"); + + tx.send(content.to_owned()).unwrap(); + ctx.request_repaint(); + } + }); + } +} + +impl notedeck::App for Dave { + fn update(&mut self, _ctx: &mut AppContext<'_>, ui: &mut egui::Ui) { + /* + self.app + .frame_history + .on_new_frame(ctx.input(|i| i.time), frame.info().cpu_usage); + */ + + //update_dave(self, ctx, ui.ctx()); + self.render(ui); + } +}