From a1861c7e211444c3180a3bef04406901e7638d7e Mon Sep 17 00:00:00 2001 From: Eugene Volynko Date: Wed, 22 Jan 2025 22:22:59 +0200 Subject: [PATCH 1/3] Increment app version to 1.0.10 --- README.md | 2 +- package.json | 4 +++- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 4 ++-- src-tauri/tauri.conf.json | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fa033bc..6108835 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ ## ⬇️ Download -- macOS: dmg +- macOS: dmg - Windows: exe | microsoft store - Coming soon... - Linux: deb | flathub - Coming soon... diff --git a/package.json b/package.json index 531e445..55066c5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,9 @@ { "name": "plain-color", + "description": "Lightweight, versatile, cross-platform color picker app", "private": false, - "version": "1.0.9", + "version": "1.0.10", + "license": "GPL-3.0-only", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 5362d08..90d786e 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "PlainColor" -version = "1.0.9" +version = "1.0.10" dependencies = [ "base64 0.22.1", "cocoa", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index dcb17eb..60998f6 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "PlainColor" -version = "1.0.9" -description = "A Tauri App" +version = "1.0.10" +description = "Lightweight, versatile, cross-platform color picker app" authors = ["Eugene Volynko "] edition = "2021" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 86e8228..ee7f911 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "PlainColor", - "version": "1.0.9", + "version": "1.0.10", "identifier": "com.moduleart.plaincolor", "build": { "beforeDevCommand": "yarn dev", From 31c48f6ddebdb5df28154dce4b2e4f999c759eea Mon Sep 17 00:00:00 2001 From: Eugene Volynko Date: Wed, 22 Jan 2025 22:26:45 +0200 Subject: [PATCH 2/3] Fix: App starts on Palettes screen --- src/layouts/AppLayout/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/layouts/AppLayout/index.tsx b/src/layouts/AppLayout/index.tsx index f650f41..625c768 100644 --- a/src/layouts/AppLayout/index.tsx +++ b/src/layouts/AppLayout/index.tsx @@ -38,8 +38,10 @@ export const AppLayout: FC = () => { const navigate = useNavigate() const openWithHandler = (palettes: IPalette[]) => { - palettes.map((palette) => palettesStore.addPalette(palette)) - navigate('/palettes') + if (palettes.length) { + palettes.map((palette) => palettesStore.addPalette(palette)) + navigate('/palettes') + } } useEffect(() => { From 814bc76bc63c01c5e563ba15053d707192bf6c5b Mon Sep 17 00:00:00 2001 From: Eugene Volynko Date: Thu, 23 Jan 2025 05:12:50 +0200 Subject: [PATCH 3/3] Add Updater --- .gitignore | 4 + README.md | 12 +-- docs/RELEASE.md | 32 +++++++ package.json | 3 +- src-tauri/Cargo.lock | 109 +++++++++++++++++++++++- src-tauri/Cargo.toml | 1 + src-tauri/capabilities/default.json | 5 +- src-tauri/src/lib.rs | 5 +- src-tauri/tauri.conf.json | 12 ++- src-tauri/updater/generate-artifacts.js | 79 +++++++++++++++++ src/components/Updater/index.tsx | 70 +++++++++++++++ src/pages/SettingsPage/index.tsx | 2 + yarn.lock | 7 ++ 13 files changed, 330 insertions(+), 11 deletions(-) create mode 100644 docs/RELEASE.md create mode 100644 src-tauri/updater/generate-artifacts.js create mode 100644 src/components/Updater/index.tsx diff --git a/.gitignore b/.gitignore index a547bf3..fd39852 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ dist-ssr *.njsproj *.sln *.sw? + +# Tauri +/artifacts +latest.json \ No newline at end of file diff --git a/README.md b/README.md index 6108835..9027fac 100644 --- a/README.md +++ b/README.md @@ -61,10 +61,9 @@ If you want to report a bug, first, thank you, that helps us a lot. Please open ### v1.1.x - Add ability to reorder colors +- Add ability to reorder palettes - More color formats: - Common: - - Oklab `oklch(40.1% 0.123 21.57)` - - HSB/HSV `268, 69, 57` - RGB/RGBA from 0 to 1 `0,36; 0,18; 0,57` - LAB - RAL @@ -81,7 +80,8 @@ If you want to report a bug, first, thank you, that helps us a lot. Please open - 🖥️ Obj-C NSColor Calibrated RGB - 📱 Obj-C UIColor RGB - 🌊 .NET RGB/ARGB - - ☕ Java RGB/RGBA + - ☕ Java HEX + - ☕ Java RGB - 📱 Android RGB/ARGB - Custom color formatter - Add ability to change global shortcuts @@ -91,10 +91,12 @@ If you want to report a bug, first, thank you, that helps us a lot. Please open - Control Select with arrows up/down - Search for colors in palette - Fix: Sometimes the cursor is not visible - Hide cursor with `set_cursor_visible` ([issue](https://github.com/tauri-apps/tauri/issues/10231)) -- Add app updater - [Aperture size](https://github.com/ModuleArt/plain-color/pull/9#issuecomment-2599870209) -- Improve list performance (infinite scroll) - Move picker with arrows (step = 1px), make sure it cannot be out of screen bounds - Settings: Add "Reset to defaults" button - Instant copy shortcut `CommandOrControl+Alt+C` - Instant pick shortcut `CommandOrControl+Shift+C` +- Improve list performance (infinite scroll) +- Add color formats: + - Oklab `oklch(40.1% 0.123 21.57)` + - HSB/HSV `268, 69, 57` diff --git a/docs/RELEASE.md b/docs/RELEASE.md new file mode 100644 index 0000000..b7f746c --- /dev/null +++ b/docs/RELEASE.md @@ -0,0 +1,32 @@ +# How to release a new version + +### Preparation (skip if it's already done) + +1. Place `tauri.key` and `tauri.key.pub` files into `~/.tauri` folder +2. Add this to your `.zshrc`: + +``` +export TAURI_SIGNING_PRIVATE_KEY="$HOME/.tauri/tauri.key" +export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="$$$" +``` + +3. Refresh `.zshrc`: + +```bash +source ~/.zshrc +``` + +where `$$$` is password for the `tauri.key` + +### Build + +1. Build bundle: + +```bash +yarn t:build:release +``` + +### Release + +1. Create a new GitHub release +2. Attach all files from `/artifacts` folder to the release diff --git a/package.json b/package.json index 55066c5..27ef60c 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "preview": "vite preview", "t:dev": "tauri dev", "t:build:debug": "tauri build --debug", - "t:build:release": "tauri build" + "t:build:release": "tauri build && node src-tauri/updater/generate-artifacts.js" }, "dependencies": { "@phosphor-icons/react": "^2.1.7", @@ -25,6 +25,7 @@ "@tauri-apps/plugin-os": "~2", "@tauri-apps/plugin-process": "^2.0.0", "@tauri-apps/plugin-shell": "^2", + "@tauri-apps/plugin-updater": "~2", "@tauri-apps/plugin-window": "^2.0.0-alpha.1", "classnames": "^2.5.1", "color-is-dark": "^1.0.1", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 90d786e..a06970d 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -27,6 +27,7 @@ dependencies = [ "tauri-plugin-process", "tauri-plugin-shell", "tauri-plugin-single-instance", + "tauri-plugin-updater", "termcolor", "tokio", ] @@ -100,6 +101,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "arboard" version = "3.4.1" @@ -960,6 +970,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "derive_more" version = "0.99.18" @@ -1253,6 +1274,18 @@ dependencies = [ "rustc_version 0.4.1", ] +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + [[package]] name = "flate2" version = "1.0.34" @@ -2358,6 +2391,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", + "redox_syscall", ] [[package]] @@ -2450,6 +2484,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minisign-verify" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6367d84fb54d4242af283086402907277715b8fe46976963af5ebf173f8efba3" + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -2589,7 +2629,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "syn 2.0.87", @@ -4468,6 +4508,17 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "tar" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" version = "0.12.16" @@ -4797,6 +4848,36 @@ dependencies = [ "zbus 5.3.0", ] +[[package]] +name = "tauri-plugin-updater" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2d39224390c41ba544f02b4f1721f42256320b3fb8c371e9425cbddeb4a68c" +dependencies = [ + "base64 0.22.1", + "dirs", + "flate2", + "futures-util", + "http", + "infer", + "minisign-verify", + "percent-encoding", + "reqwest", + "semver 1.0.23", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror 2.0.9", + "time 0.3.36", + "tokio", + "url", + "windows-sys 0.59.0", + "zip", +] + [[package]] name = "tauri-runtime" version = "2.2.0" @@ -6220,6 +6301,17 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" +[[package]] +name = "xattr" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" +dependencies = [ + "libc", + "linux-raw-sys", + "rustix", +] + [[package]] name = "xdg-home" version = "1.3.0" @@ -6446,6 +6538,21 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "zip" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "indexmap 2.6.0", + "memchr", + "thiserror 2.0.9", +] + [[package]] name = "zvariant" version = "4.0.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 60998f6..baa5868 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -44,3 +44,4 @@ cocoa = "0.26.0" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-global-shortcut = "2" tauri-plugin-single-instance = "2" +tauri-plugin-updater = "2" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 1515644..c6f3d2f 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -50,6 +50,9 @@ ] }, "deep-link:default", - "deep-link:allow-get-current" + "deep-link:allow-get-current", + "updater:default", + "updater:allow-check", + "updater:allow-download-and-install" ] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e0dc61b..873f034 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -11,7 +11,7 @@ mod mod_screenshot; use tauri::{ generate_context, generate_handler, menu::{Menu, PredefinedMenuItem, Submenu}, - Builder, DragDropEvent, Emitter, Manager, WindowEvent, + DragDropEvent, Emitter, Manager, WindowEvent, }; #[allow(non_upper_case_globals)] @@ -24,7 +24,7 @@ pub fn run() { let loop_state = std::sync::Arc::new(tokio::sync::Mutex::new(mod_pickerloop::LoopState::default())); - Builder::default() + tauri::Builder::default() .manage(loop_state) .menu(|handle| { Menu::with_items( @@ -45,6 +45,7 @@ pub fn run() { ], ) }) + .plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_http::init()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_dialog::init()) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index ee7f911..8ee73c4 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -84,6 +84,16 @@ "role": "Editor", "mimeType": "application/octet-stream" } - ] + ], + "createUpdaterArtifacts": true + }, + "plugins": { + "updater": { + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEJBQkJEQkYwMjQ2ODk5NTYKUldSV21XZ2s4TnU3dXB2andtOS9vK0o1UE91MjBaZHR6T2g5UEpScHNTK0FBMExmRURLdjlUZ3oK", + "endpoints": ["https://github.com/ModuleArt/plain-color/releases/latest/download/latest.json"], + "windows": { + "installMode": "passive" + } + } } } diff --git a/src-tauri/updater/generate-artifacts.js b/src-tauri/updater/generate-artifacts.js new file mode 100644 index 0000000..c394b54 --- /dev/null +++ b/src-tauri/updater/generate-artifacts.js @@ -0,0 +1,79 @@ +import fs from 'fs' +import path from 'path' + +// load version + +const packageJsonPath = path.join('package.json') +let packageJson = {} +try { + packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')) +} catch (error) { + console.error('Failed to read package.json:', error) + process.exit(1) +} + +const VERSION = packageJson.version +if (!VERSION) { + console.error('Version is missing in package.json!') + process.exit(1) +} + +// load signature + +const signaturePath = path.join('src-tauri', 'target', 'release', 'bundle', 'macos', 'PlainColor.app.tar.gz.sig') +let signature = '' +try { + signature = fs.readFileSync(signaturePath, 'utf-8').trim() +} catch (error) { + console.error('Failed to read signature file:', error) + process.exit(1) +} + +const DOWNLOAD_URL = 'https://github.com/user/repo/releases/latest/download/PlainColor.app.tar.gz' + +const latestJson = { + version: VERSION, + notes: 'Update', + pub_date: new Date().toISOString(), + platforms: { + aarch64: { + url: DOWNLOAD_URL, + signature, + }, + }, +} + +// write artifacts + +const artifactsPath = path.join('artifacts') +const latestJsonOutputPath = path.join(artifactsPath, 'latest.json') + +try { + if (!fs.existsSync(artifactsPath)) { + fs.mkdirSync(artifactsPath) + } +} catch (error) { + console.error('Cannot access artifacts directory:', error) + process.exit(1) +} + +try { + fs.writeFileSync(latestJsonOutputPath, JSON.stringify(latestJson, null, 2), 'utf-8') + console.log(`latest.json generated successfully at ${latestJsonOutputPath}`) +} catch (error) { + console.error('Failed to generate latest.json:', error) + process.exit(1) +} + +const updaterName = 'PlainColor.app.tar.gz' +const updaterPath = path.join('src-tauri', 'target', 'release', 'bundle', 'macos', updaterName) +const dmgName = `PlainColor_${VERSION}_aarch64.dmg` +const dmgPath = path.join('src-tauri', 'target', 'release', 'bundle', 'dmg', dmgName) + +try { + fs.copyFileSync(updaterPath, path.join(artifactsPath, updaterName)) + fs.copyFileSync(dmgPath, path.join(artifactsPath, dmgName)) +} catch (error) { + console.error('Failed to copy artifacts:', error) + process.exit(1) +} diff --git a/src/components/Updater/index.tsx b/src/components/Updater/index.tsx new file mode 100644 index 0000000..aa37ef2 --- /dev/null +++ b/src/components/Updater/index.tsx @@ -0,0 +1,70 @@ +import { FC, useEffect, useState } from 'react' +import { check, Update } from '@tauri-apps/plugin-updater' +import { relaunch } from '@tauri-apps/plugin-process' +import { Text } from '@/components/Text' +import { Stack } from '@/components/Stack' +import { Button } from '@/components/Button' + +export const Updater: FC = () => { + const [updateAvailable, setUpdateAvailable] = useState(null) + const [isDownloading, setIsDownloading] = useState(false) + const [contentLength, setContentLength] = useState(0) + const [downloadedLength, setDownloadedLength] = useState(0) + const [isFinished, setIsFinished] = useState(false) + + useEffect(() => { + check().then((update) => { + if (update) { + setUpdateAvailable(update) + } + }) + }, []) + + const downloadAndInstall = () => { + setIsDownloading(true) + + updateAvailable + ?.downloadAndInstall((event) => { + switch (event.event) { + case 'Started': + setContentLength(event.data.contentLength || 0) + break + case 'Progress': + setDownloadedLength(downloadedLength + event.data.chunkLength) + break + case 'Finished': + setIsFinished(true) + break + } + }) + .then(() => { + relaunch() + }) + } + + if (!updateAvailable) return null + + return ( + + + + + + {!isDownloading && ( + +