finally another commit

This commit is contained in:
2025-07-23 23:56:07 +01:00
parent 224300f3ea
commit ba468cafa7
30 changed files with 1591 additions and 318 deletions
Generated
+667 -2
View File
@@ -112,6 +112,15 @@ dependencies = [
"winit",
]
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.1"
@@ -462,6 +471,27 @@ dependencies = [
"arrayvec",
]
[[package]]
name = "backtrace"
version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets 0.52.6",
]
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bit-set"
version = "0.8.0"
@@ -739,7 +769,7 @@ dependencies = [
"bitflags 1.3.2",
"core-foundation 0.9.4",
"core-graphics-types",
"foreign-types",
"foreign-types 0.5.0",
"libc",
]
@@ -1067,6 +1097,15 @@ dependencies = [
"serde",
]
[[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"
@@ -1253,12 +1292,27 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
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"
@@ -1266,7 +1320,7 @@ 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]]
@@ -1280,6 +1334,12 @@ dependencies = [
"syn",
]
[[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"
@@ -1295,6 +1355,16 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "futures-channel"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.31"
@@ -1331,6 +1401,12 @@ dependencies = [
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
version = "0.3.31"
@@ -1344,8 +1420,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
@@ -1394,6 +1473,12 @@ dependencies = [
"weezl",
]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "gl_generator"
version = "0.14.0"
@@ -1483,6 +1568,25 @@ dependencies = [
"gl_generator",
]
[[package]]
name = "h2"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "half"
version = "2.6.0"
@@ -1527,6 +1631,124 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "http"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[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"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[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.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
dependencies = [
"http",
"hyper",
"hyper-util",
"rustls",
"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.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
dependencies = [
"base64",
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"http",
"http-body",
"hyper",
"ipnet",
"libc",
"percent-encoding",
"pin-project-lite",
"socket2 0.6.0",
"system-configuration",
"tokio",
"tower-service",
"tracing",
"windows-registry",
]
[[package]]
name = "iana-time-zone"
version = "0.1.63"
@@ -1718,6 +1940,33 @@ dependencies = [
"syn",
]
[[package]]
name = "io-uring"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
dependencies = [
"bitflags 2.9.1",
"cfg-if",
"libc",
]
[[package]]
name = "ipnet"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]]
name = "iri-string"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "itertools"
version = "0.12.1"
@@ -1953,6 +2202,17 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "mio"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
"windows-sys 0.59.0",
]
[[package]]
name = "naga"
version = "25.0.1"
@@ -1977,6 +2237,23 @@ dependencies = [
"unicode-ident",
]
[[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",
"security-framework-sys",
"tempfile",
]
[[package]]
name = "ndk"
version = "0.9.0"
@@ -2392,12 +2669,65 @@ dependencies = [
"objc2-foundation 0.2.2",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "openssl"
version = "0.10.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
dependencies = [
"bitflags 2.9.1",
"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.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "orbclient"
version = "0.3.48"
@@ -2839,12 +3169,74 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
[[package]]
name = "reqwest"
version = "0.12.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
dependencies = [
"base64",
"bytes",
"encoding_rs",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-rustls",
"hyper-tls",
"hyper-util",
"js-sys",
"log",
"mime",
"native-tls",
"percent-encoding",
"pin-project-lite",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tokio-native-tls",
"tower",
"tower-http",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "rgb"
version = "0.8.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a457e416a0f90d246a4c3288bd7a25b2304ca727f253f95be383dd17af56be8f"
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.16",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rustc-demangle"
version = "0.1.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
@@ -2883,6 +3275,39 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "rustls"
version = "0.23.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1"
dependencies = [
"once_cell",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.21"
@@ -2904,6 +3329,15 @@ dependencies = [
"winapi-util",
]
[[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"
@@ -2929,6 +3363,29 @@ dependencies = [
"tiny-skia",
]
[[package]]
name = "security-framework"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.9.1",
"core-foundation 0.9.4",
"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"
checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "serde"
version = "1.0.219"
@@ -2981,6 +3438,18 @@ dependencies = [
"serde",
]
[[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 = "shlex"
version = "1.3.0"
@@ -3083,6 +3552,26 @@ dependencies = [
"serde",
]
[[package]]
name = "socket2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "socket2"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "somewhatusefultool"
version = "0.1.0"
@@ -3095,6 +3584,7 @@ dependencies = [
"egui_extras",
"egui_file",
"image",
"reqwest",
"serde",
"serde_json",
"thiserror 2.0.12",
@@ -3142,6 +3632,12 @@ dependencies = [
"syn",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.104"
@@ -3153,6 +3649,15 @@ dependencies = [
"unicode-ident",
]
[[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.2"
@@ -3164,6 +3669,27 @@ dependencies = [
"syn",
]
[[package]]
name = "system-configuration"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags 2.9.1",
"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"
@@ -3291,6 +3817,56 @@ dependencies = [
"zerovec",
]
[[package]]
name = "tokio"
version = "1.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
dependencies = [
"backtrace",
"bytes",
"io-uring",
"libc",
"mio",
"pin-project-lite",
"slab",
"socket2 0.5.10",
"windows-sys 0.52.0",
]
[[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"
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
dependencies = [
"rustls",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]]
name = "toml"
version = "0.8.23"
@@ -3325,6 +3901,51 @@ dependencies = [
"winnow",
]
[[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-http"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
dependencies = [
"bitflags 2.9.1",
"bytes",
"futures-util",
"http",
"http-body",
"iri-string",
"pin-project-lite",
"tower",
"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"
@@ -3356,6 +3977,12 @@ dependencies = [
"once_cell",
]
[[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"
@@ -3406,6 +4033,12 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.5.4"
@@ -3445,6 +4078,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version-compare"
version = "0.2.0"
@@ -3467,6 +4106,15 @@ dependencies = [
"winapi-util",
]
[[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.1+wasi-snapshot-preview1"
@@ -3916,6 +4564,17 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-registry"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e"
dependencies = [
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-result"
version = "0.3.4"
@@ -4522,6 +5181,12 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
[[package]]
name = "zerotrie"
version = "0.2.2"
+1
View File
@@ -22,6 +22,7 @@ thiserror = "2.0.12"
egui_commonmark = { version = "0.21.1", features = ["embedded_image"] }
walkdir = "2.5.0"
uuid = { version = "1.17.0", features = ["v4"] }
reqwest = { version = "0.12.22", features = ["blocking", "json"] }
[target.x86_64-pc-windows-gnu]
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

+6
View File
@@ -0,0 +1,6 @@
{
"date": "2025-07-17",
"project_name": "New Project",
"project_author": "Your Name",
"project_description": "Description of your project"
}
@@ -0,0 +1,8 @@
{
"title": "test",
"id": "83592caa-f97d-427e-9d6a-50a586c30e6e",
"description": "ee",
"tags": [],
"content": "# Test project\n\n- this project is a test to ensure that this tool can be integrated with AI models correctly\n- Im testing various prompts and parameters to evaluate its capabilities. The initial focus is on simple tasks like list generation, text summarization, and question answering. More complex scenarios involving code generation and creative writing will follow in subsequent phases. A key aspect of this test project involves documenting all interactions both the prompts used and the AIs responses for later analysis. This allows us to identify patterns, biases, and areas where the tool can be improved. ",
"parent": null
}
@@ -0,0 +1,23 @@
{
"id": "161227ef-ba29-41a7-b40a-ed4ac550a8ea",
"template_id": "69cf7e1d-96a1-4d2a-9f08-bd3386d4bc69",
"name": "nucleus",
"fields": {
"age": {
"Number": 0.0
},
"parent": {
"Link": ""
},
"dob": {
"Date": "1970-01-01"
},
"pfp": {
"Image": "characters/nucleus.png"
},
"description": {
"MultiLine": "an AI"
}
},
"tags": []
}
@@ -0,0 +1,23 @@
{
"id": "3ce0e977-9f65-4f4c-a036-67f3d5c25fdc",
"template_id": "69cf7e1d-96a1-4d2a-9f08-bd3386d4bc69",
"name": "ZXQ5",
"fields": {
"dob": {
"Date": "1970-01-01"
},
"description": {
"MultiLine": "yes"
},
"age": {
"Number": 19.1
},
"parent": {
"Link": ""
},
"pfp": {
"Image": "characters/zxq5.png"
}
},
"tags": []
}
@@ -0,0 +1,23 @@
{
"id": "57429207-5fc1-4bab-a524-c550773c3d45",
"template_id": "69cf7e1d-96a1-4d2a-9f08-bd3386d4bc69",
"name": "Tayles",
"fields": {
"pfp": {
"Image": "characters/tayles.png"
},
"parent": {
"Link": ""
},
"description": {
"MultiLine": "trainspotter"
},
"age": {
"Number": 17.5
},
"dob": {
"Date": "1970-01-01"
}
},
"tags": []
}
@@ -1,23 +0,0 @@
{
"id": "8d76fdcd-0c3e-41a9-abc4-66fe21c0cb73",
"template_id": "d1223e6b-ade0-405a-8c3b-657c743a21cc",
"name": "Prophet",
"fields": {
"Age": {
"value": ""
},
"Appearance": {
"value": ""
},
"Species": {
"value": ""
},
"DOB": {
"value": ""
},
"PFP": {
"value": "./project/assets/the prophet.png"
}
},
"tags": []
}
@@ -0,0 +1,25 @@
{
"id": "be24e58f-3f79-4c5a-9224-9037eea5f51f",
"template_id": "69cf7e1d-96a1-4d2a-9f08-bd3386d4bc69",
"name": "The Order",
"fields": {
"pfp": {
"Image": "characters/the order.png"
},
"description": {
"MultiLine": "yes"
},
"dob": {
"Date": "1970-01-29"
},
"parent": {
"Link": ""
},
"age": {
"Number": 20.6
}
},
"tags": [
"bbeddabd-914c-4648-8262-bf14bfcf8fff"
]
}
@@ -0,0 +1,25 @@
{
"id": "deeaee92-bdec-4eb3-bb3b-ee760fc83d45",
"template_id": "69cf7e1d-96a1-4d2a-9f08-bd3386d4bc69",
"name": "The Chancellor",
"fields": {
"age": {
"Number": 37.0
},
"parent": {
"Link": ""
},
"dob": {
"Date": "1970-01-01"
},
"description": {
"MultiLine": "a tall ahh american"
},
"pfp": {
"Image": "characters/the chancellor.jpg"
}
},
"tags": [
"bbeddabd-914c-4648-8262-bf14bfcf8fff"
]
}
@@ -0,0 +1,11 @@
{
"id": "bbeddabd-914c-4648-8262-bf14bfcf8fff",
"name": "American",
"description": "an american smh",
"color": [
0,
32,
207,
255
]
}
@@ -0,0 +1,32 @@
{
"name": "Species",
"id": "353649f9-e1f3-46d9-b723-8e56b510b2cc",
"description": "A classification system for living or digital entities.",
"fields": [
{
"name": "Diverged from",
"field_type": {
"Link": {
"template_id": null
}
},
"required": false,
"on_preview": false,
"description": "did this diverge from another documented species?"
},
{
"name": "Appearance / Features",
"field_type": "MultiLine",
"required": true,
"on_preview": true,
"description": "anatomy etc."
},
{
"name": "behaviour",
"field_type": "MultiLine",
"required": true,
"on_preview": true,
"description": "aggressive, collaborative, etc.."
}
]
}
@@ -0,0 +1,46 @@
{
"name": "Character",
"id": "69cf7e1d-96a1-4d2a-9f08-bd3386d4bc69",
"description": "a character",
"fields": [
{
"name": "description",
"field_type": "MultiLine",
"required": true,
"on_preview": true,
"description": "yes"
},
{
"name": "age",
"field_type": "Number",
"required": true,
"on_preview": false,
"description": "yes"
},
{
"name": "dob",
"field_type": "Date",
"required": true,
"on_preview": false,
"description": "yes"
},
{
"name": "parent",
"field_type": {
"Link": {
"template_id": null
}
},
"required": true,
"on_preview": false,
"description": "yes"
},
{
"name": "pfp",
"field_type": "Image",
"required": true,
"on_preview": true,
"description": "yes"
}
]
}
@@ -1,37 +0,0 @@
{
"name": "Character",
"id": "d1223e6b-ade0-405a-8c3b-657c743a21cc",
"description": "desc",
"fields": [
{
"name": "Appearance",
"field_type": "MultiLine",
"required": true,
"description": "app"
},
{
"name": "Age",
"field_type": "Number",
"required": true,
"description": "age"
},
{
"name": "DOB",
"field_type": "Date",
"required": false,
"description": "dob"
},
{
"name": "PFP",
"field_type": "Image",
"required": true,
"description": ""
},
{
"name": "Species",
"field_type": "Link",
"required": false,
"description": ""
}
]
}
+24 -10
View File
@@ -4,37 +4,51 @@ use crate::{PROJECT_FOLDER, util};
#[derive(Debug, Clone)]
pub struct Asset {
pub new_name: String,
pub name: String,
pub old_name: String,
pub saved: bool,
}
impl Asset {
pub fn open(name: String) -> Self {
Self {
old_name: name.clone(),
new_name: name.clone(),
name,
saved: false,
saved: true,
}
}
pub fn save(&mut self) {
let old_path = Self::path(&self.old_name);
let new_path = Self::path(&self.name);
let old_path = Self::path(&self.name);
let new_path = Self::path(&self.new_name);
println!("old_path: {old_path:?}");
println!("new_path: {new_path:?}");
// move from src dir to name path
std::fs::rename(&old_path, &new_path).unwrap();
if let Err(err) = std::fs::rename(&old_path, &new_path) {
match err.kind() {
std::io::ErrorKind::NotFound => {
let dir = new_path.parent().unwrap();
if !dir.exists() {
std::fs::create_dir_all(dir).unwrap();
}
std::fs::rename(&old_path, &new_path).unwrap();
}
_ => panic!("Failed to rename file: {err}"),
}
}
self.saved = true;
self.old_name = self.name.clone();
self.name = self.new_name.clone();
}
pub fn path(name: &str) -> std::path::PathBuf {
PROJECT_FOLDER.join("assets").join(format!("{name}.png"))
PROJECT_FOLDER.join("assets").join(name)
}
pub fn ui(&mut self, ui: &mut egui::Ui) {
ui.vertical(|ui| {
util::saved_status(ui, self.saved, &self.name, &self.name);
util::saved_status(ui, self.saved, &self.name, &self.new_name);
if ui.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl)
|| ui.button("Save").clicked()
@@ -46,7 +60,7 @@ impl Asset {
ui.horizontal(|ui| {
ui.strong("Filename:");
if TextEdit::singleline(&mut self.name)
if TextEdit::singleline(&mut self.new_name)
.desired_width(f32::INFINITY)
.frame(false)
.show(ui)
+34 -9
View File
@@ -1,8 +1,8 @@
use egui::TextEdit;
use egui::{TextEdit, text};
use egui_commonmark::{CommonMarkCache, CommonMarkViewer};
use serde::{self, Deserialize, Serialize};
use crate::{PROJECT_FOLDER, editors::tags::Tag, util};
use crate::{PROJECT_FOLDER, editors::tags::Tag, llm_integration::content_llm::ai_enabled, util};
pub struct MainEditor {
pub content: ContentSection,
@@ -95,7 +95,7 @@ impl MainEditor {
Self {
content: ContentSection::new(),
show_editor: false, // Start with editor hidden
show_preview: true,
show_preview: false,
preview_cache: CommonMarkCache::default(),
}
}
@@ -104,7 +104,7 @@ impl MainEditor {
Self {
content,
show_editor: true,
show_preview: true,
show_preview: false,
preview_cache: CommonMarkCache::default(),
}
}
@@ -264,7 +264,7 @@ impl MainEditor {
}
fn editor_ui(&mut self, ui: &mut egui::Ui) {
egui::ScrollArea::both()
let response = egui::ScrollArea::both()
.auto_shrink([false, false])
.id_salt("editor_scroll")
.show(ui, |ui| {
@@ -290,14 +290,39 @@ impl MainEditor {
.hint_text("Type here...")
.desired_width(max_width as f32);
if ui
let mut ctx_menu = false;
let response = ui
.add_sized(
egui::vec2(max_width as f32 - 30.0, ui.available_height()),
text_edit,
)
.changed()
{
self.content.saved = false;
.on_hover_text("Right click to open context menu")
.context_menu(|ui| {
ctx_menu = true;
ui.menu_button("AI Actions", |ui| {
ui.add_enabled_ui(ai_enabled(), |ui| {
if ui.button("Summarise").clicked() {
println!("Summarise");
}
if ui.button("Continue").clicked() {
let content = self.content.content.clone();
let response =
crate::llm_integration::content_llm::continue_content(
&content, "", 1024,
)
.unwrap();
self.content.content.push_str(&response);
}
});
});
});
if let Some(response) = response {
if response.response.changed() || ctx_menu {
self.content.saved = false;
}
}
});
});
+78
View File
@@ -0,0 +1,78 @@
use std::io::Read;
use chrono::NaiveDate;
use egui_extras::DatePickerButton;
use serde::{Deserialize, Serialize};
use crate::PROJECT_FOLDER;
#[derive(Serialize, Deserialize)]
pub struct ProjectContext {
date: NaiveDate,
project_name: String,
project_author: String,
project_description: String,
}
impl ProjectContext {
pub fn new() -> Self {
Self::default()
}
pub fn load() -> Self {
let path = PROJECT_FOLDER.join("context.json");
if let Ok(mut file) = std::fs::File::open(path) {
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
if let Ok(proj) = serde_json::from_str(&contents) {
return proj;
}
}
Self::default()
}
pub fn save(&self) {
let path = PROJECT_FOLDER.join("context.json");
let content = serde_json::to_string_pretty(self).unwrap();
std::fs::write(path, content).unwrap();
}
pub fn ui(&mut self, ui: &mut egui::Ui) {
// table
egui::Grid::new("context_editor")
.striped(true)
.num_columns(2)
.show(ui, |ui| {
ui.label("Project Name");
ui.text_edit_singleline(&mut self.project_name);
ui.end_row();
ui.label("Project Author");
ui.text_edit_singleline(&mut self.project_author);
ui.end_row();
ui.label("Project Description");
ui.text_edit_singleline(&mut self.project_description);
ui.end_row();
ui.label("Date");
ui.add(DatePickerButton::new(&mut self.date));
ui.end_row();
});
}
}
impl Default for ProjectContext {
fn default() -> Self {
Self {
date: chrono::Local::now().naive_local().into(),
project_name: "New Project".to_string(),
project_author: "Your Name".to_string(),
project_description: "Description of your project".to_string(),
}
}
}
+1
View File
@@ -1,5 +1,6 @@
pub mod asset_editor;
pub mod content_editor;
pub mod context_editor;
pub mod note_editor;
pub mod object_editor;
pub mod tags;
+45 -61
View File
@@ -1,23 +1,22 @@
use core::f32;
use std::path::Path;
use chrono::NaiveDate;
use egui::{CollapsingHeader, Response, RichText, Sense, TextEdit, Ui, UiBuilder, vec2};
use egui::{CollapsingHeader, RichText, Sense, TextEdit, Ui, UiBuilder, vec2};
use serde::{Deserialize, Serialize};
use crate::{
PROJECT_FOLDER, RightPanelContent,
editors::{
tags::Tag,
template_editor::{FieldDefinition, FieldType, FieldValue, Template},
template_editor::{FieldValue, Template},
},
util,
};
pub type ObjectId = String;
#[derive(Debug, Serialize, Deserialize)]
pub struct ObjectInstance {
// template info
pub id: String,
pub id: ObjectId,
pub template_id: String,
// instance info
@@ -67,7 +66,7 @@ impl ObjectInstance {
let mut fields = std::collections::HashMap::new();
for field in &template.fields {
fields.insert(field.name.clone(), FieldValue::default());
fields.insert(field.name.clone(), FieldValue::from_type(&field.field_type));
}
Self {
@@ -106,7 +105,7 @@ impl ObjectInstance {
ui: &mut Ui,
template: &Template,
right_panel: &mut Option<RightPanelContent>,
objects: &mut Vec<ObjectInstance>,
objects: &mut [ObjectInstance],
) {
let _ = right_panel;
if ui.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) {
@@ -193,13 +192,7 @@ impl ObjectInstance {
ui.separator();
Self::render_field(
field_def,
field_value,
ui,
&mut self.saved,
objects,
);
Self::render_field(field_value, ui, &mut self.saved, objects);
ui.separator();
});
@@ -210,27 +203,25 @@ impl ObjectInstance {
}
fn render_field(
field_def: &FieldDefinition,
field_value: &mut FieldValue,
ui: &mut egui::Ui,
saved: &mut bool,
objects: &mut Vec<ObjectInstance>,
objects: &mut [ObjectInstance],
) {
match field_def.field_type {
FieldType::SingleLine => {
if TextEdit::singleline(&mut field_value.value)
match field_value {
FieldValue::SingleLine(value) => {
if TextEdit::singleline(value)
.desired_width(f32::INFINITY)
.frame(false)
.show(ui)
.response
.changed()
{
field_value.modified = true;
*saved = false;
}
}
FieldType::MultiLine => {
if TextEdit::multiline(&mut field_value.value)
FieldValue::MultiLine(value) => {
if TextEdit::multiline(value)
.desired_width(f32::INFINITY)
.desired_rows(5)
.frame(false)
@@ -238,51 +229,39 @@ impl ObjectInstance {
.response
.changed()
{
field_value.modified = true;
*saved = false;
}
}
FieldType::Date => {
let date_str = &field_value.value;
let mut date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d")
.unwrap_or_else(|_| chrono::Local::now().date_naive());
let response = ui.add(egui_extras::DatePickerButton::new(&mut date));
FieldValue::Date(value) => {
let response = ui.add(egui_extras::DatePickerButton::new(value));
if response.changed() {
field_value.value = date.format("%Y-%m-%d").to_string();
field_value.modified = true;
*saved = false;
}
}
FieldType::Number => {
let mut num = field_value.value.parse::<f64>().unwrap_or(0.0);
let response = ui.add(egui::DragValue::new(&mut num).speed(0.1));
FieldValue::Number(value) => {
let response = ui.add(egui::DragValue::new(value).speed(0.1));
if response.changed() {
field_value.value = num.to_string();
field_value.modified = true;
*saved = false;
}
}
FieldType::Image => {
FieldValue::Image(value) => {
ui.scope_builder(UiBuilder::new().sense(Sense::HOVER), |ui| {
let id = ui.make_persistent_id("is_hovered");
let should_show = field_value.value.is_empty()
let should_show = value.is_empty()
|| ui.response().hovered()
|| ui.memory(|mem| mem.data.get_temp(id).unwrap_or(false));
|| ui.memory(|mem| mem.data.get_temp(id).unwrap_or(false))
|| !PROJECT_FOLDER.join("assets").join(&value).exists();
// Simple path input for now
if should_show {
let response = TextEdit::singleline(&mut field_value.value)
.hint_text("Path to image")
let response = TextEdit::singleline(value)
.hint_text("Asset name (ignore file extension)")
.desired_width(f32::INFINITY)
.frame(false)
.show(ui)
.response;
if response.changed() {
field_value.modified = true;
*saved = false;
}
@@ -292,10 +271,10 @@ impl ObjectInstance {
}
// If we have a valid path, try to display a preview
if !field_value.value.is_empty() {
if let Ok(bytes) = std::fs::read(&field_value.value) {
let path = PROJECT_FOLDER.join(&field_value.value);
if !value.is_empty() {
let path = PROJECT_FOLDER.join("assets").join(&value);
if let Ok(bytes) = std::fs::read(&path) {
let image_source = egui::ImageSource::Bytes {
uri: std::borrow::Cow::Owned(path.to_str().unwrap().to_string()),
bytes: bytes.into(),
@@ -307,10 +286,12 @@ impl ObjectInstance {
}
});
}
FieldType::Link => ObjectInstance::selector_ui(field_value, objects, ui, saved),
FieldType::Links => {
if ui.text_edit_singleline(&mut field_value.value).changed() {
field_value.modified = true;
FieldValue::Link(template_id) => {
ObjectInstance::selector_ui(template_id, objects, ui, saved)
}
FieldValue::Links(_template_ids) => {
let mut value = String::new();
if ui.text_edit_singleline(&mut value).changed() {
*saved = false;
}
}
@@ -318,13 +299,13 @@ impl ObjectInstance {
}
fn selector_ui(
field_value: &mut FieldValue,
objects: &mut Vec<ObjectInstance>,
selected: &mut ObjectId,
objects: &mut [ObjectInstance],
ui: &mut egui::Ui,
saved: &mut bool,
) {
if !field_value.value.is_empty() {
if let Ok(object) = ObjectInstance::load(&field_value.value) {
if !selected.is_empty() {
if let Ok(object) = ObjectInstance::load(selected) {
ui.strong(&object.name);
}
}
@@ -334,7 +315,7 @@ impl ObjectInstance {
let ctx = ui.ctx();
let mut object_selection: usize =
ctx.memory_mut(|mem| *mem.data.get_temp_mut_or_default::<usize>(id));
ctx.memory(|mem| mem.data.get_temp::<usize>(id).unwrap_or(0));
if objects.is_empty() {
ui.label("No objects available");
@@ -348,15 +329,18 @@ impl ObjectInstance {
});
}
let ctx = ui.ctx();
ctx.memory_mut(|mem| {
*mem.data.get_temp_mut_or_default::<usize>(id) = object_selection;
});
if ui.button("Set").clicked() && object_selection < objects.len() {
field_value.value = objects[object_selection].id.clone();
field_value.modified = true;
*selected = objects[object_selection].id.clone();
*saved = false;
}
if ui.button("Remove").clicked() {
field_value.value.clear();
field_value.modified = true;
*selected = String::new();
*saved = false;
}
});
+56 -9
View File
@@ -1,5 +1,6 @@
use core::fmt;
use chrono::NaiveDate;
use egui::ScrollArea;
use serde::{Deserialize, Serialize};
@@ -16,7 +17,7 @@ pub enum FieldType {
MultiLine,
Date,
Number,
Link,
Link { template_id: Option<String> },
Links,
}
@@ -34,17 +35,54 @@ impl FieldType {
FieldType::MultiLine,
FieldType::Date,
FieldType::Number,
FieldType::Link,
FieldType::Link { template_id: None },
FieldType::Links,
]
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FieldValue {
Image(String),
SingleLine(String),
MultiLine(String),
Date(NaiveDate),
Number(f64),
Link(String),
Links(Vec<String>),
}
impl FieldValue {
pub fn from_type(_type: &FieldType) -> Self {
match _type {
FieldType::Image => Self::Image(String::new()),
FieldType::SingleLine => Self::SingleLine(String::new()),
FieldType::MultiLine => Self::MultiLine(String::new()),
FieldType::Date => Self::Date(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()),
FieldType::Number => Self::Number(0.0),
FieldType::Link { template_id: None } => Self::Link(String::new()),
FieldType::Link {
template_id: Some(template_id),
} => Self::Link(template_id.clone()),
FieldType::Links => Self::Links(Vec::new()),
}
}
}
impl Default for FieldValue {
fn default() -> Self {
Self::SingleLine(String::new())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FieldDefinition {
pub name: String,
pub field_type: FieldType,
pub required: bool,
#[serde(default)]
pub on_preview: bool,
pub description: Option<String>,
}
@@ -73,6 +111,9 @@ pub struct Template {
#[serde(skip)]
pub new_field_description: String,
#[serde(skip)]
pub new_field_on_preview: bool,
}
impl fmt::Debug for Template {
@@ -105,6 +146,7 @@ impl Clone for Template {
new_field_type: FieldType::default(),
new_field_required: false,
new_field_description: "".to_string(),
new_field_on_preview: false,
}
}
}
@@ -123,6 +165,7 @@ impl Default for Template {
new_field_type: FieldType::default(),
new_field_required: false,
new_field_description: "".to_string(),
new_field_on_preview: false,
}
}
}
@@ -325,6 +368,12 @@ impl Template {
}
ui.end_row();
ui.label("On Preview:");
if ui.checkbox(&mut field.on_preview, "").clicked() {
self.saved = false;
}
ui.end_row();
ui.label("Description:");
if ui
.text_edit_singleline(
@@ -377,6 +426,10 @@ impl Template {
ui.checkbox(&mut self.new_field_required, "");
ui.end_row();
ui.label("On Preview:");
ui.checkbox(&mut self.new_field_on_preview, "");
ui.end_row();
ui.label("Description:");
ui.text_edit_singleline(&mut self.new_field_description);
ui.end_row();
@@ -385,6 +438,7 @@ impl Template {
self.fields.push(FieldDefinition {
name: self.new_field_name.clone(),
field_type: self.new_field_type.clone(),
on_preview: self.new_field_on_preview,
required: self.new_field_required,
description: if self.new_field_description.is_empty() {
None
@@ -404,10 +458,3 @@ impl Template {
});
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FieldValue {
pub value: String,
#[serde(skip)]
pub modified: bool,
}
+239 -155
View File
@@ -1,3 +1,5 @@
use walkdir::{DirEntry, WalkDir};
use crate::{
PROJECT_FOLDER, RightPanelContent,
content_editor::MainEditor,
@@ -14,7 +16,6 @@ pub struct Explorer {
notes: Vec<Note>,
documents: Vec<MainEditor>,
tags: Vec<Tag>,
assets: Vec<Asset>,
}
impl Explorer {
@@ -25,7 +26,6 @@ impl Explorer {
notes: Vec::new(),
documents: Vec::new(),
tags: Vec::new(),
assets: Vec::new(),
}
}
@@ -43,161 +43,122 @@ impl Explorer {
self.load_objects().expect("Failed to load objects");
self.load_notes().expect("Failed to load notes");
self.load_documents().expect("Failed to load documents");
self.load_assets().expect("Failed to load assets");
self.load_tags().expect("Failed to load tags");
ui.vertical(|ui| {
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
ui.make_persistent_id("templates"),
true,
)
.show_header(ui, |ui| {
ui.horizontal(|ui| {
ui.label("Templates");
if ui.button("+").clicked() {
*to_load = Some(RightPanelContent::template(Some(Template::default())));
}
});
})
.body(|ui| {
for template in &self.templates {
let id = ui.make_persistent_id(template.name.clone());
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
id,
true,
)
.show_header(ui, |ui| {
// load the template
if ui.selectable_label(false, template.name.clone()).clicked() {
*to_load = Some(RightPanelContent::template(Some(template.clone())));
}
self.render_templates(ui, to_load);
self.render_notes(ui, to_load);
self.render_doc_root(ui, load_doc);
self.render_tags(ui, to_load);
self.render_assets(ui, to_load);
});
}
// create a new object based on this template
if ui.button("+").clicked() {
*to_load = Some(RightPanelContent::object(Some(ObjectInstance::new(
template,
))));
}
})
.body(|ui| {
for object in &self.objects {
if object.template_id == template.id {
ui.horizontal(|ui| {
ui.add_space(10.0);
// load the object
if ui.selectable_label(false, &object.name).clicked() {
*to_load =
Some(RightPanelContent::object(Some(object.clone())));
}
});
}
}
});
fn render_templates(&mut self, ui: &mut egui::Ui, to_load: &mut Option<RightPanelContent>) {
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
ui.make_persistent_id("templates"),
true,
)
.show_header(ui, |ui| {
ui.horizontal(|ui| {
ui.label("Templates");
if ui.button("+").clicked() {
*to_load = Some(RightPanelContent::template(Some(Template::default())));
}
});
})
.body(|ui| {
for template in &self.templates {
let id = ui.make_persistent_id(template.name.clone());
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
id,
true,
)
.show_header(ui, |ui| {
// load the template
if ui.selectable_label(false, template.name.clone()).clicked() {
*to_load = Some(RightPanelContent::template(Some(template.clone())));
}
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
ui.make_persistent_id("notes"),
true,
)
.show_header(ui, |ui| {
ui.horizontal(|ui| {
ui.label("Notes");
// create a new object based on this template
if ui.button("+").clicked() {
*to_load = Some(RightPanelContent::note(Some(Note::default())));
*to_load = Some(RightPanelContent::object(Some(ObjectInstance::new(
template,
))));
}
})
.body(|ui| {
for object in &self.objects {
if object.template_id == template.id {
ui.horizontal(|ui| {
ui.add_space(10.0);
// load the object
if ui.selectable_label(false, &object.name).clicked() {
*to_load =
Some(RightPanelContent::object(Some(object.clone())));
}
});
}
}
});
})
.body(|ui| {
for note in &self.notes {
ui.horizontal(|ui| {
ui.add_space(10.0);
}
});
}
// load the note
if ui.selectable_label(false, &note.name).clicked() {
*to_load = Some(RightPanelContent::note(Some(note.clone())));
}
});
fn render_notes(&mut self, ui: &mut egui::Ui, to_load: &mut Option<RightPanelContent>) {
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
ui.make_persistent_id("notes"),
true,
)
.show_header(ui, |ui| {
ui.horizontal(|ui| {
ui.label("Notes");
if ui.button("+").clicked() {
*to_load = Some(RightPanelContent::note(Some(Note::default())));
}
});
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
ui.make_persistent_id("projects"),
true,
)
.show_header(ui, |ui| {
})
.body(|ui| {
for note in &self.notes {
ui.horizontal(|ui| {
ui.label("Projects");
if ui.button("+").clicked() {
*load_doc = Some(MainEditor::open(ContentSection::new()));
ui.add_space(10.0);
// load the note
if ui.selectable_label(false, &note.name).clicked() {
*to_load = Some(RightPanelContent::note(Some(note.clone())));
}
});
})
.body(|ui| {
// Convert MainEditor vec to ContentSection vec
let content_sections: Vec<ContentSection> = self
.documents
.iter()
.map(|doc| doc.content.clone())
.collect();
}
});
}
Self::render_document_tree(ui, &content_sections, None, load_doc);
});
self.tags = Tag::load_all();
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
ui.make_persistent_id("tags"),
true,
)
.show_header(ui, |ui| {
ui.horizontal(|ui| {
ui.label("Tags");
if ui.button("+").clicked() {
*to_load = Some(RightPanelContent::Tag(Tag::default()));
}
});
})
.body(|ui| {
for tag in &mut self.tags {
ui.horizontal(|ui| {
ui.add_space(10.0);
// load the tag
if tag.list_ui(ui).clicked() {
*to_load = Some(RightPanelContent::Tag(tag.clone()));
}
});
fn render_doc_root(&self, ui: &mut egui::Ui, load_doc: &mut Option<MainEditor>) {
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
ui.make_persistent_id("projects"),
true,
)
.show_header(ui, |ui| {
ui.horizontal(|ui| {
ui.label("Projects");
if ui.button("+").clicked() {
*load_doc = Some(MainEditor::open(ContentSection::new()));
}
});
})
.body(|ui| {
// Convert MainEditor vec to ContentSection vec
let content_sections: Vec<ContentSection> = self
.documents
.iter()
.map(|doc| doc.content.clone())
.collect();
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
ui.make_persistent_id("assets"),
true,
)
.show_header(ui, |ui| {
ui.horizontal(|ui| {
ui.label("Assets");
});
})
.body(|ui| {
for asset in &mut self.assets {
ui.horizontal(|ui| {
ui.add_space(10.0);
// load the asset
if ui.selectable_label(false, &asset.name).clicked() {
*to_load = Some(RightPanelContent::Asset(Box::new(asset.clone())));
}
});
}
});
Self::render_doc_branch(ui, &content_sections, None, load_doc);
});
}
@@ -209,7 +170,7 @@ impl Explorer {
///
/// `load_doc` is a mutable reference to a `MainEditor`. When a document is clicked, it
/// is loaded into the `MainEditor` and returned as `Some`.
fn render_document_tree(
fn render_doc_branch(
ui: &mut egui::Ui,
documents: &[ContentSection],
parent_id: Option<&str>,
@@ -243,15 +204,135 @@ impl Explorer {
})
.body(|ui| {
// recursive call to render the next level of documents
Self::render_document_tree(ui, documents, Some(&doc.id), load_doc);
Self::render_doc_branch(ui, documents, Some(&doc.id), load_doc);
});
}
}
fn render_tags(&mut self, ui: &mut egui::Ui, to_load: &mut Option<RightPanelContent>) {
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
ui.make_persistent_id("tags"),
true,
)
.show_header(ui, |ui| {
ui.horizontal(|ui| {
ui.label("Tags");
if ui.button("+").clicked() {
*to_load = Some(RightPanelContent::Tag(Tag::default()));
}
});
})
.body(|ui| {
for tag in &mut self.tags {
ui.horizontal(|ui| {
ui.add_space(10.0);
// load the tag
if tag.list_ui(ui).clicked() {
*to_load = Some(RightPanelContent::Tag(tag.clone()));
}
});
}
});
}
fn render_assets(&mut self, ui: &mut egui::Ui, to_load: &mut Option<RightPanelContent>) {
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
ui.make_persistent_id("assets"),
true,
)
.show_header(ui, |ui| {
ui.horizontal(|ui| {
ui.label("Assets");
});
})
.body(|ui| {
let mut entries: Vec<_> = WalkDir::new(PROJECT_FOLDER.join("assets"))
.min_depth(1)
.max_depth(1) // Only immediate children
.sort_by(|a, b| {
// Directories first, then files
let a_is_dir = a.file_type().is_dir();
let b_is_dir = b.file_type().is_dir();
if a_is_dir == b_is_dir {
a.file_name().cmp(b.file_name())
} else if a_is_dir {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Greater
}
})
.into_iter()
.filter_map(Result::ok)
.collect();
for entry in entries {
self.render_entry(ui, to_load, &entry);
}
});
}
fn render_entry(
&mut self,
ui: &mut egui::Ui,
to_load: &mut Option<RightPanelContent>,
entry: &DirEntry,
) {
let file_type = entry.file_type();
let is_dir = file_type.is_dir();
let file_name = entry.file_name().to_string_lossy();
let path = entry.path();
if is_dir {
let entries: Vec<_> = WalkDir::new(path)
.min_depth(1)
.max_depth(1)
.sort_by(|a, b| a.file_name().cmp(b.file_name()))
.into_iter()
.filter_map(Result::ok)
.collect();
egui::collapsing_header::CollapsingState::load_with_default_open(
ui.ctx(),
ui.make_persistent_id(&file_name),
false,
)
.show_header(ui, |ui| {
ui.horizontal(|ui| {
ui.label(file_name);
let clicked = ui.button("+").on_hover_text("Add new item").clicked();
});
})
.body(|ui| {
// recursive call to render the next level of documents
for entry in entries {
self.render_entry(ui, to_load, &entry);
}
});
} else {
// Handle file
if ui
.selectable_label(false, format!("📄 {file_name}"))
.clicked()
{
// use asset::load to get the file at the path
let asset_path = path.strip_prefix(PROJECT_FOLDER.join("assets")).unwrap();
let asset = Asset::open(asset_path.to_string_lossy().to_string());
*to_load = Some(RightPanelContent::Asset(Box::new(asset)));
}
}
}
// load templates from the templates folder
fn load_templates(&mut self) -> std::io::Result<()> {
let templates_folder = PROJECT_FOLDER.join("templates");
if !templates_folder.exists() {
std::fs::create_dir_all(&templates_folder)?;
}
let mut templates = Vec::new();
for entry in std::fs::read_dir(PROJECT_FOLDER.join("templates")).unwrap() {
for entry in std::fs::read_dir(&templates_folder).unwrap() {
let path = entry.unwrap().path();
match Template::load(path.file_stem().unwrap().to_str().unwrap()) {
Ok(t) => templates.push(t),
@@ -265,8 +346,12 @@ impl Explorer {
// load objects from the objects folder
fn load_objects(&mut self) -> std::io::Result<()> {
let objects_folder = PROJECT_FOLDER.join("objects");
if !objects_folder.exists() {
std::fs::create_dir_all(&objects_folder)?;
}
let mut objects = Vec::new();
for entry in std::fs::read_dir(PROJECT_FOLDER.join("objects")).unwrap() {
for entry in std::fs::read_dir(&objects_folder).unwrap() {
let path = entry.unwrap().path();
match ObjectInstance::load(path.file_stem().unwrap().to_str().unwrap()) {
Ok(o) => objects.push(o),
@@ -280,9 +365,13 @@ impl Explorer {
// load notes from the notes folder
fn load_notes(&mut self) -> std::io::Result<()> {
let notes_folder = PROJECT_FOLDER.join("notes");
if !notes_folder.exists() {
std::fs::create_dir_all(&notes_folder)?;
}
let mut notes = Vec::new();
for entry in std::fs::read_dir(PROJECT_FOLDER.join("notes")).unwrap() {
for entry in std::fs::read_dir(&notes_folder).unwrap() {
let path = entry.unwrap().path();
match Note::load(path.file_stem().unwrap().to_str().unwrap()) {
Ok(note) => notes.push(note),
@@ -297,9 +386,13 @@ impl Explorer {
// load documents from the documents folder
fn load_documents(&mut self) -> std::io::Result<()> {
let documents_folder = PROJECT_FOLDER.join("documents");
if !documents_folder.exists() {
std::fs::create_dir_all(&documents_folder)?;
}
let mut documents = Vec::new();
for entry in std::fs::read_dir(PROJECT_FOLDER.join("documents")).unwrap() {
for entry in std::fs::read_dir(&documents_folder).unwrap() {
let path = entry.unwrap().path();
match ContentSection::load(path.file_stem().unwrap().to_str().unwrap()) {
Ok(document) => documents.push(MainEditor::open(document)),
@@ -312,17 +405,8 @@ impl Explorer {
Ok(())
}
fn load_assets(&mut self) -> std::io::Result<()> {
let mut assets = Vec::new();
for entry in std::fs::read_dir(PROJECT_FOLDER.join("assets")).unwrap() {
let path = entry.unwrap().path();
assets.push(Asset::open(
path.file_stem().unwrap().to_str().unwrap().to_string(),
));
}
self.assets = assets;
fn load_tags(&mut self) -> std::io::Result<()> {
self.tags = Tag::load_all();
Ok(())
}
}
+79
View File
@@ -0,0 +1,79 @@
use serde::{Deserialize, Serialize};
pub fn continue_content(
context: &str,
instruction: &str,
max_tokens: usize,
) -> Result<String, Box<dyn std::error::Error>> {
let client = reqwest::blocking::Client::new();
let messages = vec![
Message {
role: "system".to_string(),
content: "
Please generate content that is a direct continuation of the given text.
Your response should be a logical next step in the content and should not repeat any of the text from the instruction or the content.
Do not generate any text that is not a direct continuation of the content.
if extra instructions are provided, follow them exactly, otherwise continue the text in a logical way.
".to_string(),
},
Message {
role: "user".to_string(),
content: context.to_string(),
},
Message {
role: "user".to_string(),
content: format!("Instructions: {instruction}"),
},
];
let request = ChatRequest {
messages,
temperature: 0.7,
};
let response = client
.post("http://localhost:1234/v1/chat/completions")
.json(&request)
.send()?;
if !response.status().is_success() {
return Err(format!("Request failed: {}", response.text()?).into());
}
let response: ChatResponse = response.json()?;
if let Some(choice) = response.choices.into_iter().next() {
Ok(choice.message.content)
} else {
Err("No response from model".into())
}
}
pub fn ai_enabled() -> bool {
let client = reqwest::blocking::Client::new();
client.get("http://localhost:1234/v1/models").send().is_ok()
}
// Simple request structure
#[derive(Serialize)]
struct ChatRequest {
messages: Vec<Message>,
temperature: f32,
}
#[derive(Serialize, Deserialize, Debug)]
struct Message {
role: String,
content: String,
}
#[derive(Deserialize, Debug)]
struct ChatResponse {
choices: Vec<Choice>,
}
#[derive(Deserialize, Debug)]
struct Choice {
message: Message,
}
+1
View File
@@ -0,0 +1 @@
pub mod content_llm;
+19 -7
View File
@@ -4,14 +4,15 @@ use egui::ScrollArea;
mod editors;
mod explorer;
mod llm_integration;
mod scene;
mod util;
use crate::{
editors::{
asset_editor::Asset, content_editor, note_editor, object_editor::ObjectInstance, tags::Tag,
template_editor::Template,
asset_editor::Asset, content_editor, context_editor::ProjectContext, note_editor,
object_editor::ObjectInstance, tags::Tag, template_editor::Template,
},
explorer::Explorer,
};
@@ -39,6 +40,7 @@ pub struct Interface {
editor: content_editor::MainEditor,
scene: scene::EditorScene,
explorer: Explorer,
project: ProjectContext,
}
impl eframe::App for Interface {
@@ -84,6 +86,7 @@ impl Interface {
editor: content_editor::MainEditor::new(),
scene: scene::EditorScene::new(),
explorer: Explorer::new(),
project: ProjectContext::load(),
}
}
@@ -183,28 +186,31 @@ impl Interface {
});
}
// render main content area
fn render_main_content(&mut self, ctx: &egui::Context) {
self.editor.ui(ctx);
self.scene.ui(ctx);
self.scene.ui(ctx, &mut self.explorer.objects());
}
// configure appearance of UI elements
fn configure_appearance(&self, ctx: &egui::Context) {
// configure appearance of UI elements
let mut visuals = egui::Visuals::dark();
visuals.window_fill = egui::Color32::from_rgb(20, 20, 20);
visuals.panel_fill = egui::Color32::from_rgb(20, 20, 20);
visuals.widgets.inactive.fg_stroke =
egui::Stroke::from((2.0, egui::Color32::from_rgb(255, 255, 255)));
egui::Stroke::from((1.0, egui::Color32::from_rgb(255, 255, 255)));
visuals.widgets.inactive.bg_stroke =
egui::Stroke::from((2.0, egui::Color32::from_rgb(60, 60, 60)));
egui::Stroke::from((1.0, egui::Color32::from_rgb(60, 60, 60)));
visuals.widgets.inactive.corner_radius = egui::CornerRadius::from(4);
visuals.widgets.inactive.bg_fill = egui::Color32::from_rgb(20, 20, 20);
visuals.widgets.inactive.weak_bg_fill = egui::Color32::from_rgb(20, 20, 20);
visuals.widgets.inactive.expansion = 2.0;
visuals.widgets.inactive.expansion = 1.0;
ctx.set_visuals(visuals);
// setup fonts.
let mut fonts = egui::FontDefinitions::default();
fonts.font_data.insert(
"JetBrains Mono Nerd Font".to_string(),
std::sync::Arc::new(egui::FontData::from_static(include_bytes!(
@@ -233,3 +239,9 @@ impl Default for Interface {
Self::new()
}
}
impl Drop for Interface {
fn drop(&mut self) {
self.project.save();
}
}
+125 -5
View File
@@ -1,3 +1,13 @@
use egui::{RichText, vec2};
use crate::{
PROJECT_FOLDER,
editors::{
object_editor::ObjectInstance,
template_editor::{FieldType, FieldValue, Template},
},
};
pub struct EditorScene {
rect: egui::Rect,
}
@@ -9,17 +19,127 @@ impl EditorScene {
}
}
pub fn ui(&mut self, ctx: &egui::Context) {
pub fn ui(&mut self, ctx: &egui::Context, objects: &mut [ObjectInstance]) {
egui::CentralPanel::default()
.frame(egui::Frame::NONE)
.show(ctx, |ui| {
egui::Scene::default()
.zoom_range(0.1..=10.0)
.show(ui, &mut self.rect, |ui| {
egui::Resize::default().auto_sized().show(ui, |ui| {
ui.group(|ui| {
ui.label("Scene");
});
ui.horizontal_wrapped(|ui| {
ui.set_max_width(5000.0);
// Group objects by their template_id
use std::collections::HashMap;
let mut objects_by_template: HashMap<String, Vec<&ObjectInstance>> =
HashMap::new();
for obj in objects {
objects_by_template
.entry(obj.template_id.clone())
.or_default()
.push(obj);
}
// For each template with objects, create cards
for (template_id, template_objects) in objects_by_template {
// Try to load the template to get field definitions
if let Ok(mut template) = Template::load(&template_id) {
for obj in template_objects {
// Create a card for each object
egui::Frame::group(ui.style())
.fill(egui::Color32::from_rgba_premultiplied(
30, 30, 30, 200,
))
.corner_radius(4.0)
.show(ui, |ui| {
ui.vertical(|ui| {
ui.set_max_width(512.0);
ui.set_min_width(512.0);
// Object name as header
ui.heading(RichText::new(&obj.name).strong());
// Show fields with on_preview = true
template.fields.sort_by_key(|field| field.field_type != FieldType::Image);
for field_def in &template.fields {
if field_def.on_preview {
if let Some(field_value) =
obj.fields.get(&field_def.name)
{
ui.separator();
match field_value {
FieldValue::SingleLine(
text,
) => {
ui.strong(&field_def.name);
ui.label(text);
}
FieldValue::MultiLine(
text,
) => {
ui.strong(&field_def.name);
ui.label(text);
}
FieldValue::Number(n) => {
ui.strong(&field_def.name);
ui.label(n.to_string());
}
FieldValue::Date(date) => {
ui.strong(&field_def.name);
ui.label(
date.format(
"%Y-%m-%d",
)
.to_string(),
);
}
FieldValue::Image(value) => {
if !value.is_empty() {
let path = PROJECT_FOLDER.join("assets").join(value);
if let Ok(bytes) = std::fs::read(&path) {
let image_source = egui::ImageSource::Bytes {
uri: std::borrow::Cow::Owned(path.to_str().unwrap().to_string()),
bytes: bytes.into(),
};
ui.add(
egui::Image::new(image_source).fit_to_exact_size(vec2(512.0, 512.0)),
);
}
}
}
FieldValue::Link(
target_id,
) => {
ui.strong(&field_def.name);
ui.label(format!(
"{target_id}"
));
}
FieldValue::Links(
links,
) => {
ui.strong(&field_def.name);
ui.label(format!(
"{} links",
links.len()
));
}
}
}
}
}
});
});
// Add some spacing between cards
ui.add_space(8.0);
}
}
}
});
});
});