diff --git a/Cargo.lock b/Cargo.lock index daeec69..7c8d42d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2066,6 +2066,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.13.2", "block2", + "libc", "objc2", "objc2-core-foundation", ] @@ -2544,6 +2545,30 @@ dependencies = [ "web-sys", ] +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "block2", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + [[package]] name = "rsqlite-vfs" version = "0.1.1" @@ -2995,6 +3020,8 @@ dependencies = [ "strata_db", "tauri", "tauri-build", + "tauri-plugin-dialog", + "tauri-plugin-fs", "tempfile", ] @@ -3274,6 +3301,64 @@ dependencies = [ "tauri-utils", ] +[[package]] +name = "tauri-plugin" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1140cf34a3b3b836a13103dcab17f18831d5cc3534cbd435dc01a5c6daa65aa2" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri-utils", + "walkdir", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca325d6e66e9170f2e1b9631722e4e544bcbc51781575d266abdaba697bd51" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.21", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213fae2d85d4b76b06de5915d1862db9fa74541171f9d2c15da6ac9bd6e788c6" +dependencies = [ + "anyhow", + "dunce", + "glob", + "log", + "objc2-foundation", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.21", + "toml 1.1.8+spec-1.1.0", + "url", +] + [[package]] name = "tauri-runtime" version = "2.12.1" @@ -4169,7 +4254,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -4187,13 +4281,30 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -4220,42 +4331,90 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "0.5.40" diff --git a/app/package.json b/app/package.json index 82d22c0..714ed47 100644 --- a/app/package.json +++ b/app/package.json @@ -16,10 +16,13 @@ "@fontsource/ibm-plex-sans": "^5.3.0", "@strata/core": "*", "@tauri-apps/api": "^2.12.0", + "@tauri-apps/plugin-dialog": "^2.8.2", + "@tauri-apps/plugin-fs": "^2.7.0", "class-variance-authority": "^0.7.1", "cn": "^0.4.0", "leaflet": "^1.9.4", "lucide-react": "^1.48.0", + "pdf-lib": "^1.17.1", "react": "^19.1.0", "react-dom": "^19.1.0", "react-leaflet": "^5.0.0", diff --git a/app/src-tauri/Cargo.toml b/app/src-tauri/Cargo.toml index 6664a0e..26eb921 100644 --- a/app/src-tauri/Cargo.toml +++ b/app/src-tauri/Cargo.toml @@ -17,6 +17,8 @@ tauri-build = { version = "2", features = [] } [dependencies] tauri = { version = "2", features = [] } +tauri-plugin-dialog = "2" +tauri-plugin-fs = "2" serde = { version = "1", features = ["derive"] } strata_db = { path = "../../strata/core/db" } # Must stay the same version as strata_db's, so both use one SQLite and one Connection type. diff --git a/app/src-tauri/capabilities/default.json b/app/src-tauri/capabilities/default.json index c2ca36e..fd3e894 100644 --- a/app/src-tauri/capabilities/default.json +++ b/app/src-tauri/capabilities/default.json @@ -3,5 +3,5 @@ "identifier": "default", "description": "Capability for the main window", "windows": ["main"], - "permissions": ["core:default"] + "permissions": ["core:default", "dialog:allow-save", "fs:allow-write-file"] } diff --git a/app/src-tauri/src/lib.rs b/app/src-tauri/src/lib.rs index 5f2fa7e..8426660 100644 --- a/app/src-tauri/src/lib.rs +++ b/app/src-tauri/src/lib.rs @@ -11,6 +11,9 @@ fn load_data(app: tauri::AppHandle) -> Result { pub fn run() { tauri::Builder::default() + // Saving a section as a picture or PDF: the Save dialog, and writing the file chosen there. + .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_fs::init()) .setup(|app| Ok(open_main_window(app)?)) .invoke_handler(tauri::generate_handler![load_data]) .run(tauri::generate_context!()) diff --git a/app/src/App.tsx b/app/src/App.tsx index 0d2b922..ae98ef4 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -6,13 +6,17 @@ import { useModel } from "@/lib/useModel"; import { text } from "@/text"; import { MapView } from "@/views/MapView"; import { QualityView } from "@/views/QualityView"; +import { SectionView } from "@/views/SectionView"; type State = { records: Records; as: string } | { problem: LoadError } | null; -type View = "map" | "quality"; +type View = "map" | "section" | "quality"; export default function App() { const [state, setState] = useState(null); const [view, setView] = useState("map"); + // The section's map is first drawn when it is first shown: a map set up while hidden has no size to fit the borewells to. + const [sectionSeen, setSectionSeen] = useState(false); + if (view === "section" && !sectionSeen) setSectionSeen(true); // Reading again keeps what is on screen until the new records arrive, so the map does not blink. // Records that turn out unchanged are kept as they are, so nothing is worked out again for them. @@ -42,7 +46,7 @@ export default function App() {

{text.appName}

{records && (