Migrate to tauri v2 (#1400)

* Fix vite build (tauri build still broken)

* Fix yarn builds with a couple of shortcuts

* Fix file creation

* Fix documentDir behavior

* Got stream with default file

* Clean up

* Clean up

* Use 'unstable'; fix devtools callsite

The API changed a bit here, which forces us to use the unstable crate
feature. The call to open devtools is also new; it's now on the
webview not window.

Signed-off-by: Paul R. Tagliamonte <paul@kittycad.io>

* Bring back read_dir_recursive from v1

* Fix dates

* More fixes, incl. conf files

* cargo fmt

* Add Updater plugin

* Fix types

* Fix isTauri detection and updater bootup

* Schemas

* Clean up

* Disable devtools

* Attempt at fixing builds

* WIP Ubuntu dep

* WIP Ubuntu dep

* WIP keys in debug

* Enable updater only on release builds

* Reenable webtools on debug

* No linux bundles

* Typo

* Attemp at fixing --bundles none

* Manual tauri debug build

* Empty commit to trigger the CI

* Fix settings

* Empty commit to trigger the CI

* Merge branch 'main' into pierremtb/issue1349

* Add allow-create perm

* tauri-driver no cap

* Empty commit to trigger the CI

* Clean up

* Clean up

* Migrate to tauri v2
Fixes #1349

* Fix fmt

* Merge branch 'main' into pierremtb/issue1349

* Force BUILD_RELEASE: true

* Bump tauri to new beta

* Merge branch 'main' into pierremtb/issue1349

* Fix linux tests

* Fix types

* Add --verbose to tauri-action

* Move --verbose to front

* Back to tauri-driver@0.1.3 and single \ for win

* Back to latest driver, and windows wip

* Disable release conf temporarily

* Rollback to 2.0.0-beta.2

* Rollback to 2.0.0-beta.1

* Move bundle to root for src-tauri/tauri.release.conf.json

* All packages to latest (add http and shell to package.json)

* Testing latest commit for tauri-action

* Remove tauri action

* Add cat

* WIP

* Update ci.yml

* Disable release conf

* Disable rust cache

* Add tauri-action back for release builds

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Update .codespellrc

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Trigger CI

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Fix type

* Clean up

* More clean up

* Fix path concatenation with join

* Attempt at fixing linux tests

* Config clean up

* Downgrade to tauri-driver@0.1.3

* Looks like tauri v2 is actually doing better with linux package names ah!

* Change Linux apt packages

* Increase wdio connectionRetryTimeout

* Revert connectionRetryTimeout and bump tauri packages

* Back to latest tauri-driver

* Disable linux e2e tests

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Trigger CI

* Clean up

* Update snapshots

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Trigger CI

* Remove @sentry/react

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Rename migrated.json to desktop.json

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Trigger CI

* Change wasm url to http on Windows

---------

Signed-off-by: Paul R. Tagliamonte <paul@kittycad.io>
Co-authored-by: Paul R. Tagliamonte <paul@kittycad.io>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Adam Chalmers <adam.chalmers@zoo.dev>
This commit is contained in:
Pierre Jacquier
2024-04-09 08:04:36 -04:00
committed by GitHub
parent 979046f7e6
commit 544a7565e3
48 changed files with 2267 additions and 1062 deletions

View File

@ -1,3 +1,3 @@
[codespell]
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,absolutey,atleast
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,./src-tauri/gen/schemas

View File

@ -125,6 +125,9 @@ jobs:
fail-fast: false
matrix:
os: [macos-14, ubuntu-latest, windows-latest]
env:
TAURI_ARGS_MACOS: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }}
TAURI_ARGS_UBUNTU: ${{ matrix.os == 'ubuntu-latest' && '--bundles' || '' }}
steps:
- uses: actions/checkout@v4
@ -144,10 +147,12 @@ jobs:
sudo apt-get update &&
sudo apt-get install -y
libgtk-3-dev
libgtksourceview-3.0-dev
webkit2gtk-4.0
libappindicator3-dev
libayatana-appindicator3-dev
webkit2gtk-driver
libsoup-3.0-dev
libjavascriptcoregtk-4.1-dev
libwebkit2gtk-4.1-dev
at-spi2-core
xvfb
- name: Sync node version and setup cache
@ -161,7 +166,9 @@ jobs:
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
# TODO: re-enable for Windows builds, see https://github.com/tauri-apps/tauri/issues/9045
- name: Setup Rust cache
if: matrix.os != 'windows-latest'
uses: swatinem/rust-cache@v2
with:
workspaces: './src-tauri -> target'
@ -224,14 +231,14 @@ jobs:
with:
includeRelease: false
includeDebug: true
args: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }}
args: "${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}"
- name: Build the app (release) and sign
uses: tauri-apps/tauri-action@v0
if: ${{ env.BUILD_RELEASE == 'true' }}
env:
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
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_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
@ -240,7 +247,7 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}"
with:
args: "${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }} ${{ env.TAURI_CONF_ARGS }}"
args: "${{ env.TAURI_CONF_ARGS }} ${{ env.TAURI_ARGS_MACOS }} ${{ env.TAURI_ARGS_UBUNTU }}"
- uses: actions/upload-artifact@v3
if: matrix.os != 'ubuntu-latest'
@ -250,10 +257,11 @@ jobs:
with:
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
# TODO: re-enable linux e2e tests when possible
- name: Run e2e tests (linux only)
if: matrix.os == 'ubuntu-latest'
if: false
run: |
cargo install tauri-driver@0.1.3
cargo install tauri-driver
source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }}
export VITE_KC_API_BASE_URL
xvfb-run yarn test:e2e:tauri

1
.gitignore vendored
View File

@ -51,5 +51,6 @@ e2e/playwright/export-snapshots/*
## generated files
src/**/*.typegen.ts
src-tauri/gen
src/wasm-lib/grackle/stdlib_cube_partial.json

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -16,7 +16,12 @@
"@open-rpc/client-js": "^1.8.1",
"@react-hook/resize-observer": "^1.2.6",
"@replit/codemirror-interact": "^6.3.0",
"@tauri-apps/api": "^1.5.3",
"@tauri-apps/api": "^2.0.0-beta.7",
"@tauri-apps/plugin-dialog": "^2.0.0-beta.2",
"@tauri-apps/plugin-fs": "^2.0.0-beta.2",
"@tauri-apps/plugin-http": "^2.0.0-beta.2",
"@tauri-apps/plugin-os": "^2.0.0-beta.2",
"@tauri-apps/plugin-shell": "^2.0.0-beta.2",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.5.2",
@ -48,7 +53,6 @@
"react-router-dom": "^6.22.3",
"sketch-helpers": "^0.0.4",
"swr": "^2.2.2",
"tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1",
"three": "^0.160.0",
"toml": "^3.0.0",
"ts-node": "^10.9.2",
@ -86,7 +90,7 @@
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
"lint": "eslint --fix src",
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json",
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json",
"postinstall": "yarn xstate:typegen",
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\""
},
@ -112,7 +116,7 @@
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-env": "^7.24.3",
"@playwright/test": "^1.39.0",
"@tauri-apps/cli": "^1.5.11",
"@tauri-apps/cli": "^2.0.0-beta.12",
"@types/crypto-js": "^4.2.2",
"@types/debounce-promise": "^3.1.9",
"@types/pixelmatch": "^5.2.6",

2541
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,12 +7,12 @@ license = ""
repository = "https://github.com/KittyCAD/modeling-app"
default-run = "app"
edition = "2021"
rust-version = "1.60"
rust-version = "1.70"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "1.5.1", features = [] }
tauri-build = { version = "2.0.0-beta", features = [] }
[dependencies]
anyhow = "1"
@ -20,8 +20,13 @@ kittycad = "0.2.63"
oauth2 = "4.4.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tauri = { version = "1.6.1", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "devtools"] }
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tauri = { version = "2.0.0-beta", features = [ "devtools", "unstable"] }
tauri-plugin-dialog = { version = "2.0.0-beta.0" }
tauri-plugin-fs = { version = "2.0.0-beta.0" }
tauri-plugin-http = { version = "2.0.0-beta.0" }
tauri-plugin-os = { version = "2.0.0-beta.0" }
tauri-plugin-shell = { version = "2.0.0-beta.0" }
tauri-plugin-updater = { version = "2.0.0-beta.0" }
tokio = { version = "1.37.0", features = ["time"] }
toml = "0.8.2"

View File

@ -0,0 +1,87 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"context": "local",
"windows": [
"main"
],
"permissions": [
"path:default",
"event:default",
"window:default",
"app:default",
"resources:default",
"menu:default",
"tray:default",
"fs:allow-create",
"fs:allow-read-file",
"fs:allow-read-text-file",
"fs:allow-write-file",
"fs:allow-write-text-file",
"fs:allow-read-dir",
"fs:allow-copy-file",
"fs:allow-mkdir",
"fs:allow-remove",
"fs:allow-remove",
"fs:allow-rename",
"fs:allow-exists",
"fs:allow-stat",
{
"identifier": "fs:scope",
"allow": [
{
"path": "$HOME/**/*"
},
{
"path": "$HOME/.config"
},
{
"path": "$HOME/.config/**/*"
},
{
"path": "$APPCONFIG"
},
{
"path": "$APPCONFIG/**/*"
},
{
"path": "$DOCUMENT"
},
{
"path": "$DOCUMENT/**/*"
}
]
},
"shell:allow-open",
"dialog:allow-open",
"dialog:allow-save",
"dialog:allow-message",
"dialog:allow-ask",
"dialog:allow-confirm",
{
"identifier": "http:default",
"allow": [
"https://dev.kittycad.io/*",
"https://dev.zoo.dev/*",
"https://kittycad.io/*",
"https://zoo.dev/*",
"https://api.dev.kittycad.io/*",
"https://api.dev.zoo.dev/*"
]
},
"os:allow-platform",
"os:allow-version",
"os:allow-os-type",
"os:allow-family",
"os:allow-arch",
"os:allow-exe-extension",
"os:allow-locale",
"os:allow-hostname"
],
"platforms": [
"linux",
"macOS",
"windows"
]
}

View File

@ -4,11 +4,15 @@
use std::env;
use std::fs;
use std::io::Read;
use std::path::Path;
use std::path::PathBuf;
use anyhow::Result;
use oauth2::TokenResponse;
use serde::Serialize;
use std::process::Command;
use tauri::{InvokeError, Manager};
use tauri::ipc::InvokeError;
use tauri_plugin_shell::ShellExt;
const DEFAULT_HOST: &str = "https://api.kittycad.io";
/// This command returns the a json string parse from a toml file at the path.
@ -24,6 +28,56 @@ fn read_toml(path: &str) -> Result<String, InvokeError> {
Ok(value)
}
/// From https://github.com/tauri-apps/tauri/blob/1.x/core/tauri/src/api/dir.rs#L51
/// Removed from tauri v2
#[derive(Debug, Serialize)]
pub struct DiskEntry {
/// The path to the entry.
pub path: PathBuf,
/// The name of the entry (file name with extension or directory name).
pub name: Option<String>,
/// The children of this entry if it's a directory.
#[serde(skip_serializing_if = "Option::is_none")]
pub children: Option<Vec<DiskEntry>>,
}
/// From https://github.com/tauri-apps/tauri/blob/1.x/core/tauri/src/api/dir.rs#L51
/// Removed from tauri v2
fn is_dir<P: AsRef<Path>>(path: P) -> Result<bool> {
std::fs::metadata(path)
.map(|md| md.is_dir())
.map_err(Into::into)
}
/// From https://github.com/tauri-apps/tauri/blob/1.x/core/tauri/src/api/dir.rs#L51
/// Removed from tauri v2
#[tauri::command]
fn read_dir_recursive(path: &str) -> Result<Vec<DiskEntry>, InvokeError> {
let mut files_and_dirs: Vec<DiskEntry> = vec![];
// let path = path.as_ref();
for entry in fs::read_dir(path).map_err(|e| InvokeError::from_anyhow(e.into()))? {
let path = entry
.map_err(|e| InvokeError::from_anyhow(e.into()))?
.path();
if let Ok(flag) = is_dir(&path) {
files_and_dirs.push(DiskEntry {
path: path.clone(),
children: if flag {
Some(read_dir_recursive(path.to_str().expect("No path"))?)
} else {
None
},
name: path
.file_name()
.map(|name| name.to_string_lossy())
.map(|name| name.to_string()),
});
}
}
Ok(files_and_dirs)
}
/// This command returns a string that is the contents of a file at the path.
#[tauri::command]
fn read_txt_file(path: &str) -> Result<String, InvokeError> {
@ -85,7 +139,8 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
fs::write("/tmp/kittycad_user_code", details.user_code().secret())
.expect("Unable to write /tmp/kittycad_user_code file");
} else {
tauri::api::shell::open(&app.shell_scope(), auth_uri.secret(), None)
app.shell()
.open(auth_uri.secret(), None)
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
}
@ -165,12 +220,15 @@ fn show_in_folder(path: String) {
fn main() {
tauri::Builder::default()
.setup(|_app| {
#[cfg(debug_assertions)] // only include this code on debug builds
#[cfg(debug_assertions)]
{
let window = _app.get_window("main").unwrap();
// comment out the below if you don't devtools to open everytime.
// it's useful because otherwise devtools shuts everytime rust code changes.
window.open_devtools();
use tauri::Manager;
_app.get_webview("main").unwrap().open_devtools();
}
#[cfg(not(debug_assertions))]
{
_app.handle()
.plugin(tauri_plugin_updater::Builder::new().build())?;
}
Ok(())
})
@ -179,9 +237,14 @@ fn main() {
login,
read_toml,
read_txt_file,
read_dir_recursive,
show_in_folder,
])
.plugin(tauri_plugin_fs_extra::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_shell::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@ -1,63 +1,28 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"build": {
"beforeDevCommand": "yarn start",
"devPath": "http://localhost:3000",
"distDir": "../build"
"app": {
"security": {
"csp": null
},
"package": {
"productName": "zoo-modeling-app",
"version": "0.17.3"
},
"tauri": {
"allowlist": {
"all": false,
"dialog": {
"all": true,
"ask": true,
"confirm": true,
"message": true,
"open": true,
"save": true
},
"fs": {
"scope": [
"$HOME/**/*",
"$APPCONFIG",
"$APPCONFIG/**/*",
"$DOCUMENT",
"$DOCUMENT/**/*"
],
"all": true
},
"http": {
"request": true,
"scope": [
"https://dev.kittycad.io/*",
"https://dev.zoo.dev/*",
"https://kittycad.io/*",
"https://zoo.dev/*",
"https://api.dev.kittycad.io/*",
"https://api.dev.zoo.dev/*"
"windows": [
{
"fullscreen": false,
"height": 1200,
"resizable": true,
"title": "Zoo Modeling App",
"width": 1800
}
]
},
"os": {
"all": true
},
"shell": {
"open": true
},
"path": {
"all": true
}
"build": {
"beforeDevCommand": "yarn start",
"devUrl": "http://localhost:3000",
"frontendDist": "../build"
},
"bundle": {
"active": true,
"category": "DeveloperTool",
"copyright": "",
"deb": {
"depends": []
},
"externalBin": [],
"icon": [
"icons/32x32.png",
@ -66,7 +31,11 @@
"icons/icon.icns",
"icons/icon.ico"
],
"identifier": "dev.zoo.modeling-app",
"linux": {
"deb": {
"depends": []
}
},
"longDescription": "",
"macOS": {
"entitlements": null,
@ -79,20 +48,12 @@
"shortDescription": "",
"targets": "all"
},
"security": {
"csp": null
},
"updater": {
"active": false
},
"windows": [
{
"fullscreen": false,
"height": 1200,
"resizable": true,
"title": "Zoo Modeling App",
"width": 1800
}
]
"identifier": "dev.zoo.modeling-app",
"plugins": {
"shell": {
"open": true
}
},
"productName": "Zoo Modeling App",
"version": "0.17.3"
}

View File

@ -1,6 +0,0 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"package": {
"productName": "Zoo Modeling App"
}
}

View File

@ -1,6 +1,13 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": {
"bundle": {
"windows": {
"certificateThumbprint": "F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D",
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com"
}
},
"plugins": {
"updater": {
"active": true,
"endpoints": [
@ -8,14 +15,6 @@
],
"dialog": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUzNzA4MjBEQjFBRTY4NzYKUldSMmFLNnhEWUp3NCtsT21Jd05wQktOaGVkOVp6MUFma0hNTDRDSnI2RkJJTEZOWG1ncFhqcU8K"
},
"bundle": {
"identifier": "io.kittycad.modeling-app",
"windows": {
"certificateThumbprint": "F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D",
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com"
}
}
}
}

View File

@ -1,6 +0,0 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"package": {
"productName": "Zoo Modeling App"
}
}

View File

@ -254,7 +254,7 @@ export const Toolbar = () => {
onClick={() => commandBarSend({ type: 'Open' })}
className="rounded-r-full pr-4 self-stretch border-primary/30 hover:border-primary dark:border-chalkboard-80 dark:bg-chalkboard-80 text-primary"
>
{platform === 'darwin' ? '⌘K' : 'Ctrl+/'}
{platform === 'macos' ? '⌘K' : 'Ctrl+/'}
</ActionButton>
</div>
)

View File

@ -54,7 +54,7 @@ export const AppHeader = ({
>
Command Palette{' '}
<kbd className="bg-primary/10 dark:bg-chalkboard-100 dark:text-primary inline-block px-1 py-0.5 border-primary dark:border-chalkboard-90">
{platform === 'darwin' ? '⌘K' : 'Ctrl+/'}
{platform === 'macos' ? '⌘K' : 'Ctrl+/'}
</kbd>
</ActionButton>
)}

View File

@ -14,16 +14,10 @@ import {
} from 'xstate'
import { useCommandsContext } from 'hooks/useCommandsContext'
import { fileMachine } from 'machines/fileMachine'
import {
createDir,
removeDir,
removeFile,
renameFile,
writeFile,
} from '@tauri-apps/api/fs'
import { mkdir, remove, rename, create } from '@tauri-apps/plugin-fs'
import { readProject } from 'lib/tauriFS'
import { isTauri } from 'lib/isTauri'
import { sep } from '@tauri-apps/api/path'
import { join, sep } from '@tauri-apps/api/path'
import { DEFAULT_FILE_NAME, FILE_EXT } from 'lib/constants'
type MachineContext<T extends AnyStateMachine> = {
@ -56,7 +50,7 @@ export const FileMachineProvider = ({
commandBarSend({ type: 'Close' })
navigate(
`${paths.FILE}/${encodeURIComponent(
context.selectedDirectory + sep + event.data.name
context.selectedDirectory + sep() + event.data.name
)}`
)
}
@ -79,14 +73,13 @@ export const FileMachineProvider = ({
let name = event.data.name.trim() || DEFAULT_FILE_NAME
if (event.data.makeDir) {
await createDir(context.selectedDirectory.path + sep + name)
await mkdir(await join(context.selectedDirectory.path, name))
} else {
await writeFile(
await create(
context.selectedDirectory.path +
sep +
sep() +
name +
(name.endsWith(FILE_EXT) ? '' : FILE_EXT),
''
(name.endsWith(FILE_EXT) ? '' : FILE_EXT)
)
}
@ -99,12 +92,11 @@ export const FileMachineProvider = ({
const { oldName, newName, isDir } = event.data
let name = newName ? newName : DEFAULT_FILE_NAME
await renameFile(
context.selectedDirectory.path + sep + oldName,
context.selectedDirectory.path +
sep +
name +
(name.endsWith(FILE_EXT) || isDir ? '' : FILE_EXT)
await rename(
await join(context.selectedDirectory.path, oldName),
(await join(context.selectedDirectory.path, name)) +
(name.endsWith(FILE_EXT) || isDir ? '' : FILE_EXT),
{}
)
return (
oldName !== name && `Successfully renamed "${oldName}" to "${name}"`
@ -117,11 +109,11 @@ export const FileMachineProvider = ({
const isDir = !!event.data.children
if (isDir) {
await removeDir(event.data.path, {
await remove(event.data.path, {
recursive: true,
}).catch((e) => console.error('Error deleting directory', e))
} else {
await removeFile(event.data.path).catch((e) =>
await remove(event.data.path).catch((e) =>
console.error('Error deleting file', e)
)
}

View File

@ -1,8 +1,7 @@
import { type IndexLoaderData } from 'lib/types'
import type { FileEntry, IndexLoaderData } from 'lib/types'
import { paths } from 'lib/paths'
import { ActionButton } from './ActionButton'
import Tooltip from './Tooltip'
import { FileEntry } from '@tauri-apps/api/fs'
import { Dispatch, useEffect, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { Dialog, Disclosure } from '@headlessui/react'

View File

@ -13,7 +13,7 @@ import { Extension } from '@codemirror/state'
import { LanguageSupport } from '@codemirror/language'
import { useNavigate } from 'react-router-dom'
import { paths } from 'lib/paths'
import { FileEntry } from '@tauri-apps/api/fs'
import { FileEntry } from 'lib/types'
const DEFAULT_FILE_NAME: string = 'main.kcl'

View File

@ -136,7 +136,10 @@ function ProjectCard({
}`}
</span>
<span className="text-chalkboard-60 text-xs">
Edited {getDisplayedTime(project.entrypointMetadata.modifiedAt)}
Edited{' '}
{project.entrypointMetadata.mtime
? getDisplayedTime(project.entrypointMetadata.mtime)
: 'never'}
</span>
<div className="absolute z-10 bottom-2 right-2 flex gap-1 items-center opacity-0 group-hover:opacity-100 group-focus-within:opacity-100">
<ActionButton

View File

@ -17,23 +17,24 @@ const projectWellFormed = {
},
],
entrypointMetadata: {
accessedAt: now,
atime: now,
blksize: 32,
blocks: 32,
createdAt: now,
birthtime: now,
dev: 1,
gid: 1,
ino: 1,
isDir: false,
isDirectory: false,
isFile: true,
isSymlink: false,
mode: 1,
modifiedAt: now,
mtime: now,
nlink: 1,
permissions: { readonly: false, mode: 1 },
readonly: false,
rdev: 1,
size: 32,
uid: 1,
fileAttributes: null,
},
} satisfies ProjectWithEntryPointMetadata

View File

@ -87,7 +87,7 @@ function ProjectMenuPopover({
<div className="flex flex-col items-start py-0.5">
<span className="hidden text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block">
{isTauri() && file?.name
? file.name.slice(file.name.lastIndexOf(sep) + 1)
? file.name.slice(file.name.lastIndexOf(sep()) + 1)
: APP_NAME}
</span>
{isTauri() && project?.name && (
@ -135,7 +135,7 @@ function ProjectMenuPopover({
data-testid="createdAt"
>
Created{' '}
{project.entrypointMetadata.createdAt.toLocaleDateString()}
{project.entrypointMetadata.birthtime?.toLocaleDateString()}
</p>
)}
</div>

View File

@ -1,4 +1,4 @@
import { Platform, platform } from '@tauri-apps/api/os'
import { Platform, platform } from '@tauri-apps/plugin-os'
import { isTauri } from 'lib/isTauri'
import { useEffect, useState } from 'react'
@ -14,9 +14,9 @@ export default function usePlatform() {
void getPlatform()
} else {
if (navigator.userAgent.indexOf('Mac') !== -1) {
setPlatformName('darwin')
setPlatformName('macos')
} else if (navigator.userAgent.indexOf('Win') !== -1) {
setPlatformName('win32')
setPlatformName('windows')
} else if (navigator.userAgent.indexOf('Linux') !== -1) {
setPlatformName('linux')
}

View File

@ -20,7 +20,7 @@ import { bracket } from 'lib/exampleKcl'
import { getNodeFromPath } from './queryAst'
import { Params } from 'react-router-dom'
import { isTauri } from 'lib/isTauri'
import { writeTextFile } from '@tauri-apps/api/fs'
import { writeTextFile } from '@tauri-apps/plugin-fs'
import { toast } from 'react-hot-toast'
const PERSIST_CODE_TOKEN = 'persistCode'

View File

@ -1,10 +1,8 @@
import {
readDir,
readBinaryFile,
exists as tauriExists,
} from '@tauri-apps/api/fs'
import { readFile, exists as tauriExists } from '@tauri-apps/plugin-fs'
import { isTauri } from 'lib/isTauri'
import { join } from '@tauri-apps/api/path'
import { invoke } from '@tauri-apps/api/core'
import { FileEntry } from 'lib/types'
/// FileSystemManager is a class that provides a way to read files from the local file system.
/// It assumes that you are in a project since it is solely used by the std lib
@ -37,7 +35,7 @@ class FileSystemManager {
throw new Error(`Error reading file: ${error}`)
})
.then((file) => {
return readBinaryFile(file)
return readFile(file)
})
}
@ -71,7 +69,9 @@ class FileSystemManager {
throw new Error(`Error joining dir: ${error}`)
})
.then((p) => {
readDir(p, { recursive: true })
invoke<FileEntry[]>('read_dir_recursive', {
path: p,
})
.catch((error) => {
throw new Error(`Error reading dir: ${error}`)
})

View File

@ -78,7 +78,7 @@ const initialise = async () => {
: window.location.origin.includes('tauri://localhost')
? 'tauri://localhost' // custom protocol for macOS
: window.location.origin.includes('tauri.localhost')
? 'https://tauri.localhost' // fallback for Windows
? 'http://tauri.localhost' // fallback for Windows
: window.location.origin.includes('localhost')
? 'http://localhost:3000'
: window.location.origin && window.location.origin !== 'null'

View File

@ -1,8 +1,8 @@
import { isTauri } from './isTauri'
import { deserialize_files } from '../wasm-lib/pkg/wasm_lib'
import { browserSaveFile } from './browserSaveFile'
import { save } from '@tauri-apps/api/dialog'
import { writeBinaryFile } from '@tauri-apps/api/fs'
import { save } from '@tauri-apps/plugin-dialog'
import { writeFile } from '@tauri-apps/plugin-fs'
import JSZip from 'jszip'
@ -26,7 +26,7 @@ const save_ = async (file: ModelingAppFile) => {
}
// Write the file.
await writeBinaryFile(filePath, file.contents)
await writeFile(filePath, new Uint8Array(file.contents))
} else {
// Download the file to the user's computer.
// Now we need to download the files to the user's downloads folder.

View File

@ -1,6 +1,6 @@
export function isTauri(): boolean {
if (globalThis.window && typeof globalThis.window !== 'undefined') {
return '__TAURI__' in globalThis.window
return '__TAURI_INTERNALS__' in globalThis.window
}
return false
}

View File

@ -27,7 +27,7 @@ export const BROWSER_PATH = `%2F${BROWSER_PROJECT_NAME}%2F${BROWSER_FILE_NAME}${
export function getProjectMetaByRouteId(id?: string, defaultDir = '') {
if (!id) return undefined
const s = isTauri() ? sep : '/'
const s = isTauri() ? sep() : '/'
const decodedId = decodeURIComponent(id).replace(/\/$/, '') // remove trailing slash
const projectAndFile =

View File

@ -1,5 +1,10 @@
import { ActionFunction, LoaderFunction, redirect } from 'react-router-dom'
import { FileLoaderData, HomeLoaderData, IndexLoaderData } from './types'
import {
FileEntry,
FileLoaderData,
HomeLoaderData,
IndexLoaderData,
} from './types'
import { isTauri } from './isTauri'
import { getProjectMetaByRouteId, paths } from './paths'
import { BROWSER_PATH } from 'lib/paths'
@ -15,11 +20,11 @@ import {
initializeProjectDirectory,
} from './tauriFS'
import makeUrlPathRelative from './makeUrlPathRelative'
import { sep } from '@tauri-apps/api/path'
import { readDir, readTextFile } from '@tauri-apps/api/fs'
import { metadata } from 'tauri-plugin-fs-extra-api'
import { join, sep } from '@tauri-apps/api/path'
import { readTextFile, stat } from '@tauri-apps/plugin-fs'
import { kclManager } from 'lib/singletons'
import { fileSystemManager } from 'lang/std/fileSystemManager'
import { invoke } from '@tauri-apps/api/core'
// The root loader simply resolves the settings and any errors that
// occurred during the settings load
@ -81,7 +86,7 @@ export const fileLoader: LoaderFunction = async ({
if (!currentFileName || !currentFilePath) {
return redirect(
`${paths.FILE}/${encodeURIComponent(
`${params.id}${isTauri() ? sep : '/'}${PROJECT_ENTRYPOINT}`
`${params.id}${isTauri() ? sep() : '/'}${PROJECT_ENTRYPOINT}`
)}`
)
}
@ -89,10 +94,12 @@ export const fileLoader: LoaderFunction = async ({
// TODO: PROJECT_ENTRYPOINT is hardcoded
// until we support setting a project's entrypoint file
const code = await readTextFile(currentFilePath)
const entrypointMetadata = await metadata(
projectPath + sep + PROJECT_ENTRYPOINT
const entrypointMetadata = await stat(
await join(projectPath, PROJECT_ENTRYPOINT)
)
const children = await readDir(projectPath, { recursive: true })
const children = await invoke<FileEntry[]>('read_dir_recursive', {
path: projectPath,
})
kclManager.setCodeAndExecute(code, false)
// Set the file system manager to the project path

View File

@ -14,7 +14,7 @@ import {
} from 'lib/cameraControls'
import { isTauri } from 'lib/isTauri'
import { useRef } from 'react'
import { open } from '@tauri-apps/api/dialog'
import { open } from '@tauri-apps/plugin-dialog'
import { CustomIcon } from 'components/CustomIcon'
import Tooltip from 'components/Tooltip'

View File

@ -6,9 +6,8 @@ import {
import { Setting, createSettings, settings } from 'lib/settings/initialSettings'
import { SaveSettingsPayload, SettingsLevel } from './settingsTypes'
import { isTauri } from 'lib/isTauri'
import { removeFile, writeTextFile } from '@tauri-apps/api/fs'
import { exists } from 'tauri-plugin-fs-extra-api'
import * as TOML from '@iarna/toml'
import { remove, writeTextFile, exists } from '@tauri-apps/plugin-fs'
/**
* We expect the settings to be stored in a TOML file
@ -91,7 +90,7 @@ async function writeOrClearPersistedSettings(
)
} else {
if (isTauri() && (await exists(settingsFilePath))) {
await removeFile(settingsFilePath)
await remove(settingsFilePath)
}
localStorage.removeItem(settingsFilePath)
}

View File

@ -43,12 +43,12 @@ export function getSortFunction(sortBy: string) {
a: ProjectWithEntryPointMetadata,
b: ProjectWithEntryPointMetadata
) => {
if (a.entrypointMetadata?.modifiedAt && b.entrypointMetadata?.modifiedAt) {
if (a.entrypointMetadata?.mtime && b.entrypointMetadata?.mtime) {
return !sortBy || sortBy.includes('desc')
? b.entrypointMetadata.modifiedAt.getTime() -
a.entrypointMetadata.modifiedAt.getTime()
: a.entrypointMetadata.modifiedAt.getTime() -
b.entrypointMetadata.modifiedAt.getTime()
? b.entrypointMetadata.mtime.getTime() -
a.entrypointMetadata.mtime.getTime()
: a.entrypointMetadata.mtime.getTime() -
b.entrypointMetadata.mtime.getTime()
}
return 0
}

View File

@ -1,4 +1,3 @@
import { FileEntry } from '@tauri-apps/api/fs'
import {
deepFileFilter,
getNextProjectIndex,
@ -6,6 +5,7 @@ import {
interpolateProjectNameWithIndex,
isRelevantFileOrDir,
} from './tauriFS'
import type { FileEntry } from './types'
import { MAX_PADDING } from './constants'
describe('Test project name utility functions', () => {

View File

@ -1,15 +1,20 @@
import {
FileEntry,
createDir,
mkdir,
exists,
readDir,
readTextFile,
writeTextFile,
} from '@tauri-apps/api/fs'
import { appConfigDir, documentDir, homeDir, sep } from '@tauri-apps/api/path'
stat,
} from '@tauri-apps/plugin-fs'
import { invoke } from '@tauri-apps/api/core'
import {
appConfigDir,
documentDir,
homeDir,
join,
sep,
} from '@tauri-apps/api/path'
import { isTauri } from './isTauri'
import { type ProjectWithEntryPointMetadata } from 'lib/types'
import { metadata } from 'tauri-plugin-fs-extra-api'
import type { FileEntry, ProjectWithEntryPointMetadata } from 'lib/types'
import {
FILE_EXT,
INDEX_IDENTIFIER,
@ -33,10 +38,10 @@ export async function getInitialDefaultDir() {
try {
dir = await documentDir()
} catch (e) {
dir = `${await homeDir()}Documents/` // for headless Linux (eg. Github Actions)
dir = await join(await homeDir(), 'Documents') // for headless Linux (eg. Github Actions)
}
return dir + PROJECT_FOLDER
return await join(dir, PROJECT_FOLDER)
}
// Initializes the project directory and returns the path
@ -96,7 +101,7 @@ async function testAndCreateDir(
if (dirExists instanceof Error) {
returnValue.error = dirExists
} else if (dirExists === false) {
const newDirCreated = await createDir(directory, { recursive: true }).catch(
const newDirCreated = await mkdir(directory, { recursive: true }).catch(
(e) => {
console.error(
`Error creating directory ${directory}. Original error:`,
@ -129,14 +134,12 @@ export function isProjectDirectory(fileOrDir: Partial<FileEntry>) {
// and return the valid projects
export async function getProjectsInDir(projectDir: string) {
const readProjects = (
await readDir(projectDir, {
recursive: true,
})
await invoke<FileEntry[]>('read_dir_recursive', { path: projectDir })
).filter(isProjectDirectory)
const projectsWithMetadata = await Promise.all(
readProjects.map(async (p) => ({
entrypointMetadata: await metadata(p.path + sep + PROJECT_ENTRYPOINT),
entrypointMetadata: await stat(await join(p.path, PROJECT_ENTRYPOINT)),
...p,
}))
)
@ -196,8 +199,8 @@ export function deepFileFilterFlat(
// Read the contents of a project directory
// and return all relevant files and sub-directories recursively
export async function readProject(projectDir: string) {
const readFiles = await readDir(projectDir, {
recursive: true,
const readFiles = await invoke<FileEntry[]>('read_dir_recursive', {
path: projectDir,
})
return deepFileFilter(readFiles, isRelevantFileOrDir)
@ -285,29 +288,29 @@ export async function createNewProject(
const dirExists = await exists(path)
if (!dirExists) {
await createDir(path, { recursive: true }).catch((err) => {
await mkdir(path, { recursive: true }).catch((err) => {
console.error('Error creating new directory:', err)
throw err
})
}
await writeTextFile(path + sep + PROJECT_ENTRYPOINT, initCode).catch(
await writeTextFile(await join(path, PROJECT_ENTRYPOINT), initCode).catch(
(err) => {
console.error('Error creating new file:', err)
throw err
}
)
const m = await metadata(path)
const m = await stat(path)
return {
name: path.slice(path.lastIndexOf(sep) + 1),
name: path.slice(path.lastIndexOf(sep()) + 1),
path: path,
entrypointMetadata: m,
children: [
{
name: PROJECT_ENTRYPOINT,
path: path + sep + PROJECT_ENTRYPOINT,
path: await join(path, PROJECT_ENTRYPOINT),
children: [],
},
],
@ -371,17 +374,17 @@ export async function getUserSettingsFilePath(
filename: string = SETTINGS_FILE_EXT
) {
const dir = await appConfigDir()
return dir + filename
return await join(dir, filename)
}
export async function readSettingsFile(
path: string
): Promise<Partial<SaveSettingsPayload>> {
const dir = path.slice(0, path.lastIndexOf(sep))
const dir = path.slice(0, path.lastIndexOf(sep()))
const dirExists = await exists(dir)
if (!dirExists) {
await createDir(dir, { recursive: true })
await mkdir(dir, { recursive: true })
}
const settingsExist = dirExists ? await exists(path) : false

View File

@ -1,5 +1,4 @@
import { type Metadata } from 'tauri-plugin-fs-extra-api'
import { type FileEntry } from '@tauri-apps/api/fs'
import { type FileInfo } from '@tauri-apps/plugin-fs'
export type IndexLoaderData = {
code: string | null
@ -14,12 +13,25 @@ export type FileLoaderData = {
}
export type ProjectWithEntryPointMetadata = FileEntry & {
entrypointMetadata: Metadata
entrypointMetadata: FileInfo
}
export type HomeLoaderData = {
projects: ProjectWithEntryPointMetadata[]
}
// From https://github.com/tauri-apps/tauri/blob/1.x/tooling/api/src/fs.ts#L159
// Removed from tauri v2
export interface FileEntry {
path: string
/**
* Name of the directory/file
* can be null if the path terminates with `..`
*/
name?: string
/** Children of this entry if it's a directory; null otherwise */
children?: FileEntry[]
}
// From the very helpful @jcalz on StackOverflow: https://stackoverflow.com/a/58436959/22753272
type Join<K, P> = K extends string | number
? P extends string | number

View File

@ -2,7 +2,7 @@ import { createMachine, assign } from 'xstate'
import { Models } from '@kittycad/lib'
import withBaseURL from '../lib/withBaseURL'
import { isTauri } from 'lib/isTauri'
import { invoke } from '@tauri-apps/api'
import { invoke } from '@tauri-apps/api/core'
import { VITE_KC_API_BASE_URL } from 'env'
const SKIP_AUTH =

View File

@ -1,6 +1,5 @@
import { assign, createMachine } from 'xstate'
import { type ProjectWithEntryPointMetadata } from 'lib/types'
import { FileEntry } from '@tauri-apps/api/fs'
import type { FileEntry, ProjectWithEntryPointMetadata } from 'lib/types'
export const fileMachine = createMachine(
{

View File

@ -1,5 +1,5 @@
import { FormEvent, useEffect } from 'react'
import { removeDir, renameFile } from '@tauri-apps/api/fs'
import { remove, rename } from '@tauri-apps/plugin-fs'
import {
createNewProject,
getNextProjectIndex,
@ -31,7 +31,7 @@ import {
import useStateMachineCommands from '../hooks/useStateMachineCommands'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { useCommandsContext } from 'hooks/useCommandsContext'
import { sep } from '@tauri-apps/api/path'
import { join, sep } from '@tauri-apps/api/path'
import { homeCommandBarConfig } from 'lib/commandBarConfigs/homeCommandConfig'
import { useHotkeys } from 'react-hotkeys-hook'
import { isTauri } from 'lib/isTauri'
@ -76,7 +76,7 @@ const Home = () => {
event: EventFrom<typeof homeMachine>
) => {
if (event.data && 'name' in event.data) {
let projectPath = context.defaultDirectory + sep + event.data.name
let projectPath = context.defaultDirectory + sep() + event.data.name
onProjectOpen(
{
name: event.data.name,
@ -109,7 +109,7 @@ const Home = () => {
name = interpolateProjectNameWithIndex(name, nextIndex)
}
await createNewProject(context.defaultDirectory + sep + name)
await createNewProject(await join(context.defaultDirectory, name))
return `Successfully created "${name}"`
},
@ -124,9 +124,10 @@ const Home = () => {
name = interpolateProjectNameWithIndex(name, nextIndex)
}
await renameFile(
context.defaultDirectory + sep + oldName,
context.defaultDirectory + sep + name
await rename(
await join(context.defaultDirectory, oldName),
await join(context.defaultDirectory, name),
{}
)
return `Successfully renamed "${oldName}" to "${name}"`
},
@ -134,7 +135,7 @@ const Home = () => {
context: ContextFrom<typeof homeMachine>,
event: EventFrom<typeof homeMachine, 'Delete project'>
) => {
await removeDir(context.defaultDirectory + sep + event.data.name, {
await remove(await join(context.defaultDirectory, event.data.name), {
recursive: true,
})
return `Successfully deleted "${event.data.name}"`

View File

@ -22,7 +22,7 @@ export default function CmdK() {
<h2 className="text-2xl font-bold">Command Bar</h2>
<p className="my-4">
Press{' '}
{platformName === 'darwin' ? (
{platformName === 'macos' ? (
<>
<kbd className={kbdClasses}>K</kbd>
</>

View File

@ -19,7 +19,7 @@ import { useNavigate } from 'react-router-dom'
import { paths } from 'lib/paths'
import { useEffect } from 'react'
import { kclManager } from 'lib/singletons'
import { sep } from '@tauri-apps/api/path'
import { join } from '@tauri-apps/api/path'
import { APP_NAME, PROJECT_ENTRYPOINT } from 'lib/constants'
function OnboardingWithNewFile() {
@ -45,12 +45,12 @@ function OnboardingWithNewFile() {
nextIndex
)
const newFile = await createNewProject(
projectDirectory.current + sep + name,
await join(projectDirectory.current, name),
bracket
)
navigate(
`${paths.FILE}/${encodeURIComponent(
newFile.path + sep + PROJECT_ENTRYPOINT
await join(newFile.path, PROJECT_ENTRYPOINT)
)}${paths.ONBOARDING.INDEX}`
)
}

View File

@ -19,11 +19,11 @@ import {
interpolateProjectNameWithIndex,
} from 'lib/tauriFS'
import { ONBOARDING_PROJECT_NAME } from './Onboarding'
import { sep } from '@tauri-apps/api/path'
import { join, sep } from '@tauri-apps/api/path'
import { bracket } from 'lib/exampleKcl'
import { isTauri } from 'lib/isTauri'
import { invoke } from '@tauri-apps/api'
import toast from 'react-hot-toast'
import { invoke } from '@tauri-apps/api/core'
import React, { Fragment, useMemo, useRef, useState } from 'react'
import { Setting } from 'lib/settings/initialSettings'
import decamelize from 'decamelize'
@ -49,7 +49,7 @@ export const Settings = () => {
location.pathname
.replace(paths.FILE + '/', '')
.replace(paths.SETTINGS, '')
.slice(0, decodeURI(location.pathname).lastIndexOf(sep))
.slice(0, decodeURI(location.pathname).lastIndexOf(sep()))
)
: undefined
const [settingsLevel, setSettingsLevel] = useState<SettingsLevel>(
@ -90,7 +90,7 @@ export const Settings = () => {
nextIndex
)
const newFile = await createNewProject(
defaultDirectory + sep + name,
await join(defaultDirectory, name),
bracket
)
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)

View File

@ -1,6 +1,6 @@
import { ActionButton } from '../components/ActionButton'
import { isTauri } from '../lib/isTauri'
import { invoke } from '@tauri-apps/api/tauri'
import { invoke } from '@tauri-apps/api/core'
import { VITE_KC_SITE_BASE_URL, VITE_KC_API_BASE_URL } from '../env'
import { Themes, getSystemTheme } from '../lib/theme'
import { paths } from 'lib/paths'

162
yarn.lock
View File

@ -2101,76 +2101,116 @@
resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.2.0.tgz#874d36135e4badce2719e7bdc556ce240cbaff14"
integrity sha512-P5XgYoAw/vfW65byBbJQCw+cagdXDT/qH6wmABiLt4v4YBT2q2vqCOhihe+D1Nt325F/S/0Tkv6C5z0Lv+VBQQ==
"@tauri-apps/api@1.5.3", "@tauri-apps/api@^1.5.3":
version "1.5.3"
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-1.5.3.tgz#f7b362b1f30aadb0a8bbeb7ae111755c0ed33d73"
integrity sha512-zxnDjHHKjOsrIzZm6nO5Xapb/BxqUq1tc7cGkFXsFkGTsSWgCPH1D8mm0XS9weJY2OaR73I3k3S+b7eSzJDfqA==
"@tauri-apps/api@2.0.0-beta.4":
version "2.0.0-beta.4"
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.0.0-beta.4.tgz#7688950f6e03f38b3bac73585f8f4cdd61be6aa6"
integrity sha512-Nxtj28NYUo5iwYkpYslxmOPkdI2WkELU2e3UH9nbJm9Ydki2CQwJVGQxx4EANtdZcMNsEsUzRqaDTvEUYH1l6w==
"@tauri-apps/cli-darwin-arm64@1.5.11":
version "1.5.11"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.5.11.tgz#a831f98f685148e46e8050dbdddbf4bcdda9ddc6"
integrity sha512-2NLSglDb5VfvTbMtmOKWyD+oaL/e8Z/ZZGovHtUFyUSFRabdXc6cZOlcD1BhFvYkHqm+TqGaz5qtPR5UbqDs8A==
"@tauri-apps/api@^2.0.0-beta.7":
version "2.0.0-beta.7"
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.0.0-beta.7.tgz#a80a3ffc24e6ec8dbdc8131dc3e68d8f4342293d"
integrity sha512-cM7SJQP4DBkLLMOdybLFYUURWn/tng2FEdAnXlu42f3NhFxKL4KVeeQTkuwlgC7ePwwwvDSqiXGiF+dKOadY7w==
"@tauri-apps/cli-darwin-x64@1.5.11":
version "1.5.11"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.5.11.tgz#0afae17fe1e84b9699a6b9824cd83b60c6ebfa59"
integrity sha512-/RQllHiJRH2fJOCudtZlaUIjofkHzP3zZgxi71ZUm7Fy80smU5TDfwpwOvB0wSVh0g/ciDjMArCSTo0MRvL+ag==
"@tauri-apps/cli-darwin-arm64@2.0.0-beta.12":
version "2.0.0-beta.12"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0-beta.12.tgz#3a528ca69fda5659281c7c60583b1baff8e1ef51"
integrity sha512-+Ksrxc4QQE1R62OreCybM8PNnF090qhf+ZeRZTZ9JxOujz4Tq/RiE1vC5nRlDu+Cd3pL63fZ2TsANj2PnpN+qw==
"@tauri-apps/cli-linux-arm-gnueabihf@1.5.11":
version "1.5.11"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.5.11.tgz#c46166d7f6c1022105a13d530b1d1336f628981f"
integrity sha512-IlBuBPKmMm+a5LLUEK6a21UGr9ZYd6zKuKLq6IGM4tVweQa8Sf2kP2Nqs74dMGIUrLmMs0vuqdURpykQg+z4NQ==
"@tauri-apps/cli-darwin-x64@2.0.0-beta.12":
version "2.0.0-beta.12"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.0.0-beta.12.tgz#63e435a88b4a4abaf31d64641d829281dfab89df"
integrity sha512-KEkcJp7jfWMEiRFTJ3/yJhy8EFwgJPNGnNYjKTD5CAba9wMexxs23U7YLAWdSNrbSgPAox0pBqXaSj6p2HH0Wg==
"@tauri-apps/cli-linux-arm64-gnu@1.5.11":
version "1.5.11"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.5.11.tgz#fd5c539a03371e0ab6cd00563dced1610ceb8943"
integrity sha512-w+k1bNHCU/GbmXshtAhyTwqosThUDmCEFLU4Zkin1vl2fuAtQry2RN7thfcJFepblUGL/J7yh3Q/0+BCjtspKQ==
"@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-beta.12":
version "2.0.0-beta.12"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.0.0-beta.12.tgz#68c6e457443faea4e20691a1405f8fc553029eda"
integrity sha512-EUDen1XNF/WfG5hjTZ6P/8IcpYPFm2Ox/FwlBPlePKZJrc+/3IveME/oyC3wrloscodV41yL2ise4SZuJtNAHA==
"@tauri-apps/cli-linux-arm64-musl@1.5.11":
version "1.5.11"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.5.11.tgz#bf7f940c3aca981d7c240857a86568d5b6e8310f"
integrity sha512-PN6/dl+OfYQ/qrAy4HRAfksJ2AyWQYn2IA/2Wwpaa7SDRz2+hzwTQkvajuvy0sQ5L2WCG7ymFYRYMbpC6Hk9Pg==
"@tauri-apps/cli-linux-arm64-gnu@2.0.0-beta.12":
version "2.0.0-beta.12"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.0.0-beta.12.tgz#b559bc6598e82d66e99ae817773afed6607c5fcd"
integrity sha512-VUhCBB6kaQmi7MyXTRccqemmz7s5n15Z718OIW1n6wI68oh0IsvC9KsrbDfgOLZVG6RXzmtzyXwnaIFQAgl+Cg==
"@tauri-apps/cli-linux-x64-gnu@1.5.11":
version "1.5.11"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.5.11.tgz#17323105e3863a3f36d51771e642e489037ba59b"
integrity sha512-MTVXLi89Nj7Apcvjezw92m7ZqIDKT5SFKZtVPCg6RoLUBTzko/BQoXYIRWmdoz2pgkHDUHgO2OMJ8oKzzddXbw==
"@tauri-apps/cli-linux-arm64-musl@2.0.0-beta.12":
version "2.0.0-beta.12"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.0-beta.12.tgz#186076257e61a76b897e03fde53a281123510e3a"
integrity sha512-eus/fGCQZPcvwEa2HH2TJh1Xb2FeeVrnxL6lQseLPkgD7Wcu5mWz07AlLCMDOreUobb5vyOmm0L7d1HMIPrvEQ==
"@tauri-apps/cli-linux-x64-musl@1.5.11":
version "1.5.11"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.5.11.tgz#83e22026771ec8ab094922ab114a7385532aa16c"
integrity sha512-kwzAjqFpz7rvTs7WGZLy/a5nS5t15QKr3E9FG95MNF0exTl3d29YoAUAe1Mn0mOSrTJ9Z+vYYAcI/QdcsGBP+w==
"@tauri-apps/cli-linux-x64-gnu@2.0.0-beta.12":
version "2.0.0-beta.12"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.0.0-beta.12.tgz#17dfb1772ad9efd7e7fa7a2ee0c1a521bd47957b"
integrity sha512-C4SkDnjuhFSdSgsK8KNRj2b/oe/j1vrvI8+ZxUj+/o1jK71A2eiCN8pJlyMZYCN8wZUksGazxaFz62g0jceAEg==
"@tauri-apps/cli-win32-arm64-msvc@1.5.11":
version "1.5.11"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-1.5.11.tgz#817874d230fdb09e7211013006a9a22f66ace573"
integrity sha512-L+5NZ/rHrSUrMxjj6YpFYCXp6wHnq8c8SfDTBOX8dO8x+5283/vftb4vvuGIsLS4UwUFXFnLt3XQr44n84E67Q==
"@tauri-apps/cli-linux-x64-musl@2.0.0-beta.12":
version "2.0.0-beta.12"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.0-beta.12.tgz#bf010a14f1f6a1d574c2414236d7afa589766f6d"
integrity sha512-V8NlJ8wgNcMfKTGOubgfjRyyJVQsZxpOZkGjdfn/YK0UNdOC6iLuqxIki47hbnoJMqvuxa37lr7Z1JVawOMUyw==
"@tauri-apps/cli-win32-ia32-msvc@1.5.11":
version "1.5.11"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.5.11.tgz#dee1a00eb9e216415d9d6ab9386c35849613c560"
integrity sha512-oVlD9IVewrY0lZzTdb71kNXkjdgMqFq+ohb67YsJb4Rf7o8A9DTlFds1XLCe3joqLMm4M+gvBKD7YnGIdxQ9vA==
"@tauri-apps/cli-win32-arm64-msvc@2.0.0-beta.12":
version "2.0.0-beta.12"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.0.0-beta.12.tgz#68f5799582231f3a02e65e7d8dcab361d0baef44"
integrity sha512-dIsE5U4JRoVImW0PX9K82/dMjwugqc3DmgKbFKLYXgTFmSNaTNdojI/5VOPmbOIMJ8BNGDF8sjS80I0PZuEqvw==
"@tauri-apps/cli-win32-x64-msvc@1.5.11":
version "1.5.11"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.5.11.tgz#c003ce00b36d056a8b08e0ecf4633c2bba00c497"
integrity sha512-1CexcqUFCis5ypUIMOKllxUBrna09McbftWENgvVXMfA+SP+yPDPAVb8fIvUcdTIwR/yHJwcIucmTB4anww4vg==
"@tauri-apps/cli-win32-ia32-msvc@2.0.0-beta.12":
version "2.0.0-beta.12"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.0.0-beta.12.tgz#b9306ade1c24c111dd6b0a62ac768fc46bb99903"
integrity sha512-vTNrGzVV/LclD+4u+IOOwl1Ia2CqwZYK6AmMLp0ROLzbSn/9ROJJYe47V5VAZrnMjThEdb9fRL0FPYPk9yDaNA==
"@tauri-apps/cli@^1.5.11":
version "1.5.11"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-1.5.11.tgz#02beb559b3b55836c90a1ba9121b3fc50e3760cd"
integrity sha512-B475D7phZrq5sZ3kDABH4g2mEoUIHtnIO+r4ZGAAfsjMbZCwXxR/jlMGTEL+VO3YzjpF7gQe38IzB4vLBbVppw==
"@tauri-apps/cli-win32-x64-msvc@2.0.0-beta.12":
version "2.0.0-beta.12"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.0.0-beta.12.tgz#cb4f3a8c5b4fd86cf9f03ba1e60fea292660c466"
integrity sha512-Z/++lJ1FraoN1ZaNxYuCynNm7SyEGC+yd/adYQvhaQyudZa5HW/8eFkLx8FRcIQkEkSqLBkHf9VpwjnTDBVlwQ==
"@tauri-apps/cli@^2.0.0-beta.12":
version "2.0.0-beta.12"
resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-2.0.0-beta.12.tgz#5d4d6d41a4c86d1fdc7df42ec83baad88ae40879"
integrity sha512-MFh4Z093O+PXWI6KFR8E2zIOgpJ4zL8waDhFXVQgBpqiD4ieiqywjmbmNIWkVqYBGAia7ZI0juxpkZSyXT4f1A==
optionalDependencies:
"@tauri-apps/cli-darwin-arm64" "1.5.11"
"@tauri-apps/cli-darwin-x64" "1.5.11"
"@tauri-apps/cli-linux-arm-gnueabihf" "1.5.11"
"@tauri-apps/cli-linux-arm64-gnu" "1.5.11"
"@tauri-apps/cli-linux-arm64-musl" "1.5.11"
"@tauri-apps/cli-linux-x64-gnu" "1.5.11"
"@tauri-apps/cli-linux-x64-musl" "1.5.11"
"@tauri-apps/cli-win32-arm64-msvc" "1.5.11"
"@tauri-apps/cli-win32-ia32-msvc" "1.5.11"
"@tauri-apps/cli-win32-x64-msvc" "1.5.11"
"@tauri-apps/cli-darwin-arm64" "2.0.0-beta.12"
"@tauri-apps/cli-darwin-x64" "2.0.0-beta.12"
"@tauri-apps/cli-linux-arm-gnueabihf" "2.0.0-beta.12"
"@tauri-apps/cli-linux-arm64-gnu" "2.0.0-beta.12"
"@tauri-apps/cli-linux-arm64-musl" "2.0.0-beta.12"
"@tauri-apps/cli-linux-x64-gnu" "2.0.0-beta.12"
"@tauri-apps/cli-linux-x64-musl" "2.0.0-beta.12"
"@tauri-apps/cli-win32-arm64-msvc" "2.0.0-beta.12"
"@tauri-apps/cli-win32-ia32-msvc" "2.0.0-beta.12"
"@tauri-apps/cli-win32-x64-msvc" "2.0.0-beta.12"
"@tauri-apps/plugin-dialog@^2.0.0-beta.2":
version "2.0.0-beta.2"
resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-dialog/-/plugin-dialog-2.0.0-beta.2.tgz#64028104c51f018acfce96221a41f6389b9707b5"
integrity sha512-WugTn/8d5jYA0GL1JRIJgA1OSxG0h2V4PSZZzehgA3v7rPlIU6w9s2+dSRqj55aMj6hm3Az9YbQqC18nuaHkpw==
dependencies:
"@tauri-apps/api" "2.0.0-beta.4"
"@tauri-apps/plugin-fs@^2.0.0-beta.2":
version "2.0.0-beta.2"
resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-fs/-/plugin-fs-2.0.0-beta.2.tgz#b2dfcd72422f778e4c32edcfad24c1e96299c760"
integrity sha512-jqeRBrm0h9QUoep5OzHx5R0vgFCYVAmZIy45jJpR7hHvnEgUwDU8JLUUVPvWniq6tUtxjwr1V/a0Hm9pE9V+NQ==
dependencies:
"@tauri-apps/api" "2.0.0-beta.4"
"@tauri-apps/plugin-http@^2.0.0-beta.2":
version "2.0.0-beta.2"
resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-http/-/plugin-http-2.0.0-beta.2.tgz#b143b713e980d2c100854d1ef05b6f39edf1f8a0"
integrity sha512-g/FA/w/+a2uQQlXoVTy5HXIi/YZo3q3lnxhKkwCWE/IZOMoraDlTzM/H+MB9hUaubLAnuPUMth5LaeFC9jwdRw==
dependencies:
"@tauri-apps/api" "2.0.0-beta.4"
"@tauri-apps/plugin-os@^2.0.0-beta.2":
version "2.0.0-beta.2"
resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-os/-/plugin-os-2.0.0-beta.2.tgz#e3490c7874a2c6e5a3e5dca3b61839d8960baf9b"
integrity sha512-rhJ/sEYvEAeMUQt6UiFODi5dT6F/ciNZYBQrbFTwhIqwQ2fp87dmzsscMy7FQ9LOor8AW+kL1KWoadfgzA/mSA==
dependencies:
"@tauri-apps/api" "2.0.0-beta.4"
"@tauri-apps/plugin-shell@^2.0.0-beta.2":
version "2.0.0-beta.2"
resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-shell/-/plugin-shell-2.0.0-beta.2.tgz#1eff697246140f17478527b0d947d76d3403a226"
integrity sha512-9rWsfN7Wt+EuWmpmNnK8bCs+04fzhEYrHtWyLIAYxb9diFdcJrEoctCP9YM2v+Uf8/y8qFC7VCbZ/9VQHANymQ==
dependencies:
"@tauri-apps/api" "2.0.0-beta.4"
"@testing-library/dom@^9.0.0":
version "9.3.3"
@ -8364,12 +8404,6 @@ tar@^6.1.11:
mkdirp "^1.0.3"
yallist "^4.0.0"
"tauri-plugin-fs-extra-api@https://github.com/tauri-apps/tauri-plugin-fs-extra#v1":
version "0.0.0"
resolved "https://github.com/tauri-apps/tauri-plugin-fs-extra#b0a4a479cabb00bb7a689756f742ef89da4f2601"
dependencies:
"@tauri-apps/api" "1.5.3"
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"