Compare commits
20 Commits
6a25c6c05c
..
0.1.3
| Author | SHA1 | Date | |
|---|---|---|---|
| b6d0e10a7d | |||
| 8cf54f3346 | |||
| 874d7ff377 | |||
| 3c47ae1305 | |||
| 67d8902eaf | |||
| 2a7ec348c5 | |||
| 0653427557 | |||
| 6b6f65713d | |||
| c21819e786 | |||
| cc7eb3e7fb | |||
| 745e03a74f | |||
| e5a485d3a7 | |||
| 6c40f34122 | |||
| 5294feb5ff | |||
| 6832f1c5bc | |||
| 65213d3a9c | |||
| 73d5654e25 | |||
| 71f8f76f99 | |||
| 4d0e0c90a7 | |||
| bc71a30bfa |
@@ -9,17 +9,17 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
with:
|
# with:
|
||||||
target: x86_64-pc-windows-gnu
|
# target: x86_64-pc-windows-gnu
|
||||||
|
|
||||||
- run: cargo build --release
|
- run: cargo build --release
|
||||||
- run: cargo build --release --target x86_64-pc-windows-gnu
|
# - run: cargo build --release --target x86_64-pc-windows-gnu
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: christopherhx/gitea-upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: linux-release
|
name: linux-release
|
||||||
path: target/release/doc_writing_tool
|
path: target/release/worldcoder
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
# - uses: actions/upload-artifact@v4
|
||||||
with:
|
# with:
|
||||||
name: windows-release
|
# name: windows-release
|
||||||
path: target/x86_64-pc-windows-gnu/release/doc_writing_tool.exe
|
# path: target/x86_64-pc-windows-gnu/release/doc_writing_tool.exe
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
/target
|
/target
|
||||||
*/target
|
*/target
|
||||||
/project
|
/project
|
||||||
|
Cargo.lock
|
||||||
|
*.pkg.tar.zst
|
||||||
|
/pkg
|
||||||
|
/.config
|
||||||
|
|||||||
Generated
+26
-45
@@ -872,26 +872,6 @@ dependencies = [
|
|||||||
"libloading",
|
"libloading",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "doc_writing_tool"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"chrono",
|
|
||||||
"editor",
|
|
||||||
"eframe",
|
|
||||||
"egui",
|
|
||||||
"egui_commonmark",
|
|
||||||
"egui_extras",
|
|
||||||
"egui_file",
|
|
||||||
"image",
|
|
||||||
"reqwest",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror 2.0.12",
|
|
||||||
"uuid",
|
|
||||||
"walkdir",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "document-features"
|
name = "document-features"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
@@ -930,14 +910,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "editor"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"egui",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eframe"
|
name = "eframe"
|
||||||
version = "0.32.0"
|
version = "0.32.0"
|
||||||
@@ -1761,7 +1733,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2 0.6.0",
|
"socket2",
|
||||||
"system-configuration",
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
@@ -3428,9 +3400,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.140"
|
version = "1.0.141"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -3572,16 +3544,6 @@ dependencies = [
|
|||||||
"serde",
|
"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]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
@@ -3819,9 +3781,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.46.1"
|
version = "1.47.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
|
checksum = "43864ed400b6043a4757a25c7a64a8efde741aed79a056a2fb348a406701bb35"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -3830,8 +3792,8 @@ dependencies = [
|
|||||||
"mio",
|
"mio",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"slab",
|
"slab",
|
||||||
"socket2 0.5.10",
|
"socket2",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4950,6 +4912,25 @@ dependencies = [
|
|||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "worldcoder"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"eframe",
|
||||||
|
"egui",
|
||||||
|
"egui_commonmark",
|
||||||
|
"egui_extras",
|
||||||
|
"egui_file",
|
||||||
|
"image",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"uuid",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "writeable"
|
name = "writeable"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
|
|||||||
+2
-8
@@ -1,12 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "doc_writing_tool"
|
name = "worldcoder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
eframe = "0.32.0"
|
eframe = "0.32.0"
|
||||||
egui = { version = "0.32.0", features = ["serde"] }
|
egui = { version = "0.32.0", features = ["serde"] }
|
||||||
editor = { path = "./editor" }
|
|
||||||
egui_extras = { version = "0.32.0", features = [
|
egui_extras = { version = "0.32.0", features = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"datepicker",
|
"datepicker",
|
||||||
@@ -16,15 +15,10 @@ egui_extras = { version = "0.32.0", features = [
|
|||||||
egui_file = "0.23.0"
|
egui_file = "0.23.0"
|
||||||
image = { version = "0.25.6", features = ["jpeg", "png"] }
|
image = { version = "0.25.6", features = ["jpeg", "png"] }
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.141"
|
||||||
chrono = { version = "0.4.41", features = ["serde"] }
|
chrono = { version = "0.4.41", features = ["serde"] }
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
egui_commonmark = { version = "0.21.1", features = ["embedded_image"] }
|
egui_commonmark = { version = "0.21.1", features = ["embedded_image"] }
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
uuid = { version = "1.17.0", features = ["v4"] }
|
uuid = { version = "1.17.0", features = ["v4"] }
|
||||||
reqwest = { version = "0.12.22", features = ["blocking", "json"] }
|
reqwest = { version = "0.12.22", features = ["blocking", "json"] }
|
||||||
|
|
||||||
|
|
||||||
[target.x86_64-pc-windows-gnu]
|
|
||||||
linker = "x86_64-w64-mingw32-gcc"
|
|
||||||
ar = "x86_64-w64-mingw32-gcc-ar"
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
pkgname=worldcoder
|
||||||
|
pkgver=0.1.1
|
||||||
|
pkgrel=1
|
||||||
|
makedepends=('rust' 'cargo')
|
||||||
|
arch=('i686' 'x86_64' 'armv6h' 'armv7h')
|
||||||
|
|
||||||
|
# Generated in accordance to https://wiki.archlinux.org/title/Rust_package_guidelines.
|
||||||
|
# Might require further modification depending on the package involved.
|
||||||
|
prepare() {
|
||||||
|
cargo fetch --locked --target "$CARCH-unknown-linux-gnu"
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
export RUSTUP_TOOLCHAIN=stable
|
||||||
|
export CARGO_TARGET_DIR=target
|
||||||
|
cargo build --frozen --release --all-features
|
||||||
|
}
|
||||||
|
|
||||||
|
check() {
|
||||||
|
export RUSTUP_TOOLCHAIN=stable
|
||||||
|
cargo test --frozen --all-features
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/$pkgname"
|
||||||
|
}
|
||||||
Generated
-354
@@ -1,354 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 4
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ab_glyph"
|
|
||||||
version = "0.2.30"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e0f4f6fbdc5ee39f2ede9f5f3ec79477271a6d6a2baff22310d51736bda6cea"
|
|
||||||
dependencies = [
|
|
||||||
"ab_glyph_rasterizer",
|
|
||||||
"owned_ttf_parser",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ab_glyph_rasterizer"
|
|
||||||
version = "0.1.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ahash"
|
|
||||||
version = "0.8.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
"version_check",
|
|
||||||
"zerocopy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "2.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ecolor"
|
|
||||||
version = "0.32.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4a631732d995184114016fab22fc7e3faf73d6841c2d7650395fe251fbcd9285"
|
|
||||||
dependencies = [
|
|
||||||
"emath",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "editor"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"egui",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "egui"
|
|
||||||
version = "0.32.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8470210c95a42cc985d9ffebfd5067eea55bdb1c3f7611484907db9639675e28"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
"bitflags",
|
|
||||||
"emath",
|
|
||||||
"epaint",
|
|
||||||
"nohash-hasher",
|
|
||||||
"profiling",
|
|
||||||
"smallvec",
|
|
||||||
"unicode-segmentation",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "emath"
|
|
||||||
version = "0.32.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "45f057b141e7e46340c321400be74b793543b1b213036f0f989c35d35957c32e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "epaint"
|
|
||||||
version = "0.32.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94cca02195f0552c17cabdc02f39aa9ab6fbd815dac60ab1cd3d5b0aa6f9551c"
|
|
||||||
dependencies = [
|
|
||||||
"ab_glyph",
|
|
||||||
"ahash",
|
|
||||||
"ecolor",
|
|
||||||
"emath",
|
|
||||||
"epaint_default_fonts",
|
|
||||||
"nohash-hasher",
|
|
||||||
"parking_lot",
|
|
||||||
"profiling",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "epaint_default_fonts"
|
|
||||||
version = "0.32.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e8495e11ed527dff39663b8c36b6c2b2799d7e4287fb90556e455d72eca0b4d3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.174"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lock_api"
|
|
||||||
version = "0.4.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"scopeguard",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nohash-hasher"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.21.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "owned_ttf_parser"
|
|
||||||
version = "0.25.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4"
|
|
||||||
dependencies = [
|
|
||||||
"ttf-parser",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking_lot"
|
|
||||||
version = "0.12.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
"parking_lot_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking_lot_core"
|
|
||||||
version = "0.9.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"redox_syscall",
|
|
||||||
"smallvec",
|
|
||||||
"windows-targets",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.95"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "profiling"
|
|
||||||
version = "1.0.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.40"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.5.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scopeguard"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde"
|
|
||||||
version = "1.0.219"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
|
||||||
dependencies = [
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_derive"
|
|
||||||
version = "1.0.219"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smallvec"
|
|
||||||
version = "1.15.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.104"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ttf-parser"
|
|
||||||
version = "0.25.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-segmentation"
|
|
||||||
version = "1.12.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "version_check"
|
|
||||||
version = "0.9.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm",
|
|
||||||
"windows_aarch64_msvc",
|
|
||||||
"windows_i686_gnu",
|
|
||||||
"windows_i686_gnullvm",
|
|
||||||
"windows_i686_msvc",
|
|
||||||
"windows_x86_64_gnu",
|
|
||||||
"windows_x86_64_gnullvm",
|
|
||||||
"windows_x86_64_msvc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
|
||||||
|
|
||||||
[[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_msvc"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.52.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy"
|
|
||||||
version = "0.8.26"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy-derive"
|
|
||||||
version = "0.8.26"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "editor"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
description = "a basic text editor widget with line numbers"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
egui = "0.32.0"
|
|
||||||
serde = "1"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "editor"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
use egui::Color32;
|
|
||||||
use egui::TextBuffer;
|
|
||||||
use egui::widgets::text_edit::TextEditOutput;
|
|
||||||
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
/// CodeEditor struct which stores settings for highlighting.
|
|
||||||
pub struct CodeEditor {
|
|
||||||
id: String,
|
|
||||||
numlines: bool,
|
|
||||||
numlines_shift: isize,
|
|
||||||
numlines_only_natural: bool,
|
|
||||||
fontsize: f32,
|
|
||||||
stick_to_bottom: bool,
|
|
||||||
desired_width: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for CodeEditor {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
|
||||||
(self.fontsize as u32).hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CodeEditor {
|
|
||||||
fn default() -> CodeEditor {
|
|
||||||
CodeEditor {
|
|
||||||
id: String::from("Code Editor"),
|
|
||||||
numlines: true,
|
|
||||||
numlines_shift: 0,
|
|
||||||
numlines_only_natural: false,
|
|
||||||
fontsize: 10.0,
|
|
||||||
stick_to_bottom: false,
|
|
||||||
desired_width: f32::INFINITY,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CodeEditor {
|
|
||||||
pub fn id_source(self, id_source: impl Into<String>) -> Self {
|
|
||||||
CodeEditor {
|
|
||||||
id: id_source.into(),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Use custom font size
|
|
||||||
///
|
|
||||||
/// **Default: 10.0**
|
|
||||||
pub fn with_fontsize(self, fontsize: f32) -> Self {
|
|
||||||
CodeEditor { fontsize, ..self }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Use UI font size
|
|
||||||
pub fn with_ui_fontsize(self, ui: &mut egui::Ui) -> Self {
|
|
||||||
CodeEditor {
|
|
||||||
fontsize: egui::TextStyle::Monospace.resolve(ui.style()).size,
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show or hide lines numbering
|
|
||||||
///
|
|
||||||
/// **Default: true**
|
|
||||||
pub fn with_numlines(self, numlines: bool) -> Self {
|
|
||||||
CodeEditor { numlines, ..self }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shift lines numbering by this value
|
|
||||||
///
|
|
||||||
/// **Default: 0**
|
|
||||||
pub fn with_numlines_shift(self, numlines_shift: isize) -> Self {
|
|
||||||
CodeEditor {
|
|
||||||
numlines_shift,
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show lines numbering only above zero, useful for enabling numbering since nth row
|
|
||||||
///
|
|
||||||
/// **Default: false**
|
|
||||||
pub fn with_numlines_only_natural(self, numlines_only_natural: bool) -> Self {
|
|
||||||
CodeEditor {
|
|
||||||
numlines_only_natural,
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Should the containing area shrink if the content is small?
|
|
||||||
///
|
|
||||||
/// **Default: false**
|
|
||||||
pub fn auto_shrink(self, shrink: bool) -> Self {
|
|
||||||
CodeEditor {
|
|
||||||
desired_width: if shrink { 0.0 } else { self.desired_width },
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the desired width of the code editor
|
|
||||||
///
|
|
||||||
/// **Default: `f32::INFINITY`**
|
|
||||||
pub fn desired_width(self, width: f32) -> Self {
|
|
||||||
CodeEditor {
|
|
||||||
desired_width: width,
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stick to bottom
|
|
||||||
/// The scroll handle will stick to the bottom position even while the content size
|
|
||||||
/// changes dynamically. This can be useful to simulate terminal UIs or log/info scrollers.
|
|
||||||
/// The scroll handle remains stuck until user manually changes position. Once "unstuck"
|
|
||||||
/// it will remain focused on whatever content viewport the user left it on. If the scroll
|
|
||||||
/// handle is dragged to the bottom it will again become stuck and remain there until manually
|
|
||||||
/// pulled from the end position.
|
|
||||||
///
|
|
||||||
/// **Default: false**
|
|
||||||
pub fn stick_to_bottom(self, stick_to_bottom: bool) -> Self {
|
|
||||||
CodeEditor {
|
|
||||||
stick_to_bottom,
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn format(&self, _string: &str) -> egui::text::TextFormat {
|
|
||||||
let font_id = egui::FontId::monospace(self.fontsize);
|
|
||||||
let color = Color32::WHITE;
|
|
||||||
egui::text::TextFormat::simple(font_id, color)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn numlines_show(&self, ui: &mut egui::Ui, text: &str) {
|
|
||||||
let total = if text.ends_with('\n') || text.is_empty() {
|
|
||||||
text.lines().count() + 1
|
|
||||||
} else {
|
|
||||||
text.lines().count()
|
|
||||||
} as isize;
|
|
||||||
|
|
||||||
let max_indent = total
|
|
||||||
.to_string()
|
|
||||||
.len()
|
|
||||||
.max(!self.numlines_only_natural as usize * self.numlines_shift.to_string().len());
|
|
||||||
let mut counter = (1..=total)
|
|
||||||
.map(|i| {
|
|
||||||
let num = i + self.numlines_shift;
|
|
||||||
if num <= 0 && self.numlines_only_natural {
|
|
||||||
String::new()
|
|
||||||
} else {
|
|
||||||
let label = num.to_string();
|
|
||||||
format!(
|
|
||||||
"{}{label}",
|
|
||||||
" ".repeat(max_indent.saturating_sub(label.len()))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
#[allow(clippy::cast_precision_loss)]
|
|
||||||
let width = max_indent as f32
|
|
||||||
* self.fontsize
|
|
||||||
* 0.5
|
|
||||||
* !(total + self.numlines_shift <= 0 && self.numlines_only_natural) as u8 as f32;
|
|
||||||
|
|
||||||
let mut layouter = |ui: &egui::Ui, string: &dyn TextBuffer, _wrap_width: f32| {
|
|
||||||
let layout_job = egui::text::LayoutJob::single_section(
|
|
||||||
string.as_str().to_string(), // Convert TextBuffer to String
|
|
||||||
egui::TextFormat::simple(egui::FontId::monospace(self.fontsize), Color32::WHITE),
|
|
||||||
);
|
|
||||||
ui.fonts(|f| f.layout_job(layout_job))
|
|
||||||
};
|
|
||||||
|
|
||||||
ui.add(
|
|
||||||
egui::TextEdit::multiline(&mut counter)
|
|
||||||
.id_source(format!("{}_numlines", self.id))
|
|
||||||
.font(egui::TextStyle::Monospace)
|
|
||||||
.interactive(false)
|
|
||||||
.frame(false)
|
|
||||||
.desired_width(width)
|
|
||||||
.layouter(&mut layouter),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show Code Editor
|
|
||||||
pub fn show(&mut self, ui: &mut egui::Ui, text: &mut dyn egui::TextBuffer) -> TextEditOutput {
|
|
||||||
let mut text_edit_output: Option<TextEditOutput> = None;
|
|
||||||
let code_editor = |ui: &mut egui::Ui| {
|
|
||||||
ui.horizontal_top(|h| {
|
|
||||||
if self.numlines {
|
|
||||||
self.numlines_show(h, text.as_str());
|
|
||||||
}
|
|
||||||
egui::ScrollArea::horizontal()
|
|
||||||
.hscroll(true)
|
|
||||||
.id_salt(format!("{}_inner_scroll", self.id))
|
|
||||||
.show(h, |ui| {
|
|
||||||
let output = egui::TextEdit::multiline(text)
|
|
||||||
.id_source(&self.id)
|
|
||||||
.lock_focus(true)
|
|
||||||
.frame(false)
|
|
||||||
.desired_width(self.desired_width)
|
|
||||||
.show(ui);
|
|
||||||
text_edit_output = Some(output);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
egui::ScrollArea::vertical()
|
|
||||||
.id_salt(format!("{}_outer_scroll", self.id))
|
|
||||||
.stick_to_bottom(self.stick_to_bottom)
|
|
||||||
.show(ui, code_editor);
|
|
||||||
|
|
||||||
text_edit_output.expect("TextEditOutput should exist at this point")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 254 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 145 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 157 KiB |
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"date": "2025-07-17",
|
|
||||||
"project_name": "New Project",
|
|
||||||
"project_author": "Your Name",
|
|
||||||
"project_description": "Description of your project"
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"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- I’m 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 AI’s responses – for later analysis. This allows us to identify patterns, biases, and areas where the tool can be improved. ",
|
|
||||||
"parent": null
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"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": []
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"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": []
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"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,25 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "bbeddabd-914c-4648-8262-bf14bfcf8fff",
|
|
||||||
"name": "American",
|
|
||||||
"description": "an american smh",
|
|
||||||
"color": [
|
|
||||||
0,
|
|
||||||
32,
|
|
||||||
207,
|
|
||||||
255
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"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.."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
+6
-6
@@ -24,9 +24,9 @@
|
|||||||
|
|
||||||
### v0.1.1 - Editor
|
### v0.1.1 - Editor
|
||||||
|
|
||||||
- [ ] Basic editor (markdown formatting)
|
- [x] Basic editor (markdown formatting)
|
||||||
- [x] Basic text editing
|
- [x] Basic text editing
|
||||||
- [ ] Load & Save text file
|
- [x] Load & Save text file
|
||||||
- [x] Editor preview
|
- [x] Editor preview
|
||||||
- [x] Preview text in markdown
|
- [x] Preview text in markdown
|
||||||
|
|
||||||
@@ -34,13 +34,13 @@
|
|||||||
|
|
||||||
### v0.2.0 - links & context building
|
### v0.2.0 - links & context building
|
||||||
|
|
||||||
- [ ] Links between objects
|
- [x] Links between objects
|
||||||
- [ ] Links in templates
|
- [ ] Links in templates
|
||||||
|
|
||||||
### v0.2.1 - writing projects & organisation
|
### v0.2.1 - writing projects & organisation
|
||||||
|
|
||||||
- [ ] Project management
|
- [x] Project management
|
||||||
- [ ] Chapters/organisation
|
- [x] Chapters/organisation
|
||||||
|
|
||||||
## v0.3 - Workflows & AI integration
|
## v0.3 - Workflows & AI integration
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
|
|
||||||
- [ ] Content generation AI
|
- [ ] Content generation AI
|
||||||
- [ ] Collect context from across a story or project
|
- [ ] Collect context from across a story or project
|
||||||
- [ ] Generate content for stories
|
- [x] Generate content for stories (Done to a very basic level)
|
||||||
- [ ] Create original content in the form of objects using templates
|
- [ ] Create original content in the form of objects using templates
|
||||||
- [ ] Create a new template
|
- [ ] Create a new template
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
use egui::TextEdit;
|
use egui::TextEdit;
|
||||||
use egui_commonmark::{CommonMarkCache, CommonMarkViewer};
|
use egui_commonmark::{CommonMarkCache, CommonMarkViewer};
|
||||||
use serde::{self, Deserialize, Serialize};
|
use serde::{self, Deserialize, Serialize};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::{PROJECT_FOLDER, editors::tags::Tag, llm_integration::content_llm::ai_enabled, util};
|
use crate::{
|
||||||
|
PROJECT_FOLDER,
|
||||||
|
editors::{settings_editor::ProjectSettings, tags::Tag},
|
||||||
|
llm_integration::content_llm::{ContentAI, ReadyState, ReasoningEffort},
|
||||||
|
util,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct MainEditor {
|
pub struct MainEditor {
|
||||||
pub content: ContentSection,
|
pub content: ContentSection,
|
||||||
pub show_editor: bool,
|
pub show_editor: bool,
|
||||||
|
pub editor_separate_window: bool,
|
||||||
pub show_preview: bool,
|
pub show_preview: bool,
|
||||||
preview_cache: CommonMarkCache,
|
preview_cache: CommonMarkCache,
|
||||||
|
dialog: Option<ContentAI>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for MainEditor {
|
impl Clone for MainEditor {
|
||||||
@@ -17,8 +25,10 @@ impl Clone for MainEditor {
|
|||||||
content: self.content.clone(),
|
content: self.content.clone(),
|
||||||
|
|
||||||
show_editor: self.show_editor,
|
show_editor: self.show_editor,
|
||||||
|
editor_separate_window: self.editor_separate_window,
|
||||||
show_preview: self.show_preview,
|
show_preview: self.show_preview,
|
||||||
preview_cache: CommonMarkCache::default(),
|
preview_cache: CommonMarkCache::default(),
|
||||||
|
dialog: self.dialog.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,7 +106,9 @@ impl MainEditor {
|
|||||||
content: ContentSection::new(),
|
content: ContentSection::new(),
|
||||||
show_editor: false, // Start with editor hidden
|
show_editor: false, // Start with editor hidden
|
||||||
show_preview: false,
|
show_preview: false,
|
||||||
|
editor_separate_window: false,
|
||||||
preview_cache: CommonMarkCache::default(),
|
preview_cache: CommonMarkCache::default(),
|
||||||
|
dialog: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,20 +117,13 @@ impl MainEditor {
|
|||||||
content,
|
content,
|
||||||
show_editor: true,
|
show_editor: true,
|
||||||
show_preview: false,
|
show_preview: false,
|
||||||
|
editor_separate_window: false,
|
||||||
preview_cache: CommonMarkCache::default(),
|
preview_cache: CommonMarkCache::default(),
|
||||||
|
dialog: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, ctx: &egui::Context) {
|
pub fn render_ui(&mut self, project: &mut ProjectSettings, ui: &mut egui::Ui) {
|
||||||
// Show the editor window if enabled
|
|
||||||
let mut show = self.show_editor;
|
|
||||||
if show {
|
|
||||||
egui::Window::new("Markdown Editor")
|
|
||||||
.resizable(true)
|
|
||||||
.default_width(1000.0)
|
|
||||||
.default_height(800.0)
|
|
||||||
.open(&mut show)
|
|
||||||
.show(ctx, |ui| {
|
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
// check for Ctrl+S to save
|
// check for Ctrl+S to save
|
||||||
if ui.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) {
|
if ui.input(|i| i.key_pressed(egui::Key::S) && i.modifiers.ctrl) {
|
||||||
@@ -171,6 +176,9 @@ impl MainEditor {
|
|||||||
|
|
||||||
// preview toggle
|
// preview toggle
|
||||||
ui.checkbox(&mut self.show_preview, "Preview");
|
ui.checkbox(&mut self.show_preview, "Preview");
|
||||||
|
|
||||||
|
// editor toggle
|
||||||
|
ui.checkbox(&mut self.editor_separate_window, "Pop out editor");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -208,22 +216,52 @@ impl MainEditor {
|
|||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
ui.strong("Tags");
|
ui.strong("Tags");
|
||||||
Tag::selector_ui(
|
Tag::selector_ui(&mut self.content.tags, ui, Some(&mut self.content.saved));
|
||||||
&mut self.content.tags,
|
|
||||||
ui,
|
|
||||||
Some(&mut self.content.saved),
|
|
||||||
);
|
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
|
if let Some(dialog) = &mut self.dialog {
|
||||||
|
dialog.ui(ui, project);
|
||||||
|
|
||||||
|
dialog.content = self.content.content.clone();
|
||||||
|
if *dialog.ready.lock().unwrap() == ReadyState::Ready {
|
||||||
|
self.content
|
||||||
|
.content
|
||||||
|
.push_str(&dialog.result.lock().unwrap());
|
||||||
|
self.content.saved = false;
|
||||||
|
*dialog.ready.lock().unwrap() = ReadyState::Idle;
|
||||||
|
} else if *dialog.ready.lock().unwrap() == ReadyState::Halted {
|
||||||
|
*dialog.ready.lock().unwrap() = ReadyState::Idle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.show_preview {
|
if self.show_preview {
|
||||||
self.preview_ui(ui);
|
self.preview_ui(ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.editor_ui(ui);
|
self.editor_ui(ui, project);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ui(&mut self, ctx: &egui::Context, project: &mut ProjectSettings) {
|
||||||
|
// Show the editor window if enabled
|
||||||
|
let mut show = self.show_editor;
|
||||||
|
if show {
|
||||||
|
if self.editor_separate_window {
|
||||||
|
egui::Window::new("Editor")
|
||||||
|
.resizable(true)
|
||||||
|
.default_width(1000.0)
|
||||||
|
.default_height(800.0)
|
||||||
|
.open(&mut show)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
self.render_ui(project, ui);
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
self.render_ui(project, ui);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.show_editor = show;
|
self.show_editor = show;
|
||||||
@@ -263,7 +301,7 @@ impl MainEditor {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn editor_ui(&mut self, ui: &mut egui::Ui) {
|
fn editor_ui(&mut self, ui: &mut egui::Ui, project: &mut ProjectSettings) {
|
||||||
let _response = egui::ScrollArea::both()
|
let _response = egui::ScrollArea::both()
|
||||||
.auto_shrink([false, false])
|
.auto_shrink([false, false])
|
||||||
.id_salt("editor_scroll")
|
.id_salt("editor_scroll")
|
||||||
@@ -301,19 +339,20 @@ impl MainEditor {
|
|||||||
ctx_menu = true;
|
ctx_menu = true;
|
||||||
|
|
||||||
ui.menu_button("AI Actions", |ui| {
|
ui.menu_button("AI Actions", |ui| {
|
||||||
ui.add_enabled_ui(ai_enabled(), |ui| {
|
ui.add_enabled_ui(project.ai_enabled(), |ui| {
|
||||||
if ui.button("Summarise").clicked() {
|
if ui.button("AI Assistant").clicked() {
|
||||||
println!("Summarise");
|
self.dialog = Some(ContentAI {
|
||||||
}
|
content: self.content.content.clone(),
|
||||||
|
instruction: String::new(),
|
||||||
if ui.button("Continue").clicked() {
|
max_tokens: 1024,
|
||||||
let content = self.content.content.clone();
|
reasoning_effort: ReasoningEffort::default(),
|
||||||
let response =
|
context_override: "".to_string(),
|
||||||
crate::llm_integration::content_llm::continue_content(
|
result: Arc::new(Mutex::new(String::new())),
|
||||||
&content, "", 1024,
|
open: true,
|
||||||
)
|
ready: Arc::new(Mutex::new(ReadyState::Idle)),
|
||||||
.unwrap();
|
temperature: 0.7,
|
||||||
self.content.content.push_str(&response);
|
model_override: "".to_string(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
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 {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
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
-1
@@ -1,7 +1,7 @@
|
|||||||
pub mod asset_editor;
|
pub mod asset_editor;
|
||||||
pub mod content_editor;
|
pub mod content_editor;
|
||||||
pub mod context_editor;
|
|
||||||
pub mod note_editor;
|
pub mod note_editor;
|
||||||
pub mod object_editor;
|
pub mod object_editor;
|
||||||
|
pub mod settings_editor;
|
||||||
pub mod tags;
|
pub mod tags;
|
||||||
pub mod template_editor;
|
pub mod template_editor;
|
||||||
|
|||||||
@@ -0,0 +1,327 @@
|
|||||||
|
use chrono::NaiveDate;
|
||||||
|
use egui::TextEdit;
|
||||||
|
use egui_extras::DatePickerButton;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{io::Read, path::PathBuf, sync::LazyLock};
|
||||||
|
|
||||||
|
use crate::{PROJECT_FOLDER, util::saved_status};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct ProjectSettings {
|
||||||
|
date: NaiveDate,
|
||||||
|
project_name: String,
|
||||||
|
project_author: String,
|
||||||
|
project_description: String,
|
||||||
|
|
||||||
|
// AI settings
|
||||||
|
pub ai_context: String,
|
||||||
|
|
||||||
|
// settings
|
||||||
|
#[serde(skip)]
|
||||||
|
pub global_settings: EditorSettings,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub local_overrides: EditorSettings,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub open: bool,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub saved: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLOBAL_SETTINGS_PATH: LazyLock<String> =
|
||||||
|
LazyLock::new(|| match std::env::var("XDG_CONFIG_HOME") {
|
||||||
|
Ok(path) => path + "/worldcoder/settings.json",
|
||||||
|
Err(_) => {
|
||||||
|
eprintln!(
|
||||||
|
"XDG_CONFIG_HOME not set, using default path of ~/.config/worldcoder/settings.json"
|
||||||
|
);
|
||||||
|
"~/.config/worldcoder/settings.json".to_string()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
impl ProjectSettings {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load() -> Self {
|
||||||
|
let project_path = PROJECT_FOLDER.join("project.json");
|
||||||
|
|
||||||
|
let mut file = if let Ok(file) = std::fs::File::open(project_path) {
|
||||||
|
file
|
||||||
|
} else {
|
||||||
|
return Self::default();
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut contents = String::new();
|
||||||
|
file.read_to_string(&mut contents).unwrap();
|
||||||
|
if let Ok(mut proj) = serde_json::from_str::<Self>(&contents) {
|
||||||
|
proj.saved = true;
|
||||||
|
|
||||||
|
// load global settings
|
||||||
|
proj.global_settings = EditorSettings::load_global();
|
||||||
|
|
||||||
|
// load local overrides
|
||||||
|
proj.local_overrides = EditorSettings::load();
|
||||||
|
|
||||||
|
proj
|
||||||
|
} else {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(&mut self) {
|
||||||
|
let project_path = PROJECT_FOLDER.join("project.json");
|
||||||
|
let content = serde_json::to_string_pretty(self).unwrap();
|
||||||
|
std::fs::write(project_path, content).unwrap();
|
||||||
|
|
||||||
|
self.global_settings.save();
|
||||||
|
self.local_overrides.save();
|
||||||
|
|
||||||
|
self.saved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
|
// save state
|
||||||
|
saved_status(ui, self.saved, "N/A", "Project Settings");
|
||||||
|
if ui.button("Save").clicked() {
|
||||||
|
self.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
// project settings
|
||||||
|
ui.heading("Project Settings");
|
||||||
|
egui::Grid::new("project settings")
|
||||||
|
.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();
|
||||||
|
|
||||||
|
ui.label("AI Context Prompt");
|
||||||
|
ui.add(TextEdit::multiline(&mut self.ai_context)
|
||||||
|
.font(egui::TextStyle::Monospace)
|
||||||
|
.interactive(true)
|
||||||
|
.frame(false)
|
||||||
|
.lock_focus(true)
|
||||||
|
.hint_text("What is this project about? what should the LLM know when generating content for this project?"));
|
||||||
|
|
||||||
|
ui.end_row();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
// local settings overrides for editor
|
||||||
|
ui.heading("Local Overrides");
|
||||||
|
egui::Grid::new("local overrides")
|
||||||
|
.striped(true)
|
||||||
|
.num_columns(2)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label("Enable AI");
|
||||||
|
if let Some(ai_enabled) = &mut self.local_overrides.ai_enabled {
|
||||||
|
ui.checkbox(ai_enabled, "Enable AI");
|
||||||
|
if ui.button("Remove Override").clicked() {
|
||||||
|
self.local_overrides.ai_enabled = None;
|
||||||
|
}
|
||||||
|
} else if ui.button("Override").clicked() {
|
||||||
|
self.local_overrides.ai_enabled = Some(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
ui.label("LLM API URI");
|
||||||
|
if let Some(llm_api_uri) = &mut self.local_overrides.llm_api_uri {
|
||||||
|
ui.text_edit_singleline(llm_api_uri);
|
||||||
|
if ui.button("Remove Override").clicked() {
|
||||||
|
self.local_overrides.llm_api_uri = None;
|
||||||
|
}
|
||||||
|
} else if ui.button("Override").clicked() {
|
||||||
|
self.local_overrides.llm_api_uri = Some("http://localhost:1234".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
ui.label("LLM API Key");
|
||||||
|
if let Some(llm_api_key) = &mut self.local_overrides.llm_api_key {
|
||||||
|
ui.text_edit_singleline(llm_api_key);
|
||||||
|
if ui.button("Remove Override").clicked() {
|
||||||
|
self.local_overrides.llm_api_key = None;
|
||||||
|
}
|
||||||
|
} else if ui.button("Override").clicked() {
|
||||||
|
self.local_overrides.llm_api_key = Some("1234".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.end_row();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
// global editor settings
|
||||||
|
ui.heading("Global Editor Settings");
|
||||||
|
egui::Grid::new("global settings")
|
||||||
|
.striped(true)
|
||||||
|
.num_columns(2)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label("Enable AI");
|
||||||
|
ui.checkbox(&mut self.global_settings.ai_enabled.unwrap(), "Enable AI");
|
||||||
|
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
ui.label("LLM API URI");
|
||||||
|
ui.text_edit_singleline(self.global_settings.llm_api_uri.as_mut().unwrap());
|
||||||
|
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
ui.label("LLM API Key");
|
||||||
|
ui.text_edit_singleline(self.global_settings.llm_api_key.as_mut().unwrap());
|
||||||
|
|
||||||
|
ui.end_row();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ai_enabled(&mut self) -> bool {
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
|
||||||
|
if self.global_settings.ai_enabled.unwrap() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if client
|
||||||
|
.get(self.global_settings.llm_api_uri.clone().unwrap() + "/v1/models")
|
||||||
|
.send()
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
self.global_settings.ai_enabled = Some(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(&mut self) {
|
||||||
|
self.open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _close(&mut self) {
|
||||||
|
self.open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ProjectSettings {
|
||||||
|
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(),
|
||||||
|
|
||||||
|
ai_context: "".to_string(),
|
||||||
|
global_settings: EditorSettings::new(),
|
||||||
|
local_overrides: EditorSettings::new(),
|
||||||
|
|
||||||
|
// window state
|
||||||
|
open: false,
|
||||||
|
saved: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct EditorSettings {
|
||||||
|
pub llm_api_uri: Option<String>,
|
||||||
|
pub llm_api_key: Option<String>,
|
||||||
|
pub ai_enabled: Option<bool>,
|
||||||
|
pub dark_theme: Option<bool>,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
is_global: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EditorSettings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
llm_api_uri: Some("http://localhost:1234".to_string()),
|
||||||
|
llm_api_key: Some("".to_string()),
|
||||||
|
ai_enabled: Some(true),
|
||||||
|
dark_theme: Some(true),
|
||||||
|
|
||||||
|
// window state
|
||||||
|
is_global: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EditorSettings {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
llm_api_uri: None,
|
||||||
|
llm_api_key: None,
|
||||||
|
ai_enabled: None,
|
||||||
|
dark_theme: None,
|
||||||
|
|
||||||
|
is_global: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load() -> Self {
|
||||||
|
let path = PROJECT_FOLDER.join("settings.json");
|
||||||
|
let mut file = if let Ok(file) = std::fs::File::open(path) {
|
||||||
|
file
|
||||||
|
} else {
|
||||||
|
return Self::default();
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut contents = String::new();
|
||||||
|
file.read_to_string(&mut contents).unwrap();
|
||||||
|
serde_json::from_str(&contents).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(&self) {
|
||||||
|
let content = serde_json::to_string_pretty(self).unwrap();
|
||||||
|
|
||||||
|
let path = if self.is_global {
|
||||||
|
PathBuf::from(GLOBAL_SETTINGS_PATH.clone())
|
||||||
|
} else {
|
||||||
|
PROJECT_FOLDER.join("settings.json")
|
||||||
|
};
|
||||||
|
|
||||||
|
std::fs::write(path, content).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_global() -> Self {
|
||||||
|
let path = PathBuf::from(GLOBAL_SETTINGS_PATH.clone());
|
||||||
|
|
||||||
|
if !path.exists() {
|
||||||
|
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||||
|
let content = serde_json::to_string_pretty(&Self::default()).unwrap();
|
||||||
|
std::fs::write(&path, content).unwrap();
|
||||||
|
|
||||||
|
// return a default config
|
||||||
|
return Self::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = std::fs::read_to_string(path).unwrap();
|
||||||
|
serde_json::from_str(&content).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
use core::fmt;
|
|
||||||
|
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
|
use core::fmt;
|
||||||
use egui::ScrollArea;
|
use egui::ScrollArea;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|||||||
@@ -1,58 +1,383 @@
|
|||||||
|
use std::{
|
||||||
|
io::{BufRead, BufReader},
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::editors::settings_editor::ProjectSettings;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ContentAI {
|
||||||
|
pub open: bool,
|
||||||
|
pub content: String,
|
||||||
|
pub instruction: String,
|
||||||
|
pub max_tokens: usize,
|
||||||
|
pub context_override: String,
|
||||||
|
pub result: Arc<Mutex<String>>,
|
||||||
|
pub ready: Arc<Mutex<ReadyState>>,
|
||||||
|
pub temperature: f32,
|
||||||
|
pub reasoning_effort: ReasoningEffort,
|
||||||
|
pub model_override: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContentAI {
|
||||||
|
pub fn ui(&mut self, ui: &mut egui::Ui, project: &mut ProjectSettings) {
|
||||||
|
let is_open = self.open;
|
||||||
|
|
||||||
|
if is_open {
|
||||||
|
egui::SidePanel::right("ai_assistant").show_inside(ui, |ui| {
|
||||||
|
Self::ui_main(self, ui, project);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.open = is_open;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ui_output_box(&mut self, ui: &mut egui::Ui, project: &mut ProjectSettings) {
|
||||||
|
egui::TopBottomPanel::bottom("llm_output")
|
||||||
|
.resizable(true)
|
||||||
|
.show_inside(ui, |ui| {
|
||||||
|
let mut ready_lock = self.ready.lock().unwrap();
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if *ready_lock == ReadyState::Generating {
|
||||||
|
if ui.button("Cancel").clicked() {
|
||||||
|
*ready_lock = ReadyState::Halted;
|
||||||
|
}
|
||||||
|
if ui.button("Stop").clicked() {
|
||||||
|
*ready_lock = ReadyState::Idle;
|
||||||
|
}
|
||||||
|
ui.spinner();
|
||||||
|
ui.label("Generating...");
|
||||||
|
}
|
||||||
|
|
||||||
|
if *ready_lock == ReadyState::Idle {
|
||||||
|
let continue_content = || {
|
||||||
|
let context_override = self.context_override.clone();
|
||||||
|
let content = self.content.clone();
|
||||||
|
let instruction = self.instruction.clone();
|
||||||
|
let project = project.clone();
|
||||||
|
let ai_context = project.ai_context.clone();
|
||||||
|
let result = self.result.clone();
|
||||||
|
let ready = self.ready.clone();
|
||||||
|
|
||||||
|
let options = AIOptions {
|
||||||
|
max_completion_tokens: self.max_tokens,
|
||||||
|
reasoning_effort: self.reasoning_effort,
|
||||||
|
temperature: self.temperature,
|
||||||
|
model_override: if !self.model_override.is_empty() {
|
||||||
|
Some(self.model_override.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
result.lock().unwrap().clear();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let result = crate::llm_integration::content_llm::continue_content(
|
||||||
|
ai_context + "\n" + &context_override,
|
||||||
|
content,
|
||||||
|
instruction,
|
||||||
|
options,
|
||||||
|
project,
|
||||||
|
result,
|
||||||
|
ready.clone(),
|
||||||
|
);
|
||||||
|
if let Err(e) = result {
|
||||||
|
eprintln!("Error in content generation: {e}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if ui.button("Generate ").clicked() {
|
||||||
|
continue_content();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.label("Idle");
|
||||||
|
}
|
||||||
|
|
||||||
|
// show regardless of state
|
||||||
|
if ui.button("Insert").clicked() {
|
||||||
|
*self.ready.lock().unwrap() = ReadyState::Ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("Clear").clicked() {
|
||||||
|
self.result.lock().unwrap().clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
egui::ScrollArea::both()
|
||||||
|
.auto_shrink([false, false])
|
||||||
|
.id_salt("llm_output")
|
||||||
|
.max_width(ui.available_width())
|
||||||
|
// .max_height(ui.available_height() / 3.0)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.add(
|
||||||
|
egui::TextEdit::multiline(&mut *self.result.lock().unwrap())
|
||||||
|
.font(egui::TextStyle::Monospace)
|
||||||
|
.interactive(false)
|
||||||
|
.desired_rows(0)
|
||||||
|
.frame(false)
|
||||||
|
.desired_width(ui.available_width())
|
||||||
|
.lock_focus(true)
|
||||||
|
.hint_text("Content will appear here..."),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ui_main(&mut self, ui: &mut egui::Ui, project: &mut ProjectSettings) {
|
||||||
|
{
|
||||||
|
ui.weak("(The model will see current file content)");
|
||||||
|
|
||||||
|
egui::Grid::new("continue_grid")
|
||||||
|
.num_columns(2)
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label("Max Tokens");
|
||||||
|
ui.add(
|
||||||
|
egui::DragValue::new(&mut self.max_tokens)
|
||||||
|
.range(128..=u32::MAX)
|
||||||
|
.speed(128),
|
||||||
|
);
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
ui.label("Temperature");
|
||||||
|
ui.add(
|
||||||
|
egui::DragValue::new(&mut self.temperature)
|
||||||
|
.range(0.0..=2.0)
|
||||||
|
.speed(0.1),
|
||||||
|
);
|
||||||
|
|
||||||
|
ui.label("Reasoning effort");
|
||||||
|
|
||||||
|
egui::ComboBox::from_id_salt("reasoning_effort")
|
||||||
|
.selected_text(self.reasoning_effort.to_string())
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
ui.selectable_value(
|
||||||
|
&mut self.reasoning_effort,
|
||||||
|
ReasoningEffort::Minimal,
|
||||||
|
"Minimal",
|
||||||
|
);
|
||||||
|
ui.selectable_value(
|
||||||
|
&mut self.reasoning_effort,
|
||||||
|
ReasoningEffort::Low,
|
||||||
|
"Low",
|
||||||
|
);
|
||||||
|
ui.selectable_value(
|
||||||
|
&mut self.reasoning_effort,
|
||||||
|
ReasoningEffort::Medium,
|
||||||
|
"Medium",
|
||||||
|
);
|
||||||
|
ui.selectable_value(
|
||||||
|
&mut self.reasoning_effort,
|
||||||
|
ReasoningEffort::High,
|
||||||
|
"High",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
ui.label("Model override");
|
||||||
|
ui.add(egui::TextEdit::singleline(&mut self.model_override));
|
||||||
|
ui.end_row();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.ui_output_box(ui, project);
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
// Instructions
|
||||||
|
egui::ScrollArea::both()
|
||||||
|
.id_salt("continue_instruction")
|
||||||
|
.auto_shrink([true, false])
|
||||||
|
.max_height(ui.available_height() / 2.0)
|
||||||
|
.max_width(ui.available_width())
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.add(
|
||||||
|
egui::TextEdit::multiline(&mut self.instruction)
|
||||||
|
.frame(false)
|
||||||
|
.desired_width(ui.available_width())
|
||||||
|
.hint_text("Writing Instructions"),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
// Context
|
||||||
|
egui::ScrollArea::both()
|
||||||
|
.id_salt("continue_context")
|
||||||
|
.auto_shrink([true, false])
|
||||||
|
.max_height(ui.available_height())
|
||||||
|
.max_width(ui.available_width())
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.add(
|
||||||
|
egui::TextEdit::multiline(&mut self.context_override)
|
||||||
|
.frame(false)
|
||||||
|
.desired_width(ui.available_width())
|
||||||
|
.hint_text("Any additional context?"),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn continue_content(
|
pub fn continue_content(
|
||||||
context: &str,
|
context: String,
|
||||||
instruction: &str,
|
previous_content: String,
|
||||||
_max_tokens: usize,
|
instruction: String,
|
||||||
) -> Result<String, Box<dyn std::error::Error>> {
|
options: AIOptions,
|
||||||
|
project: ProjectSettings,
|
||||||
|
result: Arc<Mutex<String>>,
|
||||||
|
ready: Arc<Mutex<ReadyState>>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
*ready.lock().unwrap() = ReadyState::Generating;
|
||||||
|
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
|
|
||||||
let messages = vec![
|
let messages = vec![
|
||||||
Message {
|
Message {
|
||||||
role: "system".to_string(),
|
role: "system".to_string(),
|
||||||
content: "
|
content: "
|
||||||
Please generate content that is a direct continuation of the given text.
|
Please generate content that is a 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.
|
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.
|
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.
|
if extra instructions are provided, follow them exactly, otherwise continue the text in a logical way.
|
||||||
|
your output should NEVER be a repeat of any previous content
|
||||||
".to_string(),
|
".to_string(),
|
||||||
},
|
},
|
||||||
Message {
|
Message {
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: context.to_string(),
|
content: format!("Context / General instructions: {context}"),
|
||||||
},
|
},
|
||||||
Message {
|
Message {
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: format!("Instructions: {instruction}"),
|
content: format!("Content to continue: {previous_content}"),
|
||||||
},
|
},
|
||||||
|
Message {
|
||||||
|
role: "user".to_string(),
|
||||||
|
content: format!("Specific instructions: {instruction}"),
|
||||||
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let request = ChatRequest {
|
let request = ChatRequest {
|
||||||
messages,
|
messages,
|
||||||
temperature: 0.7,
|
temperature: options.temperature,
|
||||||
|
max_tokens: options.max_completion_tokens,
|
||||||
|
model: options.model_override,
|
||||||
|
reasoning_effort: options.reasoning_effort,
|
||||||
|
stream: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = client
|
let llm_api_uri = if let Some(uri) = project.local_overrides.llm_api_uri {
|
||||||
.post("http://localhost:1234/v1/chat/completions")
|
uri
|
||||||
.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 {
|
} else {
|
||||||
Err("No response from model".into())
|
project.global_settings.llm_api_uri.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let api_key = if let Some(key) = project.local_overrides.llm_api_key {
|
||||||
|
if key.is_empty() { None } else { Some(key) }
|
||||||
|
} else if let Some(key) = project.global_settings.llm_api_key {
|
||||||
|
if key.is_empty() { None } else { Some(key) }
|
||||||
|
} else {
|
||||||
|
return Err("No API key found".into());
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = if let Some(k) = api_key {
|
||||||
|
client
|
||||||
|
.post(llm_api_uri + "/v1/chat/completions")
|
||||||
|
.json(&request)
|
||||||
|
.bearer_auth(k)
|
||||||
|
.send()?
|
||||||
|
} else {
|
||||||
|
client
|
||||||
|
.post(llm_api_uri + "/v1/chat/completions")
|
||||||
|
.json(&request)
|
||||||
|
.send()?
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("success!");
|
||||||
|
|
||||||
|
let reader = BufReader::new(response);
|
||||||
|
for line in reader.lines() {
|
||||||
|
// initial loop to check if the user has terminated the generation
|
||||||
|
{
|
||||||
|
let mut ready = ready.lock().unwrap();
|
||||||
|
|
||||||
|
if *ready == ReadyState::Halted {
|
||||||
|
result.lock().unwrap().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if *ready != ReadyState::Generating {
|
||||||
|
*ready = ReadyState::Idle;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ai_enabled() -> bool {
|
let line = line?;
|
||||||
let client = reqwest::blocking::Client::new();
|
if line == "data: [DONE]" {
|
||||||
client.get("http://localhost:1234/v1/models").send().is_ok()
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(json) = line.strip_prefix("data: ") {
|
||||||
|
if let Ok(chunk) = serde_json::from_str::<StreamingChatResponse>(json) {
|
||||||
|
if let Some(content) = chunk.choices[0].delta.content.as_ref() {
|
||||||
|
result.lock().unwrap().push_str(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*ready.lock().unwrap() = ReadyState::Idle;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AIOptions {
|
||||||
|
pub max_completion_tokens: usize,
|
||||||
|
pub temperature: f32,
|
||||||
|
pub reasoning_effort: ReasoningEffort,
|
||||||
|
pub model_override: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
pub enum ReadyState {
|
||||||
|
Idle,
|
||||||
|
Generating,
|
||||||
|
Ready,
|
||||||
|
Halted,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Copy, Clone, PartialEq)]
|
||||||
|
pub enum ReasoningEffort {
|
||||||
|
#[serde(rename = "minimal")]
|
||||||
|
Minimal,
|
||||||
|
#[serde(rename = "low")]
|
||||||
|
Low,
|
||||||
|
#[serde(rename = "medium")]
|
||||||
|
Medium,
|
||||||
|
#[serde(rename = "high")]
|
||||||
|
High,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ReasoningEffort {
|
||||||
|
fn default() -> Self {
|
||||||
|
ReasoningEffort::Low
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for ReasoningEffort {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
ReasoningEffort::Minimal => "Minimal".to_string(),
|
||||||
|
ReasoningEffort::Low => "Low".to_string(),
|
||||||
|
ReasoningEffort::Medium => "Medium".to_string(),
|
||||||
|
ReasoningEffort::High => "High".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple request structure
|
// Simple request structure
|
||||||
@@ -60,6 +385,36 @@ pub fn ai_enabled() -> bool {
|
|||||||
struct ChatRequest {
|
struct ChatRequest {
|
||||||
messages: Vec<Message>,
|
messages: Vec<Message>,
|
||||||
temperature: f32,
|
temperature: f32,
|
||||||
|
max_tokens: usize,
|
||||||
|
stream: bool,
|
||||||
|
reasoning_effort: ReasoningEffort,
|
||||||
|
|
||||||
|
// if we give the API model:null it returns 500
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
model: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streaming response structures
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct StreamingChatResponse {
|
||||||
|
choices: Vec<StreamingChoice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct StreamingChoice {
|
||||||
|
delta: Delta,
|
||||||
|
#[serde(default)]
|
||||||
|
#[allow(unused)]
|
||||||
|
finish_reason: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct Delta {
|
||||||
|
#[serde(default)]
|
||||||
|
#[allow(unused)]
|
||||||
|
role: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
content: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@@ -70,10 +425,12 @@ struct Message {
|
|||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct ChatResponse {
|
struct ChatResponse {
|
||||||
|
#[allow(unused)]
|
||||||
choices: Vec<Choice>,
|
choices: Vec<Choice>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct Choice {
|
struct Choice {
|
||||||
|
#[allow(unused)]
|
||||||
message: Message,
|
message: Message,
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-18
@@ -1,3 +1,5 @@
|
|||||||
|
#![windows_subsystem = "windows"]
|
||||||
|
|
||||||
use std::{path::PathBuf, sync::LazyLock};
|
use std::{path::PathBuf, sync::LazyLock};
|
||||||
|
|
||||||
use egui::ScrollArea;
|
use egui::ScrollArea;
|
||||||
@@ -5,14 +7,13 @@ use egui::ScrollArea;
|
|||||||
mod editors;
|
mod editors;
|
||||||
mod explorer;
|
mod explorer;
|
||||||
mod llm_integration;
|
mod llm_integration;
|
||||||
mod scene;
|
|
||||||
|
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
editors::{
|
editors::{
|
||||||
asset_editor::Asset, content_editor, context_editor::ProjectContext, note_editor,
|
asset_editor::Asset, content_editor, note_editor, object_editor::ObjectInstance,
|
||||||
object_editor::ObjectInstance, tags::Tag, template_editor::Template,
|
settings_editor::ProjectSettings, tags::Tag, template_editor::Template,
|
||||||
},
|
},
|
||||||
explorer::Explorer,
|
explorer::Explorer,
|
||||||
};
|
};
|
||||||
@@ -31,16 +32,18 @@ fn main() {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = eframe::run_native("Code Editor", options, Box::new(|_cc| Ok(Box::new(app))));
|
if let Err(e) = eframe::run_native("World Coder", options, Box::new(|_cc| Ok(Box::new(app)))) {
|
||||||
|
eprintln!("Failed to run app: {e}");
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!("App closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Interface {
|
pub struct Interface {
|
||||||
// dialog: Option<egui_file::FileDialog>,
|
|
||||||
right_panel_content: RightPanelContent,
|
right_panel_content: RightPanelContent,
|
||||||
editor: content_editor::MainEditor,
|
editor: content_editor::MainEditor,
|
||||||
scene: scene::EditorScene,
|
|
||||||
explorer: Explorer,
|
explorer: Explorer,
|
||||||
project: ProjectContext,
|
project: ProjectSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl eframe::App for Interface {
|
impl eframe::App for Interface {
|
||||||
@@ -84,13 +87,12 @@ impl Interface {
|
|||||||
Self {
|
Self {
|
||||||
right_panel_content: RightPanelContent::None,
|
right_panel_content: RightPanelContent::None,
|
||||||
editor: content_editor::MainEditor::new(),
|
editor: content_editor::MainEditor::new(),
|
||||||
scene: scene::EditorScene::new(),
|
|
||||||
explorer: Explorer::new(),
|
explorer: Explorer::new(),
|
||||||
project: ProjectContext::load(),
|
project: ProjectSettings::load(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_top_panel(&self, ctx: &egui::Context) {
|
fn render_top_panel(&mut self, ctx: &egui::Context) {
|
||||||
// Top bar with actions
|
// Top bar with actions
|
||||||
egui::TopBottomPanel::top("top").show(ctx, |ui| {
|
egui::TopBottomPanel::top("top").show(ctx, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
@@ -99,9 +101,24 @@ impl Interface {
|
|||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
// version
|
// version
|
||||||
ui.label(VERSION)
|
ui.label(VERSION);
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
if ui.button("Settings").clicked() {
|
||||||
|
self.project.open();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if self.project.open {
|
||||||
|
let mut open = self.project.open;
|
||||||
|
egui::Window::new("Settings")
|
||||||
|
.open(&mut open)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
self.project.ui(ui);
|
||||||
|
});
|
||||||
|
self.project.open = open;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_left_panel(&mut self, ctx: &egui::Context) {
|
fn render_left_panel(&mut self, ctx: &egui::Context) {
|
||||||
@@ -110,8 +127,7 @@ impl Interface {
|
|||||||
.resizable(true)
|
.resizable(true)
|
||||||
.default_width(250.0)
|
.default_width(250.0)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
ui.heading("Project Files");
|
ui.heading("Explorer");
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
let mut to_load: Option<RightPanelContent> = None;
|
let mut to_load: Option<RightPanelContent> = None;
|
||||||
let mut load_doc: Option<content_editor::MainEditor> = None;
|
let mut load_doc: Option<content_editor::MainEditor> = None;
|
||||||
@@ -126,8 +142,6 @@ impl Interface {
|
|||||||
|
|
||||||
if let Some(load_doc) = load_doc {
|
if let Some(load_doc) = load_doc {
|
||||||
self.editor = load_doc;
|
self.editor = load_doc;
|
||||||
self.editor.show_editor = true;
|
|
||||||
self.editor.show_preview = true;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -188,8 +202,7 @@ impl Interface {
|
|||||||
|
|
||||||
// render main content area
|
// render main content area
|
||||||
fn render_main_content(&mut self, ctx: &egui::Context) {
|
fn render_main_content(&mut self, ctx: &egui::Context) {
|
||||||
self.editor.ui(ctx);
|
self.editor.ui(ctx, &mut self.project);
|
||||||
self.scene.ui(ctx, &mut self.explorer.objects());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure appearance of UI elements
|
// configure appearance of UI elements
|
||||||
@@ -214,7 +227,7 @@ impl Interface {
|
|||||||
fonts.font_data.insert(
|
fonts.font_data.insert(
|
||||||
"JetBrains Mono Nerd Font".to_string(),
|
"JetBrains Mono Nerd Font".to_string(),
|
||||||
std::sync::Arc::new(egui::FontData::from_static(include_bytes!(
|
std::sync::Arc::new(egui::FontData::from_static(include_bytes!(
|
||||||
"/usr/local/share/fonts/j/JetBrainsMonoNerdFont_Regular.ttf",
|
"../font/JetBrainsMonoNerdFontMono_Regular.ttf",
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
-147
@@ -1,147 +0,0 @@
|
|||||||
use egui::{RichText, vec2};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
PROJECT_FOLDER,
|
|
||||||
editors::{
|
|
||||||
object_editor::ObjectInstance,
|
|
||||||
template_editor::{FieldType, FieldValue, Template},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct EditorScene {
|
|
||||||
rect: egui::Rect,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EditorScene {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
rect: egui::Rect::ZERO,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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| {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"llm_api_uri": "http://localhost:1234",
|
||||||
|
"llm_api_key": "",
|
||||||
|
"ai_enabled": true,
|
||||||
|
"dark_theme": true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user