feat(tauri): add signed updater
This commit is contained in:
parent
2b3898aace
commit
b248e0c53a
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
|
|
@ -109,6 +109,7 @@ jobs:
|
|||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY || '' }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD || '' }}
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
|
|
@ -121,6 +122,8 @@ jobs:
|
|||
releaseName: ${{ startsWith(github.ref, 'refs/tags/v') && format('OpenPencil {0}', github.ref_name) || '' }}
|
||||
releaseBody: ${{ steps.release_body.outputs.body || steps.changelog.outputs.release_notes }}
|
||||
releaseDraft: true
|
||||
uploadUpdaterJson: true
|
||||
uploadUpdaterSignatures: true
|
||||
|
||||
publish-npm:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -11,6 +11,9 @@ node_modules
|
|||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
# Tauri updater private signing keys
|
||||
/desktop/*.key
|
||||
!/desktop/*.key.pub
|
||||
|
||||
# Copied from node_modules at build time
|
||||
public/canvaskit.wasm
|
||||
|
|
|
|||
|
|
@ -94,15 +94,16 @@ The app editor session (`src/app/editor/session/create.ts`) is a thin Vue wrappe
|
|||
|
||||
### How to release
|
||||
|
||||
1. Update version in `package.json`, `packages/core/package.json`, `packages/cli/package.json`, `desktop/tauri.conf.json`
|
||||
1. Update version in `package.json`, `packages/core/package.json`, `packages/cli/package.json`, `packages/mcp/package.json`, `packages/vue/package.json`, `desktop/tauri.conf.json`, and `desktop/Cargo.toml`
|
||||
2. Update `CHANGELOG.md` — move "Unreleased" items under new version heading with date
|
||||
3. Commit: `Release v0.x.y`
|
||||
4. Tag: `git tag v0.x.y && git push --tags`
|
||||
5. The `build.yml` workflow triggers on `v*` tags and:
|
||||
5. Ensure GitHub release secrets include `TAURI_SIGNING_PRIVATE_KEY` (and `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` if the updater key is password-protected); the public updater key is configured in `desktop/tauri.conf.json`.
|
||||
6. The `build.yml` workflow triggers on `v*` tags and:
|
||||
- Builds Tauri binaries for macOS (arm64 + x64), Windows (x64 + arm64), Linux (x64)
|
||||
- Creates a draft GitHub Release with all platform binaries
|
||||
- Publishes `@open-pencil/core`, `@open-pencil/cli`, `@open-pencil/mcp`, and `@open-pencil/vue` to npm with provenance
|
||||
6. Go to GitHub Releases → edit the draft → paste changelog section → publish
|
||||
7. Go to GitHub Releases → edit the draft → paste changelog section → publish
|
||||
|
||||
### CI workflows
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
- Split the canvas into separate scene and overlay render layers so rulers, labels, selections, and input overlays are isolated from scene rendering.
|
||||
- Use `@use-gesture/vanilla` for wheel gesture lifecycle handling and faster trackpad zoom behavior.
|
||||
- Add auto-layout inspector controls for min/max dimensions, Auto gap distribution, wrap cross-axis gap, and two-axis padding controls.
|
||||
- Add signed Tauri updater configuration, release artifacts, startup update checks, and a native Check for Updates menu item.
|
||||
|
||||
### Fixes
|
||||
|
||||
|
|
|
|||
6
bun.lock
6
bun.lock
|
|
@ -22,7 +22,9 @@
|
|||
"@tauri-apps/plugin-dialog": "^2.6.0",
|
||||
"@tauri-apps/plugin-fs": "^2.4.5",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-shell": "^2.3.5",
|
||||
"@tauri-apps/plugin-updater": "^2.10.1",
|
||||
"@unhead/vue": "^2.1.10",
|
||||
"@use-gesture/vanilla": "^10.3.1",
|
||||
"@vueuse/core": "^14.2.1",
|
||||
|
|
@ -1042,8 +1044,12 @@
|
|||
|
||||
"@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.5.3", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ=="],
|
||||
|
||||
"@tauri-apps/plugin-process": ["@tauri-apps/plugin-process@2.3.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA=="],
|
||||
|
||||
"@tauri-apps/plugin-shell": ["@tauri-apps/plugin-shell@2.3.5", "", { "dependencies": { "@tauri-apps/api": "^2.10.1" } }, "sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg=="],
|
||||
|
||||
"@tauri-apps/plugin-updater": ["@tauri-apps/plugin-updater@2.10.1", "", { "dependencies": { "@tauri-apps/api": "^2.10.1" } }, "sha512-NFYMg+tWOZPJdzE/PpFj2qfqwAWwNS3kXrb1tm1gnBJ9mYzZ4WDRrwy8udzWoAnfGCHLuePNLY1WVCNHnh3eRA=="],
|
||||
|
||||
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
||||
|
|
|
|||
330
desktop/Cargo.lock
generated
330
desktop/Cargo.lock
generated
|
|
@ -1000,6 +1000,17 @@ dependencies = [
|
|||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"libredox",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
|
|
@ -1658,6 +1669,21 @@ 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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.20"
|
||||
|
|
@ -2112,6 +2138,7 @@ checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
|
|||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"libc",
|
||||
"redox_syscall 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2199,6 +2226,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"
|
||||
|
|
@ -2523,6 +2556,18 @@ dependencies = [
|
|||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-osa-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
"objc2",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-quartz-core"
|
||||
version = "0.3.2"
|
||||
|
|
@ -2594,7 +2639,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "open_pencil"
|
||||
version = "0.2.2"
|
||||
version = "0.11.8"
|
||||
dependencies = [
|
||||
"fix-path-env",
|
||||
"font-kit",
|
||||
|
|
@ -2605,11 +2650,19 @@ dependencies = [
|
|||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-fs",
|
||||
"tauri-plugin-opener",
|
||||
"tauri-plugin-process",
|
||||
"tauri-plugin-shell",
|
||||
"zip",
|
||||
"tauri-plugin-updater",
|
||||
"zip 2.4.2",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
@ -2636,6 +2689,20 @@ dependencies = [
|
|||
"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",
|
||||
"objc2-foundation",
|
||||
"objc2-osa-kit",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pango"
|
||||
version = "0.18.3"
|
||||
|
|
@ -2685,7 +2752,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.5.18",
|
||||
"smallvec",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
|
@ -3152,6 +3219,15 @@ dependencies = [
|
|||
"bitflags 2.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a"
|
||||
dependencies = [
|
||||
"bitflags 2.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.2"
|
||||
|
|
@ -3226,15 +3302,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",
|
||||
|
|
@ -3270,6 +3351,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 = "rustc_version"
|
||||
version = "0.4.1"
|
||||
|
|
@ -3292,6 +3387,79 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pki-types",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
|
||||
dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-platform-verifier"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
|
||||
dependencies = [
|
||||
"core-foundation 0.10.1",
|
||||
"core-foundation-sys",
|
||||
"jni",
|
||||
"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"
|
||||
|
|
@ -3307,6 +3475,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"
|
||||
|
|
@ -3364,6 +3541,29 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "3.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
|
||||
dependencies = [
|
||||
"bitflags 2.11.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.24.0"
|
||||
|
|
@ -3664,7 +3864,7 @@ dependencies = [
|
|||
"objc2-foundation",
|
||||
"objc2-quartz-core",
|
||||
"raw-window-handle",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.5.18",
|
||||
"tracing",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
|
|
@ -3743,6 +3943,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 = "swift-rs"
|
||||
version = "1.0.7"
|
||||
|
|
@ -3860,6 +4066,17 @@ dependencies = [
|
|||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.16"
|
||||
|
|
@ -4061,6 +4278,16 @@ dependencies = [
|
|||
"zbus",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
@ -4082,6 +4309,39 @@ dependencies = [
|
|||
"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",
|
||||
"rustls",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tar",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tempfile",
|
||||
"thiserror 2.0.18",
|
||||
"time",
|
||||
"tokio",
|
||||
"url",
|
||||
"windows-sys 0.60.2",
|
||||
"zip 4.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.10.0"
|
||||
|
|
@ -4302,6 +4562,16 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
@ -4597,6 +4867,12 @@ version = "0.2.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[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"
|
||||
|
|
@ -4896,6 +5172,15 @@ dependencies = [
|
|||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-root-certs"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com"
|
||||
version = "0.38.2"
|
||||
|
|
@ -5126,6 +5411,15 @@ dependencies = [
|
|||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
|
@ -5554,6 +5848,16 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yeslogic-fontconfig-sys"
|
||||
version = "6.0.0"
|
||||
|
|
@ -5690,6 +5994,12 @@ dependencies = [
|
|||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.3"
|
||||
|
|
@ -5738,6 +6048,18 @@ dependencies = [
|
|||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "4.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"crc32fast",
|
||||
"indexmap 2.13.0",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
[package]
|
||||
name = "open_pencil"
|
||||
version = "0.2.2"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
version = "0.11.8"
|
||||
description = "OpenPencil desktop app"
|
||||
authors = ["Danila Poyarkov"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
|
@ -30,4 +30,6 @@ zstd = "0.13"
|
|||
zip = { version = "2", default-features = false }
|
||||
font-kit = "0.14.3"
|
||||
fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs", branch = "dev" }
|
||||
tauri-plugin-updater = "2"
|
||||
tauri-plugin-process = "2"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
"dialog:default",
|
||||
"dialog:allow-save",
|
||||
"dialog:allow-open",
|
||||
"updater:default",
|
||||
"process:default",
|
||||
{
|
||||
"identifier": "fs:allow-read-file",
|
||||
"allow": [{ "path": "**" }]
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ pub fn run() {
|
|||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_fs::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.on_menu_event(|app, event| {
|
||||
handle_menu_event(app, event.id().0.as_str());
|
||||
})
|
||||
|
|
|
|||
|
|
@ -69,6 +69,11 @@ pub fn install_app_menu<R: tauri::Runtime>(app: &mut tauri::App<R>) -> tauri::Re
|
|||
Some("About OpenPencil"),
|
||||
None,
|
||||
)?)
|
||||
.item(
|
||||
&MenuItemBuilder::new("Check for Updates…")
|
||||
.id("check-updates")
|
||||
.build(app)?,
|
||||
)
|
||||
.separator()
|
||||
.item(&PredefinedMenuItem::services(app, None)?)
|
||||
.separator()
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"createUpdaterArtifacts": true,
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
|
|
@ -34,5 +35,14 @@
|
|||
"macOS": {
|
||||
"signingIdentity": "Developer ID Application: Danila Poyarkov (N7D7M3Q3CD)"
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDI2NDdCODAwQkQxMkU0QzUKUldURjVCSzlBTGhISnJORk1HOVlXREVDbXJycWs5U2xxSVBYa1dLMzRoYTVGRVYrUEhmTS9tWWwK",
|
||||
"endpoints": ["https://github.com/open-pencil/open-pencil/releases/latest/download/latest.json"],
|
||||
"windows": {
|
||||
"installMode": "passive"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
desktop/updater.key.pub
Normal file
1
desktop/updater.key.pub
Normal file
|
|
@ -0,0 +1 @@
|
|||
dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDI2NDdCODAwQkQxMkU0QzUKUldURjVCSzlBTGhISnJORk1HOVlXREVDbXJycWs5U2xxSVBYa1dLMzRoYTVGRVYrUEhmTS9tWWwK
|
||||
|
|
@ -49,7 +49,9 @@
|
|||
"@tauri-apps/plugin-dialog": "^2.6.0",
|
||||
"@tauri-apps/plugin-fs": "^2.4.5",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-shell": "^2.3.5",
|
||||
"@tauri-apps/plugin-updater": "^2.10.1",
|
||||
"@unhead/vue": "^2.1.10",
|
||||
"@use-gesture/vanilla": "^10.3.1",
|
||||
"@vueuse/core": "^14.2.1",
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ export const menuMessages = i18n('menu', {
|
|||
zoomOut: 'Zoom out',
|
||||
profiler: 'Performance profiler',
|
||||
language: 'Language',
|
||||
checkUpdates: 'Check for updates…',
|
||||
|
||||
moveToPage: 'Move to page',
|
||||
createInstance: 'Create instance',
|
||||
|
|
@ -267,5 +268,16 @@ export const dialogMessages = i18n('dialogs', {
|
|||
connected: 'Connected',
|
||||
search: 'Search…',
|
||||
noResults: 'No results',
|
||||
share: 'Share'
|
||||
share: 'Share',
|
||||
appUpToDate: 'OpenPencil is up to date',
|
||||
updateAvailableTitle: 'Update OpenPencil',
|
||||
updateAvailable: params('OpenPencil {version} is available.'),
|
||||
updateInstallPrompt:
|
||||
'Download and install it now? The app will restart after the update is installed.',
|
||||
downloadingUpdate: params('Downloading OpenPencil {version}'),
|
||||
updateInstalledTitle: 'Update installed',
|
||||
updateInstalled: params('OpenPencil {version} was installed{size}. Restarting now.'),
|
||||
updateUnavailable:
|
||||
'Updates are not available yet. Publish a signed release with latest.json first.',
|
||||
updateCheckFailed: params('Could not check for updates: {error}')
|
||||
})
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@
|
|||
"copyAsPNG": "Als PNG kopieren",
|
||||
"copyAsJSX": "Als JSX kopieren",
|
||||
"copyNodeId": "Knoten-ID kopieren",
|
||||
"copyXPath": "XPath kopieren"
|
||||
"copyXPath": "XPath kopieren",
|
||||
"checkUpdates": "Nach Updates suchen…"
|
||||
},
|
||||
"commands": {
|
||||
"undo": "Rückgängig",
|
||||
|
|
@ -209,6 +210,15 @@
|
|||
"connected": "Verbunden",
|
||||
"search": "Suchen…",
|
||||
"noResults": "Keine Ergebnisse",
|
||||
"share": "Teilen"
|
||||
"share": "Teilen",
|
||||
"appUpToDate": "OpenPencil ist auf dem neuesten Stand",
|
||||
"updateAvailableTitle": "OpenPencil aktualisieren",
|
||||
"updateAvailable": "OpenPencil {version} ist verfügbar.",
|
||||
"updateInstallPrompt": "Jetzt herunterladen und installieren? Die App wird nach der Installation neu gestartet.",
|
||||
"downloadingUpdate": "OpenPencil {version} wird heruntergeladen",
|
||||
"updateInstalledTitle": "Update installiert",
|
||||
"updateInstalled": "OpenPencil {version} wurde installiert{size}. Neustart jetzt.",
|
||||
"updateCheckFailed": "Updates konnten nicht geprüft werden: {error}",
|
||||
"updateUnavailable": "Updates sind noch nicht verfügbar. Veröffentliche zuerst ein signiertes Release mit latest.json."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@
|
|||
"copyAsPNG": "Copiar como PNG",
|
||||
"copyAsJSX": "Copiar como JSX",
|
||||
"copyNodeId": "Copiar ID del nodo",
|
||||
"copyXPath": "Copiar XPath"
|
||||
"copyXPath": "Copiar XPath",
|
||||
"checkUpdates": "Buscar actualizaciones…"
|
||||
},
|
||||
"commands": {
|
||||
"undo": "Deshacer",
|
||||
|
|
@ -209,6 +210,15 @@
|
|||
"connected": "Conectado",
|
||||
"search": "Buscar…",
|
||||
"noResults": "Sin resultados",
|
||||
"share": "Compartir"
|
||||
"share": "Compartir",
|
||||
"appUpToDate": "OpenPencil está actualizado",
|
||||
"updateAvailableTitle": "Actualizar OpenPencil",
|
||||
"updateAvailable": "OpenPencil {version} está disponible.",
|
||||
"updateInstallPrompt": "¿Descargar e instalar ahora? La app se reiniciará después de instalar la actualización.",
|
||||
"downloadingUpdate": "Descargando OpenPencil {version}",
|
||||
"updateInstalledTitle": "Actualización instalada",
|
||||
"updateInstalled": "OpenPencil {version} se instaló{size}. Reiniciando ahora.",
|
||||
"updateCheckFailed": "No se pudo buscar actualizaciones: {error}",
|
||||
"updateUnavailable": "Las actualizaciones aún no están disponibles. Publica primero una versión firmada con latest.json."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@
|
|||
"copyAsPNG": "Copier en PNG",
|
||||
"copyAsJSX": "Copier en JSX",
|
||||
"copyNodeId": "Copier l'ID du nœud",
|
||||
"copyXPath": "Copier le XPath"
|
||||
"copyXPath": "Copier le XPath",
|
||||
"checkUpdates": "Rechercher des mises à jour…"
|
||||
},
|
||||
"commands": {
|
||||
"undo": "Annuler",
|
||||
|
|
@ -209,6 +210,15 @@
|
|||
"connected": "Connecté",
|
||||
"search": "Rechercher…",
|
||||
"noResults": "Aucun résultat",
|
||||
"share": "Partager"
|
||||
"share": "Partager",
|
||||
"appUpToDate": "OpenPencil est à jour",
|
||||
"updateAvailableTitle": "Mettre à jour OpenPencil",
|
||||
"updateAvailable": "OpenPencil {version} est disponible.",
|
||||
"updateInstallPrompt": "Télécharger et installer maintenant ? L’app redémarrera après l’installation de la mise à jour.",
|
||||
"downloadingUpdate": "Téléchargement d’OpenPencil {version}",
|
||||
"updateInstalledTitle": "Mise à jour installée",
|
||||
"updateInstalled": "OpenPencil {version} a été installé{size}. Redémarrage maintenant.",
|
||||
"updateCheckFailed": "Impossible de rechercher des mises à jour : {error}",
|
||||
"updateUnavailable": "Les mises à jour ne sont pas encore disponibles. Publiez d’abord une version signée avec latest.json."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@
|
|||
"copyAsPNG": "Copia come PNG",
|
||||
"copyAsJSX": "Copia come JSX",
|
||||
"copyNodeId": "Copia ID nodo",
|
||||
"copyXPath": "Copia XPath"
|
||||
"copyXPath": "Copia XPath",
|
||||
"checkUpdates": "Controlla aggiornamenti…"
|
||||
},
|
||||
"commands": {
|
||||
"undo": "Annulla",
|
||||
|
|
@ -209,6 +210,15 @@
|
|||
"connected": "Connesso",
|
||||
"search": "Cerca…",
|
||||
"noResults": "Nessun risultato",
|
||||
"share": "Condividi"
|
||||
"share": "Condividi",
|
||||
"appUpToDate": "OpenPencil è aggiornato",
|
||||
"updateAvailableTitle": "Aggiorna OpenPencil",
|
||||
"updateAvailable": "OpenPencil {version} è disponibile.",
|
||||
"updateInstallPrompt": "Scaricare e installare ora? L’app verrà riavviata dopo l’installazione dell’aggiornamento.",
|
||||
"downloadingUpdate": "Download di OpenPencil {version}",
|
||||
"updateInstalledTitle": "Aggiornamento installato",
|
||||
"updateInstalled": "OpenPencil {version} è stato installato{size}. Riavvio in corso.",
|
||||
"updateCheckFailed": "Impossibile controllare gli aggiornamenti: {error}",
|
||||
"updateUnavailable": "Gli aggiornamenti non sono ancora disponibili. Pubblica prima una release firmata con latest.json."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@
|
|||
"copyAsPNG": "Kopiuj jako PNG",
|
||||
"copyAsJSX": "Kopiuj jako JSX",
|
||||
"copyNodeId": "Kopiuj ID węzła",
|
||||
"copyXPath": "Kopiuj XPath"
|
||||
"copyXPath": "Kopiuj XPath",
|
||||
"checkUpdates": "Sprawdź aktualizacje…"
|
||||
},
|
||||
"commands": {
|
||||
"undo": "Cofnij",
|
||||
|
|
@ -209,6 +210,15 @@
|
|||
"connected": "Połączono",
|
||||
"search": "Szukaj…",
|
||||
"noResults": "Brak wyników",
|
||||
"share": "Udostępnij"
|
||||
"share": "Udostępnij",
|
||||
"appUpToDate": "OpenPencil jest aktualny",
|
||||
"updateAvailableTitle": "Zaktualizuj OpenPencil",
|
||||
"updateAvailable": "OpenPencil {version} jest dostępny.",
|
||||
"updateInstallPrompt": "Pobrać i zainstalować teraz? Aplikacja uruchomi się ponownie po zainstalowaniu aktualizacji.",
|
||||
"downloadingUpdate": "Pobieranie OpenPencil {version}",
|
||||
"updateInstalledTitle": "Aktualizacja zainstalowana",
|
||||
"updateInstalled": "OpenPencil {version} został zainstalowany{size}. Ponowne uruchamianie.",
|
||||
"updateCheckFailed": "Nie udało się sprawdzić aktualizacji: {error}",
|
||||
"updateUnavailable": "Aktualizacje nie są jeszcze dostępne. Najpierw opublikuj podpisane wydanie z latest.json."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@
|
|||
"copyAsPNG": "Копировать как PNG",
|
||||
"copyAsJSX": "Копировать как JSX",
|
||||
"copyNodeId": "Копировать ID узла",
|
||||
"copyXPath": "Копировать XPath"
|
||||
"copyXPath": "Копировать XPath",
|
||||
"checkUpdates": "Проверить обновления…"
|
||||
},
|
||||
"commands": {
|
||||
"undo": "Отменить",
|
||||
|
|
@ -209,6 +210,15 @@
|
|||
"connected": "Подключено",
|
||||
"search": "Поиск…",
|
||||
"noResults": "Ничего не найдено",
|
||||
"share": "Поделиться"
|
||||
"share": "Поделиться",
|
||||
"appUpToDate": "OpenPencil обновлён до последней версии",
|
||||
"updateAvailableTitle": "Обновить OpenPencil",
|
||||
"updateAvailable": "Доступен OpenPencil {version}.",
|
||||
"updateInstallPrompt": "Скачать и установить сейчас? После установки обновления приложение перезапустится.",
|
||||
"downloadingUpdate": "Загрузка OpenPencil {version}",
|
||||
"updateInstalledTitle": "Обновление установлено",
|
||||
"updateInstalled": "OpenPencil {version} установлен{size}. Перезапуск.",
|
||||
"updateCheckFailed": "Не удалось проверить обновления: {error}",
|
||||
"updateUnavailable": "Обновления пока недоступны. Сначала опубликуйте подписанный релиз с latest.json."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@
|
|||
"copyAsPNG": "复制为 PNG",
|
||||
"copyAsJSX": "复制为 JSX",
|
||||
"copyNodeId": "复制节点 ID",
|
||||
"copyXPath": "复制 XPath"
|
||||
"copyXPath": "复制 XPath",
|
||||
"checkUpdates": "检查更新…"
|
||||
},
|
||||
"commands": {
|
||||
"undo": "撤销",
|
||||
|
|
@ -89,7 +90,7 @@
|
|||
"autoLayout": "自动布局",
|
||||
"alignment": "对齐",
|
||||
"appearance": "外观",
|
||||
"sizingFill": "填充",
|
||||
"fill": "填充",
|
||||
"stroke": "描边",
|
||||
"effects": "效果",
|
||||
"export": "导出",
|
||||
|
|
@ -158,6 +159,7 @@
|
|||
"rows": "行",
|
||||
"sizingFixed": "固定",
|
||||
"sizingHug": "适应内容",
|
||||
"sizingFill": "填充",
|
||||
"sizingHugShort": "适应",
|
||||
"sizingFillShort": "填充",
|
||||
"sizingFillFr": "填充 (fr)",
|
||||
|
|
@ -208,6 +210,15 @@
|
|||
"connected": "已连接",
|
||||
"search": "搜索…",
|
||||
"noResults": "无结果",
|
||||
"share": "分享"
|
||||
"share": "分享",
|
||||
"appUpToDate": "OpenPencil 已是最新版本",
|
||||
"updateAvailableTitle": "更新 OpenPencil",
|
||||
"updateAvailable": "OpenPencil {version} 可用。",
|
||||
"updateInstallPrompt": "现在下载并安装?更新安装后应用将重启。",
|
||||
"downloadingUpdate": "正在下载 OpenPencil {version}",
|
||||
"updateInstalledTitle": "更新已安装",
|
||||
"updateInstalled": "OpenPencil {version} 已安装{size}。正在重启。",
|
||||
"updateCheckFailed": "无法检查更新:{error}",
|
||||
"updateUnavailable": "更新尚不可用。请先发布带有 latest.json 的已签名版本。"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,20 +3,23 @@ import { onMounted } from 'vue'
|
|||
import { useHead } from '@unhead/vue'
|
||||
import { TooltipProvider } from 'reka-ui'
|
||||
|
||||
import { provideEditor } from '@open-pencil/vue'
|
||||
import { provideEditor, useI18n } from '@open-pencil/vue'
|
||||
import AppToast from '@/components/AppToast.vue'
|
||||
import { useEditorStore } from '@/app/editor/active-store'
|
||||
import { toast } from '@/app/shell/ui'
|
||||
import { useAppTheme } from '@/app/shell/theme'
|
||||
import { scheduleStartupUpdateCheck } from '@/app/shell/updater'
|
||||
|
||||
useHead({ titleTemplate: (title) => (title ? `${title} — OpenPencil` : 'OpenPencil') })
|
||||
|
||||
const store = useEditorStore()
|
||||
const { dialogs } = useI18n()
|
||||
provideEditor(store)
|
||||
useAppTheme()
|
||||
|
||||
onMounted(() => {
|
||||
toast.setupGlobalErrorHandler()
|
||||
scheduleStartupUpdateCheck(dialogs)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ import { tryOnScopeDispose } from '@vueuse/core'
|
|||
import { useEditorStore } from '@/app/editor/active-store'
|
||||
import { importFileDialog, openFileDialog } from '@/app/shell/menu/files'
|
||||
import { useAppTheme } from '@/app/shell/theme'
|
||||
import { checkForAppUpdate } from '@/app/shell/updater'
|
||||
import { createTab, closeTab, activeTab } from '@/app/tabs'
|
||||
import { IS_TAURI } from '@/constants'
|
||||
import { useEditorCommands } from '@open-pencil/vue'
|
||||
import { useEditorCommands, useI18n } from '@open-pencil/vue'
|
||||
|
||||
import type { EditorCommandId } from '@open-pencil/vue'
|
||||
|
||||
|
|
@ -55,6 +56,7 @@ export function useMenu() {
|
|||
|
||||
let unlisten: (() => void) | undefined
|
||||
const { setTheme } = useAppTheme()
|
||||
const { dialogs } = useI18n()
|
||||
const { runCommand } = useEditorCommands()
|
||||
|
||||
const actions: Partial<Record<string, () => void>> = {
|
||||
|
|
@ -90,6 +92,7 @@ export function useMenu() {
|
|||
'toggle-ui': () => {
|
||||
store.state.showUI = !store.state.showUI
|
||||
},
|
||||
'check-updates': () => void checkForAppUpdate({ messages: dialogs }),
|
||||
'text.bold': () => {
|
||||
const node = store.selectedNodes.value.find((item) => item.type === 'TEXT')
|
||||
updateSelectedText({ fontWeight: node && node.fontWeight >= 700 ? 400 : 700 })
|
||||
|
|
|
|||
121
src/app/shell/updater.ts
Normal file
121
src/app/shell/updater.ts
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import { promiseTimeout } from '@vueuse/core'
|
||||
|
||||
import { toast } from '@/app/shell/ui'
|
||||
import { IS_TAURI } from '@/constants'
|
||||
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
const STARTUP_UPDATE_CHECK_DELAY_MS = 2500
|
||||
|
||||
interface UpdaterMessages {
|
||||
appUpToDate: string
|
||||
updateAvailableTitle: string
|
||||
updateAvailable: (params: { version: string }) => string
|
||||
updateInstallPrompt: string
|
||||
downloadingUpdate: (params: { version: string }) => string
|
||||
updateInstalledTitle: string
|
||||
updateInstalled: (params: { version: string; size: string }) => string
|
||||
updateUnavailable: string
|
||||
updateCheckFailed: (params: { error: string }) => string
|
||||
}
|
||||
|
||||
interface UpdateCheckOptions {
|
||||
silent?: boolean
|
||||
messages: Ref<UpdaterMessages>
|
||||
}
|
||||
|
||||
let startupCheckStarted = false
|
||||
let updateCheckInFlight: Promise<void> | null = null
|
||||
|
||||
export async function checkForAppUpdate(options: UpdateCheckOptions) {
|
||||
if (!IS_TAURI) return
|
||||
if (updateCheckInFlight) return updateCheckInFlight
|
||||
|
||||
const { silent = false, messages } = options
|
||||
updateCheckInFlight = runUpdateCheck(silent, messages).finally(() => {
|
||||
updateCheckInFlight = null
|
||||
})
|
||||
return updateCheckInFlight
|
||||
}
|
||||
|
||||
export function scheduleStartupUpdateCheck(messages: Ref<UpdaterMessages>) {
|
||||
if (startupCheckStarted || !IS_TAURI) return
|
||||
startupCheckStarted = true
|
||||
void promiseTimeout(STARTUP_UPDATE_CHECK_DELAY_MS).then(() =>
|
||||
checkForAppUpdate({ silent: true, messages })
|
||||
)
|
||||
}
|
||||
|
||||
async function runUpdateCheck(silent: boolean, messages: Ref<UpdaterMessages>) {
|
||||
try {
|
||||
const [{ check }, { confirm, message }, { relaunch }] = await Promise.all([
|
||||
import('@tauri-apps/plugin-updater'),
|
||||
import('@tauri-apps/plugin-dialog'),
|
||||
import('@tauri-apps/plugin-process')
|
||||
])
|
||||
|
||||
const update = await check()
|
||||
const t = messages.value
|
||||
|
||||
if (!update) {
|
||||
if (!silent) toast.info(t.appUpToDate)
|
||||
return
|
||||
}
|
||||
|
||||
const details = [
|
||||
t.updateAvailable({ version: update.version }),
|
||||
update.body ? `\n${update.body}` : '',
|
||||
`\n${t.updateInstallPrompt}`
|
||||
].join('')
|
||||
|
||||
const shouldInstall = await confirm(details, {
|
||||
title: t.updateAvailableTitle,
|
||||
kind: 'info'
|
||||
})
|
||||
|
||||
if (!shouldInstall) return
|
||||
|
||||
let downloaded = 0
|
||||
let contentLength: number | undefined
|
||||
toast.info(t.downloadingUpdate({ version: update.version }))
|
||||
|
||||
await update.downloadAndInstall((event) => {
|
||||
if (event.event === 'Started') {
|
||||
contentLength = event.data.contentLength
|
||||
return
|
||||
}
|
||||
if (event.event === 'Progress') {
|
||||
downloaded += event.data.chunkLength
|
||||
}
|
||||
})
|
||||
|
||||
const sizeLabel = contentLength
|
||||
? ` (${formatBytes(downloaded)} of ${formatBytes(contentLength)})`
|
||||
: ''
|
||||
await message(t.updateInstalled({ version: update.version, size: sizeLabel }), {
|
||||
title: t.updateInstalledTitle,
|
||||
kind: 'info'
|
||||
})
|
||||
await relaunch()
|
||||
} catch (error) {
|
||||
if (!silent) {
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
toast.warning(
|
||||
isMissingUpdateManifestError(message)
|
||||
? messages.value.updateUnavailable
|
||||
: messages.value.updateCheckFailed({ error: message })
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isMissingUpdateManifestError(message: string) {
|
||||
return message.toLowerCase().includes('valid release json')
|
||||
}
|
||||
|
||||
function formatBytes(bytes: number) {
|
||||
if (bytes < 1024) return `${bytes} B`
|
||||
const kib = bytes / 1024
|
||||
if (kib < 1024) return `${kib.toFixed(1)} KiB`
|
||||
return `${(kib / 1024).toFixed(1)} MiB`
|
||||
}
|
||||
|
|
@ -29,9 +29,9 @@ const { copy, copied } = useClipboard({ copiedDuring: 1500 })
|
|||
<ToastDescription class="min-w-0 flex-1 select-text">
|
||||
{{ t.message }}<span v-if="t.count > 1" class="ml-1.5 opacity-70">×{{ t.count }}</span>
|
||||
</ToastDescription>
|
||||
<Tip v-if="t.variant === 'error'" :label="copied ? 'Copied!' : 'Copy error'">
|
||||
<Tip v-if="t.variant !== 'default'" :label="copied ? 'Copied!' : 'Copy message'">
|
||||
<button
|
||||
data-test-id="toast-copy-error"
|
||||
data-test-id="toast-copy-message"
|
||||
class="mt-0.5 shrink-0 cursor-pointer rounded p-0.5 opacity-70 hover:opacity-100"
|
||||
@click="copy(t.message)"
|
||||
>
|
||||
|
|
@ -40,7 +40,7 @@ const { copy, copied } = useClipboard({ copiedDuring: 1500 })
|
|||
</button>
|
||||
</Tip>
|
||||
<ToastClose
|
||||
v-if="t.variant === 'error'"
|
||||
v-if="t.variant !== 'default'"
|
||||
data-test-id="toast-close"
|
||||
class="mt-0.5 shrink-0 cursor-pointer rounded p-0.5 opacity-70 hover:opacity-100"
|
||||
>
|
||||
|
|
|
|||
Loading…
Reference in a new issue