diff --git a/package-lock.json b/package-lock.json index 3e2892f..e5881b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,22 @@ { - "name": "cad", + "name": "dossier", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "cad", + "name": "dossier", "version": "0.0.0", + "license": "AGPL-3.0-or-later", "dependencies": { "@mlightcad/libredwg-web": "^0.7.7", "@tauri-apps/api": "^2.11.1", "@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-fs": "^2.5.1", + "@tauri-apps/plugin-http": "^2.5.9", + "@tauri-apps/plugin-process": "^2.3.1", + "@tauri-apps/plugin-shell": "^2.3.5", + "@tauri-apps/plugin-updater": "^2.10.1", "delaunator": "^5.1.0", "dxf-parser": "^1.1.2", "jspdf": "^4.2.1", @@ -1862,6 +1867,42 @@ "@tauri-apps/api": "^2.11.0" } }, + "node_modules/@tauri-apps/plugin-http": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-http/-/plugin-http-2.5.9.tgz", + "integrity": "sha512-lCiY0+vs4HvIUSvZrBs8TC3TiCB0MOPRmiUjTq4prW7SlcJE2jdLeT6KBsJrT9Tlplufl7W1pY6SFAO3gCWxDA==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.11.0" + } + }, + "node_modules/@tauri-apps/plugin-process": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-process/-/plugin-process-2.3.1.tgz", + "integrity": "sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.8.0" + } + }, + "node_modules/@tauri-apps/plugin-shell": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.3.5.tgz", + "integrity": "sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.10.1" + } + }, + "node_modules/@tauri-apps/plugin-updater": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.10.1.tgz", + "integrity": "sha512-NFYMg+tWOZPJdzE/PpFj2qfqwAWwNS3kXrb1tm1gnBJ9mYzZ4WDRrwy8udzWoAnfGCHLuePNLY1WVCNHnh3eRA==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.10.1" + } + }, "node_modules/@tweenjs/tween.js": { "version": "23.1.3", "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", diff --git a/package.json b/package.json index 9ef7e72..581e50d 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,10 @@ "@tauri-apps/api": "^2.11.1", "@tauri-apps/plugin-dialog": "^2.7.1", "@tauri-apps/plugin-fs": "^2.5.1", + "@tauri-apps/plugin-http": "^2.5.9", + "@tauri-apps/plugin-process": "^2.3.1", + "@tauri-apps/plugin-shell": "^2.3.5", + "@tauri-apps/plugin-updater": "^2.10.1", "delaunator": "^5.1.0", "dxf-parser": "^1.1.2", "jspdf": "^4.2.1", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 59874da..4fcb92f 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -157,6 +157,15 @@ version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -395,6 +404,10 @@ dependencies = [ "tauri-build", "tauri-plugin-dialog", "tauri-plugin-fs", + "tauri-plugin-http", + "tauri-plugin-process", + "tauri-plugin-shell", + "tauri-plugin-updater", "wgpu", "winit", ] @@ -543,6 +556,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core", +] + [[package]] name = "chrono" version = "0.4.45" @@ -607,10 +631,29 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ + "percent-encoding", "time", "version_check", ] +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -727,6 +770,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -846,6 +898,12 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + [[package]] name = "dbus" version = "0.9.11" @@ -898,6 +956,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -1112,6 +1181,15 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "env_filter" version = "2.0.0" @@ -1205,6 +1283,16 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -1524,8 +1612,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1547,8 +1637,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", + "rand_core", + "wasm-bindgen", ] [[package]] @@ -1778,6 +1871,25 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.7.1" @@ -1924,6 +2036,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2", "http", "http-body", "httparse", @@ -1934,6 +2047,22 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -1952,9 +2081,11 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -2151,6 +2282,25 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -2484,6 +2634,12 @@ version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "markup5ever" version = "0.38.0" @@ -2525,6 +2681,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minisign-verify" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -2957,6 +3119,18 @@ dependencies = [ "objc2-foundation 0.3.2", ] +[[package]] +name = "objc2-osa-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0" +dependencies = [ + "bitflags 2.13.0", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-quartz-core" version = "0.2.2" @@ -3095,6 +3269,23 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "open" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b3d059e795d52b8a72fef45658620edd4d9c359b338564aa14391ffa511ed5" +dependencies = [ + "dunce", + "is-wsl", + "libc", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "option-ext" version = "0.2.0" @@ -3120,6 +3311,30 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_pipe" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "osakit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" +dependencies = [ + "objc2 0.6.4", + "objc2-foundation 0.3.2", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 2.0.18", +] + [[package]] name = "owned_ttf_parser" version = "0.25.1" @@ -3465,6 +3680,22 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + [[package]] name = "pxfm" version = "0.1.29" @@ -3480,6 +3711,62 @@ dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.3", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" +dependencies = [ + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand", + "rand_pcg", + "ring", + "rustc-hash 2.1.3", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + [[package]] name = "quote" version = "1.0.46" @@ -3501,6 +3788,32 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core", +] + [[package]] name = "range-alloc" version = "0.1.5" @@ -3685,6 +3998,49 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "cookie", + "cookie_store", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + [[package]] name = "reqwest" version = "0.13.4" @@ -3699,15 +4055,20 @@ dependencies = [ "http-body", "http-body-util", "hyper", + "hyper-rustls", "hyper-util", "js-sys", "log", "percent-encoding", "pin-project-lite", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", "serde", "serde_json", "sync_wrapper", "tokio", + "tokio-rustls", "tokio-util", "tower", "tower-http", @@ -3743,6 +4104,20 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "roxmltree" version = "0.20.0" @@ -3796,12 +4171,92 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.23.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni 0.22.4", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "same-file" version = "1.0.6" @@ -3811,6 +4266,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "schemars" version = "0.8.22" @@ -3887,6 +4351,29 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "selectors" version = "0.36.1" @@ -4017,6 +4504,18 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_with" version = "3.21.0" @@ -4087,16 +4586,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] +[[package]] +name = "shared_child" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7" +dependencies = [ + "libc", + "sigchld", + "windows-sys 0.60.2", +] + [[package]] name = "shlex" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +[[package]] +name = "sigchld" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1" +dependencies = [ + "libc", + "os_pipe", + "signal-hook", +] + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + [[package]] name = "simd-adler32" version = "0.3.9" @@ -4321,6 +4862,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "swash" version = "0.2.9" @@ -4364,6 +4911,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -4393,6 +4951,27 @@ dependencies = [ "libc", ] +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -4457,6 +5036,17 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" version = "0.12.16" @@ -4493,7 +5083,7 @@ dependencies = [ "percent-encoding", "plist", "raw-window-handle", - "reqwest", + "reqwest 0.13.4", "serde", "serde_json", "serde_repr", @@ -4634,6 +5224,94 @@ dependencies = [ "url", ] +[[package]] +name = "tauri-plugin-http" +version = "2.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5bd512048e1985b7ec78f96d99083e2ddaf7e0d906b2b63c44ce5bb8b894067" +dependencies = [ + "bytes", + "cookie_store", + "data-url", + "http", + "regex", + "reqwest 0.12.28", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "tokio", + "url", + "urlpattern", +] + +[[package]] +name = "tauri-plugin-process" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a" +dependencies = [ + "tauri", + "tauri-plugin", +] + +[[package]] +name = "tauri-plugin-shell" +version = "2.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8457dbf9e2bab1edd8df22bb2c20857a59a9868e79cb3eac5ed639eec4d0c73b" +dependencies = [ + "encoding_rs", + "log", + "open", + "os_pipe", + "regex", + "schemars 0.8.22", + "serde", + "serde_json", + "shared_child", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "tauri-plugin-updater" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806d9dac662c2e4594ff03c647a552f2c9bd544e7d0f683ec58f872f952ce4af" +dependencies = [ + "base64 0.22.1", + "dirs", + "flate2", + "futures-util", + "http", + "infer", + "log", + "minisign-verify", + "osakit", + "percent-encoding", + "reqwest 0.13.4", + "rustls", + "semver", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror 2.0.18", + "time", + "tokio", + "url", + "windows-sys 0.60.2", + "zip", +] + [[package]] name = "tauri-runtime" version = "2.11.3" @@ -4734,6 +5412,19 @@ dependencies = [ "toml 1.1.2+spec-1.1.0", ] +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + [[package]] name = "tendril" version = "0.5.0" @@ -4884,9 +5575,31 @@ dependencies = [ "mio", "pin-project-lite", "socket2", + "tokio-macros", "windows-sys 0.61.2", ] +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -5210,6 +5923,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.8" @@ -5584,6 +6303,24 @@ dependencies = [ "system-deps", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webview2-com" version = "0.38.2" @@ -5979,6 +6716,17 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -6457,6 +7205,16 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix 1.1.4", +] + [[package]] name = "xcursor" version = "0.3.10" @@ -6564,6 +7322,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + [[package]] name = "zerotrie" version = "0.2.4" @@ -6597,6 +7361,18 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "zip" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" +dependencies = [ + "arbitrary", + "crc32fast", + "indexmap 2.14.0", + "memchr", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index fc9d8c3..749fe60 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -39,6 +39,10 @@ tauri-build = { version = "2", features = [] } tauri = { version = "2", features = [] } tauri-plugin-dialog = "2" tauri-plugin-fs = "2" +tauri-plugin-updater = "2" +tauri-plugin-process = "2" +tauri-plugin-http = "2" +tauri-plugin-shell = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" geometry = { path = "geometry" } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index b403288..ee359b0 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -1,7 +1,7 @@ { "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", - "description": "Basis-Permissions + Fenstersteuerung fuer die randlose (decorations:false) Titelleiste: Minimieren/Maximieren/Schliessen + Ziehen der eigenen Titelleiste (data-tauri-drag-region). Menu: fuer die native macOS-Systemmenueleiste (src/native/appMenu.ts). Webview/Event: fuer die nativen Ressourcen-/Einstellungs-/Zeichnungsebenen-/Ebeneneinstellungs-Fenster + ihre Sync-Bruecken (src/native/*Window.ts) - gilt fuer alle Fensterlabels, da jedes Events senden/empfangen muss.", + "description": "Basis-Permissions + Fenstersteuerung fuer die randlose (decorations:false) Titelleiste: Minimieren/Maximieren/Schliessen + Ziehen der eigenen Titelleiste (data-tauri-drag-region). Menu: fuer die native macOS-Systemmenueleiste (src/native/appMenu.ts). Webview/Event: fuer die nativen Ressourcen-/Einstellungs-/Zeichnungsebenen-/Ebeneneinstellungs-Fenster + ihre Sync-Bruecken (src/native/*Window.ts) - gilt fuer alle Fensterlabels, da jedes Events senden/empfangen muss. Dialog/Fs: nativer Oeffnen- UND Speichern-Dialog fuer Projektdateien (.obp/.json) plus Lesen/Schreiben der gewaehlten Datei (io/projectFile.ts, io/saveFile.ts) - open+read fuer Projekt laden, save+write fuer Projekt/Export speichern. Updater/Process: Selbst-Update-Check beim Start (src/update/checkForUpdate.ts) + Neustart nach Installation. Http: Versionsverlauf (versions.json vom Gitea-Release) ohne Browser-CORS, Scope auf den eigenen Gitea-Host begrenzt. Shell:allow-open: oeffnet den DMG-Download einer aelteren Version im System-Browser (Rollback).", "windows": ["main", "resources", "settings", "drawing-levels", "layer-settings", "context-import"], "permissions": [ "core:default", @@ -16,7 +16,16 @@ "core:webview:allow-create-webview-window", "core:menu:default", "core:event:default", + "dialog:allow-open", "dialog:allow-save", - "fs:allow-write-text-file" + "fs:allow-read-text-file", + "fs:allow-write-text-file", + "updater:default", + "process:allow-restart", + { + "identifier": "http:default", + "allow": [{ "url": "https://git.openbureau.ch/*" }] + }, + "shell:allow-open" ] } diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000..460af3f Binary files /dev/null and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000..399326d Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png new file mode 100644 index 0000000..6b6020d Binary files /dev/null and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/64x64.png b/src-tauri/icons/64x64.png new file mode 100644 index 0000000..0bcc96a Binary files /dev/null and b/src-tauri/icons/64x64.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000..8a641dc Binary files /dev/null and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000..1f4f88c Binary files /dev/null and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000..ad5c701 Binary files /dev/null and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000..154221b Binary files /dev/null and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000..fd10029 Binary files /dev/null and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000..7ffa8e6 Binary files /dev/null and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000..925bd05 Binary files /dev/null and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000..194ca00 Binary files /dev/null and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000..341696b Binary files /dev/null and b/src-tauri/icons/Square89x89Logo.png differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000..dc12aad Binary files /dev/null and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns new file mode 100644 index 0000000..1acbf66 Binary files /dev/null and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico new file mode 100644 index 0000000..7ea8e8f Binary files /dev/null and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 6599b5f..ecfe5be 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -72,6 +72,18 @@ pub fn run() { // im Tauri-Fenster einen echten Save-Dialog zeigen statt still zu laden. .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_fs::init()) + // Selbst-Update: prueft beim Start gegen `endpoints` aus tauri.conf.json + // (Gitea-Release-Asset `latest.json`), Installation via JS-Seite + // (@tauri-apps/plugin-updater). `tauri_plugin_process` liefert `relaunch()` + // fuer den Neustart nach der Installation. + .plugin(tauri_plugin_updater::Builder::new().build()) + .plugin(tauri_plugin_process::init()) + // HTTP-Requests aus der Webview (Versionsverlauf, versions.json vom + // selben Gitea-Release) ohne Browser-CORS-Einschraenkung; Shell: oeffnet + // den DMG-Download einer aelteren Version im System-Browser (Rollback, + // s. src/update/checkForUpdate.ts). + .plugin(tauri_plugin_http::init()) + .plugin(tauri_plugin_shell::init()) // Haelt die pro Instanz offen gelockten Projektdatei-Handles (lock.rs). .manage(lock::LockState::default()) .setup(|_app| { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index d1308db..daeed46 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "$schema": "https://schema.tauri.app/config/2", - "productName": "Dossier", + "productName": "dossier", "version": "0.1.0", "identifier": "ch.dossier.cad", "build": { @@ -11,7 +11,7 @@ "windows": [ { "label": "main", - "title": "Dossier", + "title": "dossier", "width": 1400, "height": 900, "dragDropEnabled": false, @@ -27,6 +27,21 @@ "bundle": { "active": true, "targets": "all", - "icon": ["icons/icon.png"] + "createUpdaterArtifacts": true, + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + }, + "plugins": { + "updater": { + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEYyRDk3NkZGODQwRUJBNTQKUldSVXVnNkUvM2JaOHV2QWp0L05qZnpEUmt2TkN3ZWJBeFQwb1I0cExWNEpuZWU0UjZiazdQNU0K", + "endpoints": [ + "https://git.openbureau.ch/karim/DOSSIER-STANDALONE/releases/download/latest/latest.json" + ] + } } } diff --git a/src/App.tsx b/src/App.tsx index d343ec8..83df1f6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -74,6 +74,7 @@ import { isTauriRuntime, } from "./io/projectFile"; import type { LockInfo } from "./io/projectFile"; +import { checkForUpdate } from "./update/checkForUpdate"; import type { PlanViewHandle, PlanSelection, @@ -541,6 +542,13 @@ export default function App() { return () => setDxfImportTrigger(null); }, []); + // Selbst-Update: einmalig beim Start prüfen (still — meldet sich nur bei + // einem gefundenen Update oder einem echten Fehler, s. checkForUpdate.ts). + // No-op ausserhalb von Tauri (Vite-Dev-Server im Browser). + useEffect(() => { + checkForUpdate(true); + }, []); + // Panel-Layout (Docks/Tabs/Größen) — aus dem Store-Slice (Init lädt aus // localStorage + reconcile). Bei jeder Änderung zurückspeichern. const layout = useStore((s) => s.layout); diff --git a/src/i18n/de.ts b/src/i18n/de.ts index aad961e..a7912bc 100644 --- a/src/i18n/de.ts +++ b/src/i18n/de.ts @@ -140,7 +140,7 @@ export const de = { "menu.native.view": "Ansicht", "menu.native.window": "Fenster", "menu.native.panels": "Panels", - "menu.native.about": "Über Dossier", + "menu.native.about": "Über dossier", "menu.native.quit": "Beenden", // ── Layout-Menü (Topbar) ───────────────────────────────────────────────── @@ -1318,12 +1318,22 @@ export const de = { "exportSave.fmt.stl": "STL (3D-Mesh)", "about.title": "Über dossier", "about.version": "Version", - "about.desc": "Open-Source-CAAD (Computer Aided Architecture Design) — schöne, normgerechte Pläne aus einem semantischen Gebäudemodell. Als Desktop-App (vollständige Fassung) und im Browser zugänglich.", + "about.desc": "Open Source Computer Aided Architecture Design — 2D-Vektordesign sowie normgerechte Pläne aus einem semantischen Gebäudemodell.", "about.copyright": "© 2026 Karim Gabriele Varano", "about.license": "Lizenz: AGPL-3.0-or-later", - "about.suite": "Teil der openbureau-Suite", + "about.suite": "Teil von openbureau", "about.licenses": "Open-Source-Lizenzen (Drittkomponenten)", "about.close": "Schließen", + "about.checkUpdate": "Nach Updates suchen", + "about.checkingUpdate": "Suche nach Updates …", + + "update.title": "Update", + "update.upToDate": "Du hast bereits die aktuellste Version.", + "update.availableBody": "Version {version} ist verfügbar. Jetzt herunterladen und installieren? Die App startet danach neu.", + "update.installNow": "Installieren", + "update.installLater": "Später", + "update.checkFailed": "Update-Prüfung fehlgeschlagen: {error}", + "update.installFailed": "Installation fehlgeschlagen: {error}", // ── Einstellungs-Fenster ────────────────────────────────────────────────── "topbar.settings": "Einstellungen", @@ -1344,6 +1354,19 @@ export const de = { "settings.referenceElevation": "Referenzhöhe EG (m ü. M.)", "settings.referenceElevation.hint": "Nur gespeichert — wird noch nicht verwendet. Folgt für Terrain-Draping/reale Höhen relativ zum Erdgeschoss.", + "settings.section.update": "Update", + "settings.update.autoCheck": "Automatisch nach Updates suchen", + "settings.update.snooze.active": "Aktiv", + "settings.update.snooze.1day": "1 Tag pausieren", + "settings.update.snooze.1week": "1 Woche pausieren", + "settings.update.snooze.1month": "1 Monat pausieren", + "settings.update.snoozedUntil": "Pausiert bis {date}. Manuelles Suchen funktioniert weiterhin.", + "settings.update.versionHistory": "Frühere Versionen", + "settings.update.versionHistory.hint": + "Öffnet den Download im Browser — Installation bleibt manuell (ersetzt die aktuelle Version, wie beim ersten Installieren).", + "settings.update.loadingHistory": "Lade Versionsliste …", + "settings.update.noHistory": "Keine früheren Versionen verfügbar.", + "settings.update.download": "Herunterladen", // ── Drag & Drop (App-weiter Datei-Drop) ────────────────────────────────── "drop.hint": "DXF/DWG-Datei hier ablegen", diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 98ee5eb..4c0a476 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -138,7 +138,7 @@ export const en: Record = { "menu.native.view": "View", "menu.native.window": "Window", "menu.native.panels": "Panels", - "menu.native.about": "About Dossier", + "menu.native.about": "About dossier", "menu.native.quit": "Quit", // Layout menu. @@ -1305,12 +1305,22 @@ export const en: Record = { "exportSave.fmt.stl": "STL (3D mesh)", "about.title": "About dossier", "about.version": "Version", - "about.desc": "Open-source CAAD (Computer Aided Architecture Design) — beautiful, standard-compliant drawings from a semantic building model. Available as a desktop app (full version) and in the browser.", + "about.desc": "Open Source Computer Aided Architecture Design — 2D vector design plus standards-compliant drawings from a semantic building model.", "about.copyright": "© 2026 Karim Gabriele Varano", "about.license": "License: AGPL-3.0-or-later", - "about.suite": "Part of the openbureau suite", + "about.suite": "Part of openbureau", "about.licenses": "Open-source licenses (third-party)", "about.close": "Close", + "about.checkUpdate": "Check for updates", + "about.checkingUpdate": "Checking for updates …", + + "update.title": "Update", + "update.upToDate": "You already have the latest version.", + "update.availableBody": "Version {version} is available. Download and install now? The app will restart afterwards.", + "update.installNow": "Install", + "update.installLater": "Later", + "update.checkFailed": "Update check failed: {error}", + "update.installFailed": "Installation failed: {error}", // Settings dialog. "topbar.settings": "Settings", @@ -1331,6 +1341,19 @@ export const en: Record = { "settings.referenceElevation": "Ground floor reference elevation (m a.s.l.)", "settings.referenceElevation.hint": "Stored only — not used yet. Terrain draping / real-world elevations relative to the ground floor will follow.", + "settings.section.update": "Update", + "settings.update.autoCheck": "Check for updates automatically", + "settings.update.snooze.active": "Active", + "settings.update.snooze.1day": "Pause for 1 day", + "settings.update.snooze.1week": "Pause for 1 week", + "settings.update.snooze.1month": "Pause for 1 month", + "settings.update.snoozedUntil": "Paused until {date}. Manual checks still work.", + "settings.update.versionHistory": "Earlier versions", + "settings.update.versionHistory.hint": + "Opens the download in your browser — installation stays manual (replaces the current version, same as the first install).", + "settings.update.loadingHistory": "Loading version list …", + "settings.update.noHistory": "No earlier versions available.", + "settings.update.download": "Download", // Drag & drop (app-wide file drop). "drop.hint": "Drop a DXF/DWG file here", diff --git a/src/native/appMenu.ts b/src/native/appMenu.ts index ab68d0e..a0288e2 100644 --- a/src/native/appMenu.ts +++ b/src/native/appMenu.ts @@ -319,14 +319,27 @@ export function useNativeAppMenu( // macOS: der allererste Eintrag ist das App-Menü (Über/Beenden). Ohne ihn // rutscht „Datei" an die Stelle, an der macOS sonst den App-Namen zeigt. - // „Über Dossier" nutzt das NATIVE macOS-Info-Fenster (PredefinedMenuItem + // „Über dossier" nutzt das NATIVE macOS-Info-Fenster (PredefinedMenuItem // „About") statt des eigenen In-App-Dialogs (AboutDialog in TopBar.tsx) — // an dieser Stelle erwartet macOS exakt diesen Eintrag, kein Custom-Dialog. + // Metadaten spiegeln den In-App-Dialog: kleingeschriebener Name „dossier", + // Beschreibung + Lizenz/Suite als `credits` (macOS zeigt sie als Textblock + // unter der Version), Copyright unten. Das Icon zieht macOS aus dem Bundle + // (tauri.conf.json bundle.icon → icon.icns). Auf macOS NICHT unterstützt + // und daher weggelassen: authors/comments/license/website. const appMenu = await Submenu.new({ - text: "Dossier", + text: "dossier", items: [ await PredefinedMenuItem.new({ - item: { About: { name: "Dossier", version: APP_VERSION } }, + item: { + About: { + name: "dossier", + version: APP_VERSION, + shortVersion: APP_VERSION, + copyright: t("about.copyright"), + credits: `${t("about.desc")}\n\n${t("about.license")} · ${t("about.suite")}`, + }, + }, text: t("menu.native.about"), }), await PredefinedMenuItem.new({ item: "Separator" }), diff --git a/src/native/contextImportWindow.ts b/src/native/contextImportWindow.ts index 7b7627d..c051100 100644 --- a/src/native/contextImportWindow.ts +++ b/src/native/contextImportWindow.ts @@ -41,7 +41,7 @@ export async function openContextImportWindowNative(): Promise { } new WebviewWindow(CONTEXT_IMPORT_WINDOW_LABEL, { url: "index.html?window=context-import", - title: "Standort importieren — Dossier", + title: "Standort importieren — dossier", width: 480, height: 680, minWidth: 400, diff --git a/src/native/drawingLevelsWindow.ts b/src/native/drawingLevelsWindow.ts index 770a2c8..6438a32 100644 --- a/src/native/drawingLevelsWindow.ts +++ b/src/native/drawingLevelsWindow.ts @@ -51,7 +51,7 @@ export async function openDrawingLevelsWindowNative(): Promise { } new WebviewWindow(DRAWING_LEVELS_WINDOW_LABEL, { url: "index.html?window=drawing-levels", - title: "Zeichnungsebenen — Dossier", + title: "Zeichnungsebenen — dossier", width: 340, height: 560, minWidth: 260, diff --git a/src/native/layerSettingsWindow.ts b/src/native/layerSettingsWindow.ts index 5727ca2..4f3ca3b 100644 --- a/src/native/layerSettingsWindow.ts +++ b/src/native/layerSettingsWindow.ts @@ -54,7 +54,7 @@ export async function openLayerSettingsWindowNative(code: string): Promise } new WebviewWindow(LAYER_SETTINGS_WINDOW_LABEL, { url: `index.html?window=layer-settings&code=${encodeURIComponent(code)}`, - title: "Ebeneneinstellungen — Dossier", + title: "Ebeneneinstellungen — dossier", width: 360, height: 420, minWidth: 300, diff --git a/src/native/resourceWindow.ts b/src/native/resourceWindow.ts index 1c79bd6..40d6c0b 100644 --- a/src/native/resourceWindow.ts +++ b/src/native/resourceWindow.ts @@ -47,7 +47,7 @@ export async function openResourceWindowNative(initialTab?: TabId): Promise { } new WebviewWindow(SETTINGS_WINDOW_LABEL, { url: "index.html?window=settings", - title: "Einstellungen — Dossier", + title: "Einstellungen — dossier", width: 520, height: 640, minWidth: 420, diff --git a/src/styles.css b/src/styles.css index 0513f4f..f370816 100644 --- a/src/styles.css +++ b/src/styles.css @@ -630,9 +630,9 @@ body { border: 1px solid var(--border, #3a3f47); box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5); color: var(--ink, #e6e8ea); - /* Der Dialog ist ein DOM-Kind der `.topbar` (white-space: nowrap) — ohne - dieses Reset liefe der mehrzeilige Beschreibungstext einzeilig aus dem - Dialog heraus. Hier wieder normalen Zeilenumbruch erzwingen. */ + /* Defensiv gesetzt (unabhängig davon, ob ein Vorfahre white-space: nowrap + setzt) — ohne dieses Reset liefe der mehrzeilige Beschreibungstext + einzeilig aus dem Dialog heraus. */ white-space: normal; } .about-brand { @@ -676,9 +676,15 @@ body { line-height: 1.6; color: var(--label, #c4c8cd); } -.about-close { - appearance: none; +.about-actions { + display: flex; + justify-content: flex-end; + gap: 8px; margin-top: 18px; +} +.about-close, +.about-check-update { + appearance: none; padding: 6px 14px; border-radius: 6px; border: 1px solid var(--border, #3a3f47); @@ -687,9 +693,14 @@ body { cursor: pointer; font-size: 12px; } -.about-close:hover { +.about-close:hover, +.about-check-update:hover:not(:disabled) { background: var(--accent-dim, #33383f); } +.about-check-update:disabled { + opacity: 0.6; + cursor: default; +} .tag { font-weight: 500; @@ -5392,6 +5403,35 @@ body { outline: none; box-shadow: 0 0 0 2px var(--accent-border); } +.settings-subsection { + display: flex; + flex-direction: column; + gap: 6px; + margin-top: 14px; + padding-top: 12px; + border-top: 1px solid var(--line); +} +.settings-select { + font-family: inherit; + font-size: 13px; + padding: 6px 9px; + border: none; + border-radius: 7px; + background: var(--field-bg); + color: var(--ink); +} +.settings-select:focus { + outline: none; + box-shadow: 0 0 0 2px var(--accent-border); +} +.settings-history-row { + display: flex; + align-items: center; + gap: 8px; +} +.settings-history-row .settings-select { + flex: 1; +} /* ── App-weites Drop-Feedback ────────────────────────────────────────────── Dezenter Rahmen + Hinweis, solange eine Datei über die App gezogen wird. */ diff --git a/src/ui/SettingsDialog.tsx b/src/ui/SettingsDialog.tsx index 028d459..06f531a 100644 --- a/src/ui/SettingsDialog.tsx +++ b/src/ui/SettingsDialog.tsx @@ -14,9 +14,25 @@ import { t } from "../i18n"; import { ACCENT_SWATCHES } from "../theme/accents"; import { ColorHexField } from "./ColorHexField"; import { LayoutMenu } from "./LayoutMenu"; +import { APP_VERSION } from "./TopBar"; import type { RendererMode } from "./StatusBar"; import type { LayoutState } from "../panels/types"; import type { ThemeMode } from "../theme/themeMode"; +import { checkForUpdate, getUpdateSnoozeUntil, setUpdateSnooze } from "../update/checkForUpdate"; +import { getRunningVersion } from "../update/version"; +import { + fetchVersionHistory, + openVersionDownload, + type VersionEntry, +} from "../update/versionHistory"; + +/** Pausier-Optionen fuer den stillen Start-Check (s. checkForUpdate.ts). */ +const SNOOZE_OPTIONS = [ + { id: "active", ms: null }, + { id: "1day", ms: 24 * 60 * 60 * 1000 }, + { id: "1week", ms: 7 * 24 * 60 * 60 * 1000 }, + { id: "1month", ms: 30 * 24 * 60 * 60 * 1000 }, +] as const; export interface SettingsDialogProps { /** Hell/Dunkel — Standard ist Hell (siehe theme/themeMode.ts). */ @@ -71,6 +87,22 @@ export function SettingsDialog({ onClose, windowed = false, }: SettingsDialogProps) { + const [checkingUpdate, setCheckingUpdate] = useState(false); + const [runningVersion, setRunningVersion] = useState(null); + const [snoozeUntil, setSnoozeUntilState] = useState(() => getUpdateSnoozeUntil()); + const [versionHistory, setVersionHistory] = useState(null); + const [selectedTag, setSelectedTag] = useState(""); + + useEffect(() => { + void getRunningVersion().then(setRunningVersion); + }, []); + useEffect(() => { + void fetchVersionHistory().then((entries) => { + setVersionHistory(entries); + if (entries.length > 0) setSelectedTag(entries[0].tag); + }); + }, []); + useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); @@ -208,6 +240,97 @@ export function SettingsDialog({ />
{t("settings.referenceElevation.hint")}
+ +
+
{t("settings.section.update")}
+
+ {t("about.version")} {runningVersion ?? APP_VERSION} +
+ + +
+ + + {snoozeUntil != null && ( +
+ {t("settings.update.snoozedUntil", { + date: new Date(snoozeUntil).toLocaleDateString(), + })} +
+ )} +
+ +
+ + {versionHistory == null ? ( +
{t("settings.update.loadingHistory")}
+ ) : versionHistory.length === 0 ? ( +
{t("settings.update.noHistory")}
+ ) : ( + <> +
+ + +
+
{t("settings.update.versionHistory.hint")}
+ + )} +
+
{!windowed && ( diff --git a/src/ui/TopBar.tsx b/src/ui/TopBar.tsx index a6957af..d7afb09 100644 --- a/src/ui/TopBar.tsx +++ b/src/ui/TopBar.tsx @@ -10,6 +10,8 @@ import type { Project } from "../model/types"; import { t } from "../i18n"; +import { checkForUpdate } from "../update/checkForUpdate"; +import { getRunningVersion } from "../update/version"; import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { Dropdown } from "./Dropdown"; @@ -1020,6 +1022,11 @@ export const APP_VERSION = "0.1.0"; * Wortmarke. Klick ausserhalb / Esc schliesst. */ function AboutDialog({ onClose }: { onClose: () => void }) { + const [checkingUpdate, setCheckingUpdate] = useState(false); + const [runningVersion, setRunningVersion] = useState(null); + useEffect(() => { + void getRunningVersion().then(setRunningVersion); + }, []); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); @@ -1028,7 +1035,14 @@ function AboutDialog({ onClose }: { onClose: () => void }) { return () => document.removeEventListener("keydown", onKey); }, [onClose]); - return ( + // Per Portal an document.body statt als DOM-Kind der .topbar rendern: die + // .topbar bildet (position:relative + z-index) einen eigenen Stacking- + // Context, der das hohe z-index:1000 von .about-overlay einsperrt — andere + // z-index:2-Elemente ausserhalb (z. B. das Lineal-Overlay im Grundriss) + // konnten dadurch trotzdem durch den abgedunkelten Hintergrund "durch- + // scheinen". Als Portal-Kind von body liegt der Dialog in keinem fremden + // Stacking-Context mehr. + return createPortal(
void }) { dossier.
- {t("about.version")} {APP_VERSION} + {t("about.version")} {runningVersion ?? APP_VERSION}

{t("about.desc")}

@@ -1059,11 +1073,29 @@ function AboutDialog({ onClose }: { onClose: () => void }) {
  • LibreDWG — GPL-3.0
  • acadrust — MPL-2.0
  • - +
    + + +
    -
    + , + document.body, ); } diff --git a/src/update/checkForUpdate.ts b/src/update/checkForUpdate.ts new file mode 100644 index 0000000..7716054 --- /dev/null +++ b/src/update/checkForUpdate.ts @@ -0,0 +1,81 @@ +// Selbst-Update: fragt `endpoints` aus tauri.conf.json ab (Gitea-Release-Asset +// `latest.json`, per `/releases/latest/download/` immer der aktuellste Release +// dieses Repos — die URL selbst muss bei neuen Versionen nicht angepasst +// werden). Nur unter Tauri aktiv; im reinen Browser/Vite-Dev-Server gibt es +// weder die Plugins noch etwas zu aktualisieren. +import { ask, message } from "@tauri-apps/plugin-dialog"; +import { t } from "../i18n"; +import { isTauriRuntime } from "../io/projectFile"; + +/** localStorage-Key fuer die Update-Pause (s. `getUpdateSnoozeUntil`). */ +const SNOOZE_KEY = "dossier.updateSnoozeUntil"; + +/** Ende der aktuellen Update-Pause (ms epoch), `null` wenn keine aktiv/abgelaufen. */ +export function getUpdateSnoozeUntil(): number | null { + const raw = localStorage.getItem(SNOOZE_KEY); + if (!raw) return null; + const n = Number(raw); + if (!Number.isFinite(n) || n <= Date.now()) return null; + return n; +} + +/** Pausiert den STILLEN Start-Check bis `untilMs` (epoch); `null` hebt die Pause auf. + * Der manuelle Check (Button in Settings/About) ignoriert die Pause immer — + * das ist eine explizite Nutzeraktion. */ +export function setUpdateSnooze(untilMs: number | null): void { + if (untilMs == null) localStorage.removeItem(SNOOZE_KEY); + else localStorage.setItem(SNOOZE_KEY, String(untilMs)); +} + +/** + * Prueft auf eine neue Version. `silent=true` (Start-Check) meldet sich nur + * bei einem GEFUNDENEN Update oder einem echten Fehler — nicht bei „aktuell", + * damit der App-Start nicht bei jedem Start einen Dialog zeigt. Respektiert + * ausserdem eine aktive Update-Pause (s. `setUpdateSnooze`). Der manuelle + * Aufruf (About-/Settings-Dialog, `silent=false`) meldet in jedem Fall ein + * Ergebnis und ignoriert die Pause. + */ +export async function checkForUpdate(silent: boolean): Promise { + if (!isTauriRuntime()) return; + if (silent && getUpdateSnoozeUntil() != null) return; + const { check } = await import("@tauri-apps/plugin-updater"); + let update: Awaited>; + try { + update = await check(); + } catch (err) { + if (!silent) { + await message(t("update.checkFailed", { error: String(err) }), { + title: t("update.title"), + kind: "error", + }); + } + return; + } + if (!update) { + if (!silent) { + await message(t("update.upToDate"), { + title: t("update.title"), + kind: "info", + }); + } + return; + } + const install = await ask(t("update.availableBody", { version: update.version }), { + title: t("update.title"), + kind: "info", + okLabel: t("update.installNow"), + cancelLabel: t("update.installLater"), + }); + if (!install) return; + try { + await update.downloadAndInstall(); + } catch (err) { + await message(t("update.installFailed", { error: String(err) }), { + title: t("update.title"), + kind: "error", + }); + return; + } + const { relaunch } = await import("@tauri-apps/plugin-process"); + await relaunch(); +} diff --git a/src/update/version.ts b/src/update/version.ts new file mode 100644 index 0000000..599d6cf --- /dev/null +++ b/src/update/version.ts @@ -0,0 +1,12 @@ +// Tatsaechlich laufende App-Version (aus Tauris eigenen Metadaten, NICHT der +// manuell gepflegte `APP_VERSION`-String in ui/TopBar.tsx) — vermeidet, dass +// die Anzeige von der echten Installation abweicht, falls jemand beim +// Versionsbump eine der beiden Stellen vergisst. +import { isTauriRuntime } from "../io/projectFile"; + +/** `null` ausserhalb von Tauri (Vite-Dev-Server im Browser, keine Installation). */ +export async function getRunningVersion(): Promise { + if (!isTauriRuntime()) return null; + const { getVersion } = await import("@tauri-apps/api/app"); + return getVersion(); +} diff --git a/src/update/versionHistory.ts b/src/update/versionHistory.ts new file mode 100644 index 0000000..f55afa4 --- /dev/null +++ b/src/update/versionHistory.ts @@ -0,0 +1,48 @@ +// Versionsverlauf fuer den manuellen Rollback (Settings → Update): Tauris +// Updater-Plugin kennt nur „neuere Version installieren", kein Downgrade- +// Manifest mit Auswahl. Statt das Plugin zu verbiegen, pflegen wir einen +// eigenen, schlanken Index `versions.json` (liegt auf demselben Gitea- +// Release „latest" wie `latest.json`, s. checkForUpdate.ts) und bieten pro +// Eintrag nur den DMG-Download im System-Browser an — Installation bleibt +// der gewohnte manuelle Schritt (DMG oeffnen, App in Applications ersetzen). +import { isTauriRuntime } from "../io/projectFile"; + +export interface VersionEntry { + version: string; + /** Git-Tag des Releases, z. B. "v0.1.0". */ + tag: string; + /** ISO-Datum. */ + pub_date: string; + notes: string; + /** Dateiname der DMG innerhalb dieses Release-Tags. */ + dmg: string; +} + +const VERSIONS_URL = + "https://git.openbureau.ch/karim/DOSSIER-STANDALONE/releases/download/latest/versions.json"; + +/** Leeres Array ausserhalb von Tauri oder bei Netzwerk-/Parsefehlern (keine + * Wurf-Semantik hier — der Aufrufer zeigt einfach eine leere Liste). */ +export async function fetchVersionHistory(): Promise { + if (!isTauriRuntime()) return []; + try { + const { fetch: tauriFetch } = await import("@tauri-apps/plugin-http"); + const res = await tauriFetch(VERSIONS_URL); + if (!res.ok) return []; + const data = (await res.json()) as VersionEntry[]; + return Array.isArray(data) ? data : []; + } catch { + return []; + } +} + +export function downloadUrlFor(entry: VersionEntry): string { + return `https://git.openbureau.ch/karim/DOSSIER-STANDALONE/releases/download/${entry.tag}/${entry.dmg}`; +} + +/** Oeffnet den DMG-Download im System-Standardbrowser (kein In-App-Download — + * Installation bleibt manuell). */ +export async function openVersionDownload(entry: VersionEntry): Promise { + const { open } = await import("@tauri-apps/plugin-shell"); + await open(downloadUrlFor(entry)); +}