Compare commits
12 Commits
v0.0.4
...
v0.0.5-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e29b48b98c | ||
|
|
21cdc7c32d | ||
|
|
27439593b4 | ||
|
|
ed040aadf5 | ||
|
|
8f885a5412 | ||
|
|
ee03bf0160 | ||
|
|
e0a84a0570 | ||
|
|
66d62b970a | ||
|
|
0a78a9d056 | ||
|
|
0b65cdb129 | ||
|
|
e2d8f3effd | ||
|
|
7a99672317 |
@@ -51,6 +51,9 @@
|
||||
"@typescript-eslint/no-unsafe-member-access": "warn",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-unused-vars": "warn",
|
||||
"jsx-a11y/click-events-have-key-events": "off"
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "warn",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-base-to-string": "warn"
|
||||
}
|
||||
}
|
||||
217
README.md
217
README.md
@@ -1,216 +1,3 @@
|
||||
# Tauri + Next.js Template
|
||||
# CS工具箱
|
||||
|
||||

|
||||
|
||||
This is a [Tauri](https://tauri.app/) project template using [Next.js](https://nextjs.org/),
|
||||
bootstrapped by combining [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app)
|
||||
and [`create tauri-app`](https://tauri.app/v1/guides/getting-started/setup).
|
||||
|
||||
This template uses [`pnpm`](https://pnpm.io/) as the Node.js dependency
|
||||
manager.
|
||||
|
||||
## Template Features
|
||||
|
||||
- TypeScript frontend using Next.js React framework
|
||||
- [TailwindCSS](https://tailwindcss.com/) as a utility-first atomic CSS framework
|
||||
- The example page in this template app has been updated to use only TailwindCSS
|
||||
- While not included by default, consider using
|
||||
[React Aria components](https://react-spectrum.adobe.com/react-aria/index.html)
|
||||
and/or [HeadlessUI components](https://headlessui.com/) for completely unstyled and
|
||||
fully accessible UI components, which integrate nicely with TailwindCSS
|
||||
- Opinionated formatting and linting already setup and enabled
|
||||
- [ESLint](https://eslint.org/) for pure React + TypeScript linting, and
|
||||
[Biome](https://biomejs.dev/) for a combination of fast formatting, linting, and
|
||||
import sorting of JavaScript and TypeScript code
|
||||
- [clippy](https://github.com/rust-lang/rust-clippy) and
|
||||
[rustfmt](https://github.com/rust-lang/rustfmt) for Rust code
|
||||
- GitHub Actions to check code formatting and linting for both TypeScript and Rust
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Running development server and use Tauri window
|
||||
|
||||
After cloning for the first time, set up git pre-commit hooks:
|
||||
|
||||
```shell
|
||||
pnpm prepare
|
||||
```
|
||||
|
||||
To develop and run the frontend in a Tauri window:
|
||||
|
||||
```shell
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
This will load the Next.js frontend directly in a Tauri webview window, in addition to
|
||||
starting a development server on `localhost:3000`.
|
||||
|
||||
### Building for release
|
||||
|
||||
To export the Next.js frontend via SSG and build the Tauri application for release:
|
||||
|
||||
```shell
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Please remember to change the bundle identifier in
|
||||
`tauri.conf.json > tauri > bundle > identifier`, as the default value will yield an
|
||||
error that prevents you from building the application for release.
|
||||
|
||||
### Source structure
|
||||
|
||||
Next.js frontend source files are located in `src/` and Tauri Rust application source
|
||||
files are located in `src-tauri/`. Please consult the Next.js and Tauri documentation
|
||||
respectively for questions pertaining to either technology.
|
||||
|
||||
## Caveats
|
||||
|
||||
### Static Site Generation / Pre-rendering
|
||||
|
||||
Next.js is a great React frontend framework which supports server-side rendering (SSR)
|
||||
as well as static site generation (SSG or pre-rendering). For the purposes of creating a
|
||||
Tauri frontend, only SSG can be used since SSR requires an active Node.js server.
|
||||
|
||||
Using Next.js and SSG helps to provide a quick and performant single-page-application
|
||||
(SPA) frontend experience. More information regarding this can be found here:
|
||||
https://nextjs.org/docs/basic-features/pages#pre-rendering
|
||||
|
||||
### `next/image`
|
||||
|
||||
The [`next/image` component](https://nextjs.org/docs/basic-features/image-optimization)
|
||||
is an enhancement over the regular `<img>` HTML element with additional optimizations
|
||||
built in. However, because we are not deploying the frontend onto Vercel directly, some
|
||||
optimizations must be disabled to properly build and export the frontend via SSG.
|
||||
As such, the
|
||||
[`unoptimized` property](https://nextjs.org/docs/api-reference/next/image#unoptimized)
|
||||
is set to true for the `next/image` component in the `next.config.js` configuration.
|
||||
This will allow the image to be served as-is from source, without
|
||||
changes to its quality, size, or format.
|
||||
|
||||
### error[E0554]: `#![feature]` may not be used on the stable release channel
|
||||
|
||||
If you are getting this issue when trying to run `pnpm tauri dev`, it may be that you
|
||||
have a newer version of a Rust dependency that uses an unstable feature.
|
||||
`pnpm tauri build` should still work for production builds, but to get the dev command
|
||||
working, either downgrade the dependency or use Rust nightly via
|
||||
`rustup override set nightly`.
|
||||
|
||||
### ReferenceError: navigator is not defined
|
||||
|
||||
If you are using Tauri's `invoke` function or any OS related Tauri function from within
|
||||
JavaScript, you may encounter this error when importing the function in a global,
|
||||
non-browser context. This is due to the nature of Next.js' dev server effectively
|
||||
running a Node.js server for SSR and hot module replacement (HMR), and Node.js does not
|
||||
have a notion of `window` or `navigator`.
|
||||
|
||||
#### Solution 1 - Dependency Injection (may not always work)
|
||||
|
||||
Make sure that you are calling these functions within the browser context, e.g. within a
|
||||
React component inside a `useEffect` hook when the DOM actually exists by then. If you
|
||||
are trying to use a Tauri function in a generalized utility source file, a workaround is
|
||||
to use dependency injection for the function itself to delay the actual importing of the
|
||||
real function (see example below for more info).
|
||||
|
||||
Example using Tauri's `invoke` function:
|
||||
|
||||
`src/lib/some_tauri_functions.ts` (problematic)
|
||||
|
||||
```typescript
|
||||
// Generalized file containing all the invoke functions we need to fetch data from Rust
|
||||
import { invoke } from "@tauri-apps/api/tauri"
|
||||
|
||||
const loadFoo = (): Promise<string> => {
|
||||
return invoke<string>("invoke_handler_foo")
|
||||
}
|
||||
|
||||
const loadBar = (): Promise<string> => {
|
||||
return invoke<string>("invoke_handler_bar")
|
||||
}
|
||||
|
||||
const loadBaz = (): Promise<string> => {
|
||||
return invoke<string>("invoke_handler_baz")
|
||||
}
|
||||
|
||||
// and so on ...
|
||||
```
|
||||
|
||||
`src/lib/some_tauri_functions.ts` (fixed)
|
||||
|
||||
```typescript
|
||||
// Generalized file containing all the invoke functions we need to fetch data from Rust
|
||||
//
|
||||
// We apply the idea of dependency injection to use a supplied invoke function as a
|
||||
// function argument, rather than directly referencing the Tauri invoke function.
|
||||
// Hence, don't import invoke globally in this file.
|
||||
//
|
||||
// import { invoke } from "@tauri-apps/api/tauri" <-- remove this!
|
||||
//
|
||||
|
||||
import { InvokeArgs } from "@tauri-apps/api/tauri"
|
||||
type InvokeFunction = <T>(cmd: string, args?: InvokeArgs | undefined) => Promise<T>
|
||||
|
||||
const loadFoo = (invoke: InvokeFunction): Promise<string> => {
|
||||
return invoke<string>("invoke_handler_foo")
|
||||
}
|
||||
|
||||
const loadBar = (invoke: InvokeFunction): Promise<string> => {
|
||||
return invoke<string>("invoke_handler_bar")
|
||||
}
|
||||
|
||||
const loadBaz = (invoke: InvokeFunction): Promise<string> => {
|
||||
return invoke<string>("invoke_handler_baz")
|
||||
}
|
||||
|
||||
// and so on ...
|
||||
```
|
||||
|
||||
Then, when using `loadFoo`/`loadBar`/`loadBaz` within your React components, import the
|
||||
invoke function from `@tauri-apps/api` and pass `invoke` into the loadXXX function as
|
||||
the `InvokeFunction` argument. This should allow the actual Tauri API to be bundled
|
||||
only within the context of a React component, so it should not be loaded by Next.js upon
|
||||
initial startup until the browser has finished loading the page.
|
||||
|
||||
#### Solution 2: Wrap Tauri API behind dynamic `import()`
|
||||
|
||||
Since the Tauri API needs to read from the browser's `window` and `navigator` object,
|
||||
this data does not exist in a Node.js and hence SSR environment. One can create an
|
||||
exported function that wraps the Tauri API behind a dynamic runtime `import()` call.
|
||||
|
||||
Example: create a `src/lib/tauri.ts` to re-export `invoke`
|
||||
|
||||
```typescript
|
||||
import type { InvokeArgs } from "@tauri-apps/api/tauri"
|
||||
|
||||
const isNode = (): boolean =>
|
||||
Object.prototype.toString.call(typeof process !== "undefined" ? process : 0) ===
|
||||
"[object process]"
|
||||
|
||||
export async function invoke<T>(
|
||||
cmd: string,
|
||||
args?: InvokeArgs | undefined,
|
||||
): Promise<T> {
|
||||
if (isNode()) {
|
||||
// This shouldn't ever happen when React fully loads
|
||||
return Promise.resolve(undefined as unknown as T)
|
||||
}
|
||||
const tauriAppsApi = await import("@tauri-apps/api")
|
||||
const tauriInvoke = tauriAppsApi.invoke
|
||||
return tauriInvoke(cmd, args)
|
||||
}
|
||||
```
|
||||
|
||||
Then, instead of importing `import { invoke } from "@tauri-apps/api/tauri"`, use invoke
|
||||
from `import { invoke } from "@/lib/tauri"`.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and
|
||||
API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
And to learn more about Tauri, take a look at the following resources:
|
||||
|
||||
- [Tauri Documentation - Guides](https://tauri.app/v1/guides/) - learn about the Tauri
|
||||
toolkit.
|
||||
> CS Toolbox
|
||||
|
||||
11
package.json
11
package.json
@@ -24,6 +24,7 @@
|
||||
"@supabase/ssr": "0.6.1",
|
||||
"@tauri-apps/api": "2.4.0",
|
||||
"@tauri-apps/plugin-autostart": "^2.2.0",
|
||||
"@tauri-apps/plugin-cli": "~2",
|
||||
"@tauri-apps/plugin-clipboard-manager": "2.2.2",
|
||||
"@tauri-apps/plugin-deep-link": "~2.2.0",
|
||||
"@tauri-apps/plugin-dialog": "~2.2.0",
|
||||
@@ -35,7 +36,7 @@
|
||||
"@tauri-apps/plugin-process": "2.2.0",
|
||||
"@tauri-apps/plugin-shell": "2.2.0",
|
||||
"@tauri-apps/plugin-store": "^2.2.0",
|
||||
"@tauri-store/valtio": "2.1.0",
|
||||
"@tauri-store/valtio": "2.1.1",
|
||||
"@types/throttle-debounce": "^5.0.2",
|
||||
"ahooks": "^3.8.4",
|
||||
"framer-motion": "^12.5.0",
|
||||
@@ -43,6 +44,8 @@
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"swr": "^2.3.3",
|
||||
"tauri-plugin-system-info-api": "^2.0.10"
|
||||
},
|
||||
@@ -53,11 +56,11 @@
|
||||
"@testing-library/react": "^16.2.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.13.11",
|
||||
"@types/node": "^22.13.13",
|
||||
"@types/react": "19.0.10",
|
||||
"@types/react-dom": "19.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^8.27.0",
|
||||
"@typescript-eslint/parser": "^8.27.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.28.0",
|
||||
"@typescript-eslint/parser": "^8.28.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"clsx": "^2.1.1",
|
||||
"cross-env": "^7.0.3",
|
||||
|
||||
115
src-tauri/Cargo.lock
generated
115
src-tauri/Cargo.lock
generated
@@ -16,6 +16,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-autostart",
|
||||
"tauri-plugin-cli",
|
||||
"tauri-plugin-clipboard-manager",
|
||||
"tauri-plugin-deep-link",
|
||||
"tauri-plugin-dialog",
|
||||
@@ -90,6 +91,56 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.97"
|
||||
@@ -691,6 +742,33 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.4.0"
|
||||
@@ -730,6 +808,12 @@ dependencies = [
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
@@ -2441,6 +2525,12 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
@@ -5075,6 +5165,21 @@ dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-cli"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5458ae16eac81bdbe8d9da2a9f3e01e8cdedbc381cc1727c01127542c8a61c5"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-clipboard-manager"
|
||||
version = "2.2.2"
|
||||
@@ -5326,9 +5431,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-valtio"
|
||||
version = "2.1.0"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0b4f506590b0ce053703e225e780adc8dfae67cdb6e7a60141d4c25e23be0a0"
|
||||
checksum = "9de45344d9278229b43786fc7301c2afd8a05d8232862290e746767a3eb60a2c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"tauri",
|
||||
@@ -6013,6 +6118,12 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.16.0"
|
||||
|
||||
@@ -44,7 +44,7 @@ tauri-plugin-clipboard-manager = "2.2.2"
|
||||
tauri-plugin-shell = "2.2.0"
|
||||
tauri-plugin-http = "2.4.2"
|
||||
tauri-plugin-notification = "2.2.2"
|
||||
tauri-plugin-valtio = "2.1.0"
|
||||
tauri-plugin-valtio = "2.1.1"
|
||||
tauri-plugin-store = "2.2.0"
|
||||
tauri-plugin-system-info = "2.0.9"
|
||||
tauri-plugin-theme = "2.1.3"
|
||||
@@ -64,5 +64,6 @@ default = [ "custom-protocol" ]
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
tauri-plugin-cli = "2"
|
||||
tauri-plugin-global-shortcut = "2.2.0"
|
||||
tauri-plugin-single-instance = "2.2.2"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"deep-link:allow-get-current",
|
||||
"autostart:default",
|
||||
"autostart:allow-enable",
|
||||
"autostart:allow-disable"
|
||||
"autostart:allow-disable",
|
||||
"cli:default"
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"desktop-capability":{"identifier":"desktop-capability","description":"","local":true,"windows":["main"],"permissions":["global-shortcut:default","theme:default","store:default","store:allow-set","store:allow-get-store","store:allow-has","store:allow-delete","store:allow-clear","store:allow-values","store:allow-save","store:allow-load","store:allow-reset","store:allow-entries","deep-link:default","deep-link:allow-register","deep-link:allow-get-current","autostart:default","autostart:allow-enable","autostart:allow-disable"],"platforms":["macOS","windows","linux"]},"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["core:default","fs:allow-read-file","fs:allow-write-file","fs:allow-read-dir","fs:allow-copy-file","fs:allow-mkdir","fs:allow-remove","fs:allow-remove","fs:allow-rename","fs:allow-exists","core:window:allow-create","core:window:allow-center","core:window:allow-request-user-attention","core:window:allow-set-resizable","core:window:allow-set-maximizable","core:window:allow-set-minimizable","core:window:allow-set-closable","core:window:allow-set-title","core:window:allow-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-set-always-on-top","core:window:allow-set-content-protected","core:window:allow-set-size","core:window:allow-set-min-size","core:window:allow-set-max-size","core:window:allow-set-position","core:window:allow-set-fullscreen","core:window:allow-set-focus","core:window:allow-set-icon","core:window:allow-set-skip-taskbar","core:window:allow-set-cursor-grab","core:window:allow-set-cursor-visible","core:window:allow-set-cursor-icon","core:window:allow-set-cursor-position","core:window:allow-set-ignore-cursor-events","core:window:allow-start-dragging","core:webview:allow-print","shell:allow-execute","shell:allow-open","dialog:allow-open","dialog:allow-save","dialog:allow-message","dialog:allow-ask","dialog:allow-confirm","http:default","notification:default","global-shortcut:allow-is-registered","global-shortcut:allow-register","global-shortcut:allow-register-all","global-shortcut:allow-unregister","global-shortcut:allow-unregister-all","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","process:allow-restart","process:allow-exit","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","core:app:allow-app-show","core:app:allow-app-hide","core:app:allow-set-app-theme","process:default","fs:default","dialog:default","os:default","clipboard-manager:default"]},"system-info":{"identifier":"system-info","description":"","local":true,"windows":["*"],"permissions":["system-info:allow-all"]},"valtio":{"identifier":"valtio","description":"","local":true,"windows":["*"],"permissions":["valtio:default","core:event:default"]}}
|
||||
{"desktop-capability":{"identifier":"desktop-capability","description":"","local":true,"windows":["main"],"permissions":["global-shortcut:default","theme:default","store:default","store:allow-set","store:allow-get-store","store:allow-has","store:allow-delete","store:allow-clear","store:allow-values","store:allow-save","store:allow-load","store:allow-reset","store:allow-entries","deep-link:default","deep-link:allow-register","deep-link:allow-get-current","autostart:default","autostart:allow-enable","autostart:allow-disable","cli:default"],"platforms":["macOS","windows","linux"]},"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["core:default","fs:allow-read-file","fs:allow-write-file","fs:allow-read-dir","fs:allow-copy-file","fs:allow-mkdir","fs:allow-remove","fs:allow-remove","fs:allow-rename","fs:allow-exists","core:window:allow-create","core:window:allow-center","core:window:allow-request-user-attention","core:window:allow-set-resizable","core:window:allow-set-maximizable","core:window:allow-set-minimizable","core:window:allow-set-closable","core:window:allow-set-title","core:window:allow-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-set-always-on-top","core:window:allow-set-content-protected","core:window:allow-set-size","core:window:allow-set-min-size","core:window:allow-set-max-size","core:window:allow-set-position","core:window:allow-set-fullscreen","core:window:allow-set-focus","core:window:allow-set-icon","core:window:allow-set-skip-taskbar","core:window:allow-set-cursor-grab","core:window:allow-set-cursor-visible","core:window:allow-set-cursor-icon","core:window:allow-set-cursor-position","core:window:allow-set-ignore-cursor-events","core:window:allow-start-dragging","core:webview:allow-print","shell:allow-execute","shell:allow-open","dialog:allow-open","dialog:allow-save","dialog:allow-message","dialog:allow-ask","dialog:allow-confirm","http:default","notification:default","global-shortcut:allow-is-registered","global-shortcut:allow-register","global-shortcut:allow-register-all","global-shortcut:allow-unregister","global-shortcut:allow-unregister-all","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","process:allow-restart","process:allow-exit","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","core:app:allow-app-show","core:app:allow-app-hide","core:app:allow-set-app-theme","process:default","fs:default","dialog:default","os:default","clipboard-manager:default"]},"system-info":{"identifier":"system-info","description":"","local":true,"windows":["*"],"permissions":["system-info:allow-all"]},"valtio":{"identifier":"valtio","description":"","local":true,"windows":["*"],"permissions":["valtio:default","core:event:default"]}}
|
||||
@@ -2039,6 +2039,21 @@
|
||||
"type": "string",
|
||||
"const": "autostart:deny-is-enabled"
|
||||
},
|
||||
{
|
||||
"description": "Allows reading the CLI matches",
|
||||
"type": "string",
|
||||
"const": "cli:default"
|
||||
},
|
||||
{
|
||||
"description": "Enables the cli_matches command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "cli:allow-cli-matches"
|
||||
},
|
||||
{
|
||||
"description": "Denies the cli_matches command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "cli:deny-cli-matches"
|
||||
},
|
||||
{
|
||||
"description": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n",
|
||||
"type": "string",
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
],
|
||||
"definitions": {
|
||||
"Capability": {
|
||||
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
|
||||
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"identifier",
|
||||
@@ -70,14 +70,14 @@
|
||||
"type": "boolean"
|
||||
},
|
||||
"windows": {
|
||||
"description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`",
|
||||
"description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"webviews": {
|
||||
"description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThis is only required when using on multiwebview contexts, by default all child webviews of a window that matches [`Self::windows`] are linked.\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`",
|
||||
"description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
@@ -2004,6 +2004,41 @@
|
||||
"Identifier": {
|
||||
"description": "Permission identifier",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "This permission set configures if your\napplication can enable or disable auto\nstarting the application on boot.\n\n#### Granted Permissions\n\nIt allows all to check, enable and\ndisable the automatic start on boot.\n\n",
|
||||
"type": "string",
|
||||
"const": "autostart:default"
|
||||
},
|
||||
{
|
||||
"description": "Enables the disable command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "autostart:allow-disable"
|
||||
},
|
||||
{
|
||||
"description": "Enables the enable command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "autostart:allow-enable"
|
||||
},
|
||||
{
|
||||
"description": "Enables the is_enabled command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "autostart:allow-is-enabled"
|
||||
},
|
||||
{
|
||||
"description": "Denies the disable command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "autostart:deny-disable"
|
||||
},
|
||||
{
|
||||
"description": "Denies the enable command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "autostart:deny-enable"
|
||||
},
|
||||
{
|
||||
"description": "Denies the is_enabled command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "autostart:deny-is-enabled"
|
||||
},
|
||||
{
|
||||
"description": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n",
|
||||
"type": "string",
|
||||
@@ -2094,11 +2129,26 @@
|
||||
"type": "string",
|
||||
"const": "core:app:allow-default-window-icon"
|
||||
},
|
||||
{
|
||||
"description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:app:allow-fetch-data-store-identifiers"
|
||||
},
|
||||
{
|
||||
"description": "Enables the identifier command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:app:allow-identifier"
|
||||
},
|
||||
{
|
||||
"description": "Enables the name command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:app:allow-name"
|
||||
},
|
||||
{
|
||||
"description": "Enables the remove_data_store command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:app:allow-remove-data-store"
|
||||
},
|
||||
{
|
||||
"description": "Enables the set_app_theme command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -2129,11 +2179,26 @@
|
||||
"type": "string",
|
||||
"const": "core:app:deny-default-window-icon"
|
||||
},
|
||||
{
|
||||
"description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:app:deny-fetch-data-store-identifiers"
|
||||
},
|
||||
{
|
||||
"description": "Denies the identifier command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:app:deny-identifier"
|
||||
},
|
||||
{
|
||||
"description": "Denies the name command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:app:deny-name"
|
||||
},
|
||||
{
|
||||
"description": "Denies the remove_data_store command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:app:deny-remove-data-store"
|
||||
},
|
||||
{
|
||||
"description": "Denies the set_app_theme command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -2929,6 +2994,11 @@
|
||||
"type": "string",
|
||||
"const": "core:window:allow-internal-toggle-maximize"
|
||||
},
|
||||
{
|
||||
"description": "Enables the is_always_on_top command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:window:allow-is-always-on-top"
|
||||
},
|
||||
{
|
||||
"description": "Enables the is_closable command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -3294,6 +3364,11 @@
|
||||
"type": "string",
|
||||
"const": "core:window:deny-internal-toggle-maximize"
|
||||
},
|
||||
{
|
||||
"description": "Denies the is_always_on_top command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "core:window:deny-is-always-on-top"
|
||||
},
|
||||
{
|
||||
"description": "Denies the is_closable command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -3599,6 +3674,51 @@
|
||||
"type": "string",
|
||||
"const": "core:window:deny-unminimize"
|
||||
},
|
||||
{
|
||||
"description": "Allows reading the opened deep link via the get_current command",
|
||||
"type": "string",
|
||||
"const": "deep-link:default"
|
||||
},
|
||||
{
|
||||
"description": "Enables the get_current command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deep-link:allow-get-current"
|
||||
},
|
||||
{
|
||||
"description": "Enables the is_registered command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deep-link:allow-is-registered"
|
||||
},
|
||||
{
|
||||
"description": "Enables the register command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deep-link:allow-register"
|
||||
},
|
||||
{
|
||||
"description": "Enables the unregister command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deep-link:allow-unregister"
|
||||
},
|
||||
{
|
||||
"description": "Denies the get_current command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deep-link:deny-get-current"
|
||||
},
|
||||
{
|
||||
"description": "Denies the is_registered command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deep-link:deny-is-registered"
|
||||
},
|
||||
{
|
||||
"description": "Denies the register command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deep-link:deny-register"
|
||||
},
|
||||
{
|
||||
"description": "Denies the unregister command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deep-link:deny-unregister"
|
||||
},
|
||||
{
|
||||
"description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n",
|
||||
"type": "string",
|
||||
@@ -5969,6 +6089,11 @@
|
||||
"type": "string",
|
||||
"const": "valtio:allow-get-save-strategy"
|
||||
},
|
||||
{
|
||||
"description": "Enables the get_store_collection_path command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "valtio:allow-get-store-collection-path"
|
||||
},
|
||||
{
|
||||
"description": "Enables the get_store_ids command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -5984,11 +6109,6 @@
|
||||
"type": "string",
|
||||
"const": "valtio:allow-get-store-state"
|
||||
},
|
||||
{
|
||||
"description": "Enables the get_valtio_path command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "valtio:allow-get-valtio-path"
|
||||
},
|
||||
{
|
||||
"description": "Enables the load command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -6039,16 +6159,16 @@
|
||||
"type": "string",
|
||||
"const": "valtio:allow-set-save-strategy"
|
||||
},
|
||||
{
|
||||
"description": "Enables the set_store_collection_path command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "valtio:allow-set-store-collection-path"
|
||||
},
|
||||
{
|
||||
"description": "Enables the set_store_options command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "valtio:allow-set-store-options"
|
||||
},
|
||||
{
|
||||
"description": "Enables the set_valtio_path command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "valtio:allow-set-valtio-path"
|
||||
},
|
||||
{
|
||||
"description": "Enables the unload command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -6069,6 +6189,11 @@
|
||||
"type": "string",
|
||||
"const": "valtio:deny-get-save-strategy"
|
||||
},
|
||||
{
|
||||
"description": "Denies the get_store_collection_path command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "valtio:deny-get-store-collection-path"
|
||||
},
|
||||
{
|
||||
"description": "Denies the get_store_ids command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -6084,11 +6209,6 @@
|
||||
"type": "string",
|
||||
"const": "valtio:deny-get-store-state"
|
||||
},
|
||||
{
|
||||
"description": "Denies the get_valtio_path command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "valtio:deny-get-valtio-path"
|
||||
},
|
||||
{
|
||||
"description": "Denies the load command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -6139,16 +6259,16 @@
|
||||
"type": "string",
|
||||
"const": "valtio:deny-set-save-strategy"
|
||||
},
|
||||
{
|
||||
"description": "Denies the set_store_collection_path command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "valtio:deny-set-store-collection-path"
|
||||
},
|
||||
{
|
||||
"description": "Denies the set_store_options command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "valtio:deny-set-store-options"
|
||||
},
|
||||
{
|
||||
"description": "Denies the set_valtio_path command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "valtio:deny-set-valtio-path"
|
||||
},
|
||||
{
|
||||
"description": "Denies the unload command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
|
||||
@@ -2039,6 +2039,21 @@
|
||||
"type": "string",
|
||||
"const": "autostart:deny-is-enabled"
|
||||
},
|
||||
{
|
||||
"description": "Allows reading the CLI matches",
|
||||
"type": "string",
|
||||
"const": "cli:default"
|
||||
},
|
||||
{
|
||||
"description": "Enables the cli_matches command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "cli:allow-cli-matches"
|
||||
},
|
||||
{
|
||||
"description": "Denies the cli_matches command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "cli:deny-cli-matches"
|
||||
},
|
||||
{
|
||||
"description": "No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n",
|
||||
"type": "string",
|
||||
|
||||
@@ -54,6 +54,9 @@ pub fn get_powerplan() -> Result<i32, String> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let powerplan = powerplan::get_powerplan()?;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let powerplan = powerplan::PowerPlanMode::Other.into();
|
||||
|
||||
Ok(powerplan)
|
||||
}
|
||||
|
||||
@@ -67,7 +70,7 @@ pub fn set_powerplan(plan: i32) -> Result<(), String> {
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_steam_users(steam_dir: &str) -> Result<Vec<preset::User>, String> {
|
||||
wrap_err!(preset::get_users(steam_dir))
|
||||
wrap_err!(preset::get_users(steam_dir))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
)]
|
||||
|
||||
use tauri::Manager;
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_cli::CliExt;
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
use tauri_plugin_store::StoreExt;
|
||||
|
||||
// Window Vibrancy
|
||||
#[cfg(target_os = "windows")]
|
||||
use window_vibrancy::apply_mica;
|
||||
use window_vibrancy::apply_acrylic;
|
||||
#[cfg(target_os = "macos")]
|
||||
use window_vibrancy::{apply_vibrancy, NSVisualEffectMaterial};
|
||||
|
||||
@@ -50,7 +52,10 @@ fn main() {
|
||||
.plugin(tauri_plugin_store::Builder::new().build())
|
||||
.plugin(tauri_plugin_theme::init(ctx.config_mut()))
|
||||
.plugin(tauri_plugin_notification::init())
|
||||
.plugin(tauri_plugin_autostart::init(MacosLauncher::LaunchAgent, Some(vec![]) /* arbitrary number of args to pass to your app */))
|
||||
.plugin(tauri_plugin_autostart::init(
|
||||
MacosLauncher::LaunchAgent,
|
||||
Some(vec!["hidden"]), /* arbitrary number of args to pass to your app */
|
||||
))
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
|
||||
@@ -60,9 +65,29 @@ fn main() {
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_system_info::init())
|
||||
.plugin(tauri_plugin_cli::init())
|
||||
// .plugin(tauri_plugin_store::Builder::default().build())
|
||||
// .plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.setup(|app| {
|
||||
// Get Window
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
|
||||
let store = app.store("cstb.json")?;
|
||||
// 获取boolean类型的hidden值,Err时设置为False
|
||||
let hidden: bool = store.get("hidden").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||
|
||||
// Vibrant Window
|
||||
#[cfg(target_os = "macos")]
|
||||
apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, Some(10.0))
|
||||
.expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS");
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
apply_acrylic(&window, None)
|
||||
.expect("Unsupported platform! 'apply_acrylic' is only supported on Windows");
|
||||
|
||||
// apply_blur(&window, Some((18, 18, 18, 0)))
|
||||
// .expect("Unsupported platform! 'apply_blur' is only supported on Windows");
|
||||
|
||||
// Deep Link
|
||||
#[cfg(desktop)]
|
||||
app.deep_link().register("cstb")?;
|
||||
@@ -74,20 +99,22 @@ fn main() {
|
||||
tray::create_tray(handle)?;
|
||||
}
|
||||
|
||||
// Get Window
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
// CLI
|
||||
match app.cli().matches() {
|
||||
// `matches` here is a Struct with { args, subcommand }.
|
||||
// `args` is `HashMap<String, ArgData>` where `ArgData` is a struct with { value, occurrences }.
|
||||
// `subcommand` is `Option<Box<SubcommandMatches>>` where `SubcommandMatches` is a struct with { name, matches }.
|
||||
Ok(matches) => {
|
||||
println!("{:?}", matches);
|
||||
if matches.args.contains_key("hidden") && matches.args["hidden"].value == true && hidden {
|
||||
window.hide().unwrap();
|
||||
} else {
|
||||
window.show().unwrap();
|
||||
}
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
// Vibrant Window
|
||||
#[cfg(target_os = "macos")]
|
||||
apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, Some(10.0))
|
||||
.expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS");
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
apply_mica(&window, Some(false))
|
||||
.expect("Unsupported platform! 'apply_mica' is only supported on Windows");
|
||||
|
||||
// apply_blur(&window, Some((18, 18, 18, 0)))
|
||||
// .expect("Unsupported platform! 'apply_blur' is only supported on Windows");
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
@@ -100,7 +127,7 @@ fn main() {
|
||||
cmds::open_path,
|
||||
cmds::get_powerplan,
|
||||
cmds::set_powerplan,
|
||||
cmds::get_steam_users,
|
||||
cmds::get_steam_users,
|
||||
cmds::set_auto_login_user,
|
||||
cmds::check_path,
|
||||
on_button_clicked
|
||||
|
||||
@@ -21,7 +21,6 @@ pub fn run_steam() -> std::io::Result<std::process::Output> {
|
||||
.output()
|
||||
}
|
||||
|
||||
|
||||
pub fn get_exe_path(name: &str) -> Result<String, std::io::Error> {
|
||||
// [原理]
|
||||
// Powershell 运行 Get-Process name | Select-Object path
|
||||
|
||||
@@ -42,4 +42,4 @@ macro_rules! wrap_err {
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
pub mod common;
|
||||
pub mod macros;
|
||||
pub mod powerplan;
|
||||
pub mod powerplan;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
use std::process::Command;
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::process::CommandExt;
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
// const DETACHED_PROCESS: u32 = 0x00000008;
|
||||
|
||||
|
||||
@@ -1,12 +1,125 @@
|
||||
use tauri::{
|
||||
menu::{Menu, MenuItem},
|
||||
menu::{CheckMenuItem, Menu, MenuItem, PredefinedMenuItem},
|
||||
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
||||
Manager, Runtime,
|
||||
Emitter, Listener, Manager, Runtime,
|
||||
};
|
||||
|
||||
use crate::tool::powerplan::PowerPlanMode;
|
||||
|
||||
pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
|
||||
let quit_i = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
|
||||
let menu = Menu::with_items(app, &[&quit_i])?;
|
||||
// 托盘菜单项目
|
||||
let separator = &PredefinedMenuItem::separator(app).unwrap();
|
||||
|
||||
let show_i = &MenuItem::with_id(app, "show", "显示主界面", true, None::<&str>)?;
|
||||
let quit_i = &MenuItem::with_id(app, "quit", "退出", true, None::<&str>)?;
|
||||
|
||||
let kill_game_i = &MenuItem::with_id(app, "kill_game", "关闭CS2", true, None::<&str>)?;
|
||||
let kill_steam_i = &MenuItem::with_id(app, "kill_steam", "关闭Steam", true, None::<&str>)?;
|
||||
|
||||
let launch_ww_i = &MenuItem::with_id(app, "launch_ww", "启动国际服", true, None::<&str>)?;
|
||||
let launch_pw_i = &MenuItem::with_id(app, "launch_pw", "启动国服", true, None::<&str>)?;
|
||||
|
||||
let power_plan_extreme = CheckMenuItem::with_id(
|
||||
app,
|
||||
"power_plan_extreme",
|
||||
"卓越性能",
|
||||
true,
|
||||
false,
|
||||
None::<&str>,
|
||||
)?;
|
||||
let power_plan_high =
|
||||
CheckMenuItem::with_id(app, "power_plan_high", "高性能", true, false, None::<&str>)?;
|
||||
let power_plan_balanced = CheckMenuItem::with_id(
|
||||
app,
|
||||
"power_plan_balanced",
|
||||
"平衡",
|
||||
true,
|
||||
false,
|
||||
None::<&str>,
|
||||
)?;
|
||||
let power_plan_powersave = CheckMenuItem::with_id(
|
||||
app,
|
||||
"power_plan_powersave",
|
||||
"节能",
|
||||
true,
|
||||
false,
|
||||
None::<&str>,
|
||||
)?;
|
||||
|
||||
|
||||
let current_launch_option = MenuItem::with_id(
|
||||
app,
|
||||
"current_launch_option",
|
||||
"启动项档位",
|
||||
true,
|
||||
None::<&str>,
|
||||
)?;
|
||||
|
||||
// 创建托盘菜单
|
||||
let menu = Menu::with_items(
|
||||
app,
|
||||
&[
|
||||
&power_plan_extreme,
|
||||
&power_plan_high,
|
||||
&power_plan_balanced,
|
||||
&power_plan_powersave,
|
||||
separator,
|
||||
¤t_launch_option,
|
||||
launch_ww_i,
|
||||
launch_pw_i,
|
||||
separator,
|
||||
kill_game_i,
|
||||
kill_steam_i,
|
||||
separator,
|
||||
show_i,
|
||||
quit_i,
|
||||
],
|
||||
)?;
|
||||
|
||||
let _ = app.listen("tray://get_powerplan", move |event| {
|
||||
if let Ok(payload) = event.payload().parse::<i32>() {
|
||||
match payload {
|
||||
x if x == PowerPlanMode::Other as i32 => {
|
||||
let _ = power_plan_powersave.set_checked(false);
|
||||
let _ = power_plan_balanced.set_checked(false);
|
||||
let _ = power_plan_high.set_checked(false);
|
||||
let _ = power_plan_extreme.set_checked(false);
|
||||
}
|
||||
x if x == PowerPlanMode::PowerSaving as i32 => {
|
||||
let _ = power_plan_powersave.set_checked(true);
|
||||
let _ = power_plan_balanced.set_checked(false);
|
||||
let _ = power_plan_high.set_checked(false);
|
||||
let _ = power_plan_extreme.set_checked(false);
|
||||
}
|
||||
x if x == PowerPlanMode::Balanced as i32 => {
|
||||
let _ = power_plan_powersave.set_checked(false);
|
||||
let _ = power_plan_balanced.set_checked(true);
|
||||
let _ = power_plan_high.set_checked(false);
|
||||
let _ = power_plan_extreme.set_checked(false);
|
||||
}
|
||||
x if x == PowerPlanMode::HighPerformance as i32 => {
|
||||
let _ = power_plan_powersave.set_checked(false);
|
||||
let _ = power_plan_balanced.set_checked(false);
|
||||
let _ = power_plan_high.set_checked(true);
|
||||
let _ = power_plan_extreme.set_checked(false);
|
||||
}
|
||||
x if x == PowerPlanMode::Extreme as i32 => {
|
||||
let _ = power_plan_powersave.set_checked(false);
|
||||
let _ = power_plan_balanced.set_checked(false);
|
||||
let _ = power_plan_high.set_checked(false);
|
||||
let _ = power_plan_extreme.set_checked(true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let _ = app.listen("tray://get_current_launch_option", move |event| {
|
||||
let payload = event.payload();
|
||||
if payload != "" {
|
||||
let _ = current_launch_option.set_text("启动项档位 ".to_string() + payload);
|
||||
}
|
||||
});
|
||||
|
||||
let _ = TrayIconBuilder::with_id("tray")
|
||||
.icon(app.default_window_icon().unwrap().clone())
|
||||
@@ -16,7 +129,43 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
|
||||
"quit" => {
|
||||
app.exit(0);
|
||||
}
|
||||
// Add more events here
|
||||
"show" => {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
let _ = window.show();
|
||||
let _ = window.set_focus();
|
||||
}
|
||||
}
|
||||
"launch_ww" => {
|
||||
let _ = app.emit("tray://launch_game", "worldwide");
|
||||
}
|
||||
"launch_pw" => {
|
||||
let _ = app.emit("tray://launch_game", "perfectworld");
|
||||
}
|
||||
"kill_game" => {
|
||||
let _ = app.emit("tray://kill_game", None::<()>);
|
||||
}
|
||||
"kill_steam" => {
|
||||
let _ = app.emit("tray://kill_steam", None::<()>);
|
||||
}
|
||||
"power_plan_extreme" => {
|
||||
let _ = app.emit("tray://set_powerplan", PowerPlanMode::Extreme as i32);
|
||||
// let _ = power_plan_extreme.set_checked(true);
|
||||
}
|
||||
"power_plan_high" => {
|
||||
let _ = app.emit(
|
||||
"tray://set_powerplan",
|
||||
PowerPlanMode::HighPerformance as i32,
|
||||
);
|
||||
// let _ = power_plan_high.set_checked(true);
|
||||
}
|
||||
"power_plan_balanced" => {
|
||||
let _ = app.emit("tray://set_powerplan", PowerPlanMode::Balanced as i32);
|
||||
// let _ = power_plan_balanced.set_checked(true);
|
||||
}
|
||||
"power_plan_powersave" => {
|
||||
let _ = app.emit("tray://set_powerplan", PowerPlanMode::PowerSaving as i32);
|
||||
// let _ = power_plan_powersave.set_checked(true);
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
.on_tray_icon_event(|tray, event| {
|
||||
@@ -37,68 +186,3 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Tray Menu
|
||||
// let quit = CustomMenuItem::new("quit".to_string(), "Quit");
|
||||
// let hide = CustomMenuItem::new("hide".to_string(), "Hide");
|
||||
// let tray_menu = SystemTrayMenu::new() // insert the menu items here
|
||||
// .add_item(hide)
|
||||
// .add_item(quit);
|
||||
// .add_native_item(SystemTrayMenuItem::Separator)
|
||||
// let toggle = MenuItemBuilder::with_id("toggle", "Toggle").build(app)?;
|
||||
// let menu = MenuBuilder::new(app).items(&[&toggle]).build()?;
|
||||
|
||||
// Setup Tray
|
||||
// let tray = tauri::tray::TrayIconBuilder::with_id("my-tray").build(app)?;
|
||||
// let _ = TrayIconBuilder::new()
|
||||
// .menu(&menu)
|
||||
// .on_menu_event(move |_, event| {
|
||||
// match event.id().as_ref() {
|
||||
// "toggle" => {
|
||||
// println!("toggle clicked");
|
||||
// }
|
||||
// _ => (),
|
||||
// }
|
||||
// // match event {
|
||||
// // SystemTrayEvent::LeftClick { position: _, size: _, .. } => {
|
||||
// // let window = app.get_window("main").unwrap();
|
||||
// // window.show().unwrap();
|
||||
// // window.set_focus().unwrap();
|
||||
|
||||
// // // thread::sleep(Duration::from_millis(100));
|
||||
// // // window.set_always_on_top(false).unwrap();
|
||||
// // println!("system tray received a left click");
|
||||
// // }
|
||||
// // SystemTrayEvent::RightClick { position: _, size: _, .. } => {
|
||||
// // // let window = app.get_window("main").unwrap();
|
||||
// // // window.hide().unwrap();
|
||||
// // println!("system tray received a right click");
|
||||
// // }
|
||||
// // SystemTrayEvent::DoubleClick { position: _, size: _, .. } => {
|
||||
// // println!("system tray received a double click");
|
||||
// // }
|
||||
// // SystemTrayEvent::MenuItemClick { id, .. } =>
|
||||
// // match id.as_str() {
|
||||
// // "quit" => {
|
||||
// // std::process::exit(0);
|
||||
// // }
|
||||
// // "hide" => {
|
||||
// // let window = app.get_window("main").unwrap();
|
||||
// // window.hide().unwrap();
|
||||
// // }
|
||||
// // _ => {}
|
||||
// // }
|
||||
// // _ => {}
|
||||
// // }
|
||||
// })
|
||||
// .on_tray_icon_event(|tray, event| {
|
||||
// if event.click_type == ClickType::Left {
|
||||
// let app = tray.app_handle();
|
||||
// if let Some(webview_window) = app.get_webview_window("main") {
|
||||
// let _ = webview_window.show();
|
||||
// let _ = webview_window.set_focus();
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .build(app)
|
||||
// .unwrap();
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
pub mod parse;
|
||||
pub mod preset;
|
||||
pub mod preset;
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
},
|
||||
"productName": "CS工具箱",
|
||||
"mainBinaryName": "cstb",
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.5-beta.4",
|
||||
"identifier": "upup.cool",
|
||||
"plugins": {
|
||||
"deep-link": {
|
||||
@@ -51,6 +51,15 @@
|
||||
"cstb"
|
||||
]
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"description": "CS Toolbox CLI",
|
||||
"args": [
|
||||
{
|
||||
"name": "hidden",
|
||||
"description": "hidden on start"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"app": {
|
||||
@@ -70,7 +79,8 @@
|
||||
"transparent": true,
|
||||
"theme": null,
|
||||
"hiddenTitle": true,
|
||||
"titleBarStyle": "Transparent"
|
||||
"titleBarStyle": "Transparent",
|
||||
"visible": false
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
|
||||
@@ -7,12 +7,13 @@ import LaunchOption from "@/components/cstb/LaunchOption"
|
||||
import Notice from "@/components/cstb/Notice"
|
||||
import PowerPlan from "@/components/cstb/PowerPlan"
|
||||
import SmartTransfer from "@/components/cstb/SmartTranser"
|
||||
|
||||
const Home = () => {
|
||||
return (
|
||||
<section className="flex flex-col h-full gap-4">
|
||||
<div className="flex flex-grow w-full gap-4">
|
||||
<Notice />
|
||||
<SmartTransfer />
|
||||
<SmartTransfer />
|
||||
</div>
|
||||
|
||||
<CommonDir />
|
||||
|
||||
@@ -17,6 +17,21 @@ export default function Page() {
|
||||
>
|
||||
开机自启动 {app.state.autoStart ? "开" : "关"}
|
||||
</Switch>
|
||||
<Switch
|
||||
isSelected={app.state.startHidden}
|
||||
size="sm"
|
||||
onChange={(e) => app.setStartHidden(e.target.checked)}
|
||||
>
|
||||
静默启动 {app.state.startHidden ? "开" : "关"}
|
||||
</Switch>
|
||||
{/* hiddenOnClose */}
|
||||
<Switch
|
||||
isSelected={app.state.hiddenOnClose}
|
||||
size="sm"
|
||||
onChange={(e) => app.setHiddenOnClose(e.target.checked)}
|
||||
>
|
||||
关闭时最小化到托盘 {app.state.hiddenOnClose ? "开" : "关"}
|
||||
</Switch>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,17 +3,11 @@ import {
|
||||
Card,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
CardIcon,
|
||||
CardTool,
|
||||
CardIcon
|
||||
} from "@/components/window/Card"
|
||||
import { ToolButton } from "@/components/window/ToolButton"
|
||||
import { cn } from "@heroui/react"
|
||||
import {
|
||||
AssemblyLine,
|
||||
HardDisk,
|
||||
SettingConfig,
|
||||
UploadOne,
|
||||
Videocamera,
|
||||
AssemblyLine, SettingConfig, Videocamera
|
||||
} from "@icon-park/react"
|
||||
import { usePathname, useRouter } from "next/navigation"
|
||||
// import { platform } from "@tauri-apps/plugin-os"
|
||||
@@ -49,7 +43,8 @@ export default function PreferenceLayout({
|
||||
<Videocamera /> 录像
|
||||
</CardIcon>
|
||||
|
||||
<CardTool>
|
||||
{/* TODO 完善云同步等功能 */}
|
||||
{/* <CardTool>
|
||||
<ToolButton>
|
||||
<UploadOne />
|
||||
云同步
|
||||
@@ -58,7 +53,7 @@ export default function PreferenceLayout({
|
||||
<HardDisk />
|
||||
保存
|
||||
</ToolButton>
|
||||
</CardTool>
|
||||
</CardTool> */}
|
||||
</CardHeader>
|
||||
<CardBody>{children}</CardBody>
|
||||
</Card>
|
||||
|
||||
@@ -1,21 +1,71 @@
|
||||
"use client"
|
||||
import { init } from "@/store"
|
||||
import { useSteamStore } from "@/store/steam"
|
||||
import { useToolStore } from "@/store/tool"
|
||||
import { addToast } from "@heroui/react"
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { listen } from "@tauri-apps/api/event"
|
||||
import { useDebounce } from "ahooks"
|
||||
import { useEffect } from "react"
|
||||
import "./globals.css"
|
||||
import Providers from "./providers"
|
||||
import { init } from "@/store"
|
||||
import { useDebounce } from "ahooks"
|
||||
import { PowerPlans } from "@/components/cstb/PowerPlan"
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
const steam = useSteamStore()
|
||||
const tool = useToolStore()
|
||||
|
||||
useEffect(() => {
|
||||
void init()
|
||||
|
||||
void listen<string>("tray://launch_game", async (event) => {
|
||||
await invoke("launch_game", {
|
||||
steamPath: `${steam.state.steamDir}/steam.exe`,
|
||||
launchOption: tool.state.launchOptions[tool.state.launchIndex].option || "",
|
||||
server: event.payload || "worldwide",
|
||||
})
|
||||
addToast({ title: "启动国服成功" })
|
||||
})
|
||||
|
||||
void listen("tray://kill_steam", async () => {
|
||||
await invoke("kill_steam")
|
||||
addToast({ title: "已关闭Steam" })
|
||||
})
|
||||
|
||||
void listen("tray://kill_game", async () => {
|
||||
await invoke("kill_game")
|
||||
addToast({ title: "已关闭CS2" })
|
||||
})
|
||||
|
||||
void listen<number>("tray://set_powerplan", async (event) => {
|
||||
if (typeof(event.payload) === "number" && event.payload <= 0 && event.payload > 4) return
|
||||
await invoke("set_powerplan", { plan: event.payload })
|
||||
const current = await invoke<number>("get_powerplan")
|
||||
tool.setPowerPlan(current)
|
||||
|
||||
addToast({ title: `电源计划已切换 → ${PowerPlans[current].title}` })
|
||||
})
|
||||
})
|
||||
|
||||
// 检测steam路径和游戏路径是否有效
|
||||
const steam = useSteamStore()
|
||||
const debounceSteamDir = useDebounce(steam.state.steamDir, {wait: 500, leading: true, trailing: true, maxWait: 2500})
|
||||
const debounceCs2Dir = useDebounce(steam.state.cs2Dir, {wait: 500, leading: true, trailing: true, maxWait: 2500})
|
||||
const debounceSteamDirValid = useDebounce(steam.state.steamDirValid, {wait: 500, leading: true, trailing: true, maxWait: 2500})
|
||||
const debounceSteamDir = useDebounce(steam.state.steamDir, {
|
||||
wait: 500,
|
||||
leading: true,
|
||||
trailing: true,
|
||||
maxWait: 2500,
|
||||
})
|
||||
const debounceCs2Dir = useDebounce(steam.state.cs2Dir, {
|
||||
wait: 500,
|
||||
leading: true,
|
||||
trailing: true,
|
||||
maxWait: 2500,
|
||||
})
|
||||
const debounceSteamDirValid = useDebounce(steam.state.steamDirValid, {
|
||||
wait: 500,
|
||||
leading: true,
|
||||
trailing: true,
|
||||
maxWait: 2500,
|
||||
})
|
||||
useEffect(() => {
|
||||
void steam.checkSteamDirValid()
|
||||
}, [debounceSteamDir])
|
||||
|
||||
@@ -23,7 +23,7 @@ const FastLaunch = () => {
|
||||
onPress={() => {
|
||||
void invoke("launch_game", {
|
||||
steamPath: `${steam.state.steamDir}/steam.exe`,
|
||||
launchOption: tool.state.launchOptions[tool.state.launchIndex] || "",
|
||||
launchOption: tool.state.launchOptions[tool.state.launchIndex].option || "",
|
||||
server: "perfectworld",
|
||||
})
|
||||
addToast({ title: "启动国服成功" })
|
||||
@@ -37,7 +37,7 @@ const FastLaunch = () => {
|
||||
onPress={() => {
|
||||
void invoke("launch_game", {
|
||||
steamPath: `${steam.state.steamDir}/steam.exe`,
|
||||
launchOption: tool.state.launchOptions[tool.state.launchIndex] || "",
|
||||
launchOption: tool.state.launchOptions[tool.state.launchIndex].option || "",
|
||||
server: "worldwide",
|
||||
})
|
||||
addToast({ title: "启动国际服成功" })
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Plus, SettingConfig, Switch } from "@icon-park/react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { Card, CardBody, CardHeader, CardIcon, CardTool } from "../window/Card"
|
||||
import { ToolButton } from "../window/ToolButton"
|
||||
import { input, Textarea } from "@heroui/react"
|
||||
import { Tooltip } from "@heroui/react"
|
||||
|
||||
const LaunchOption = () => {
|
||||
const tool = useToolStore()
|
||||
@@ -29,10 +29,12 @@ const LaunchOption = () => {
|
||||
<Plus />
|
||||
添加
|
||||
</ToolButton>
|
||||
<ToolButton>
|
||||
<Switch />
|
||||
切换模式
|
||||
</ToolButton>
|
||||
<Tooltip content="功能测试中,尚未实装" showArrow={true} delay={300}>
|
||||
<ToolButton>
|
||||
<Switch />
|
||||
切换模式
|
||||
</ToolButton>
|
||||
</Tooltip>
|
||||
</CardTool>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
"use client"
|
||||
import {
|
||||
Card,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
CardIcon,
|
||||
CardTool,
|
||||
} from "@/components/window/Card"
|
||||
import { Card, CardBody, CardHeader, CardIcon, CardTool } from "@/components/window/Card"
|
||||
import { useAppStore } from "@/store/app"
|
||||
import { createClient } from "@/utils/supabase/client"
|
||||
import { Skeleton } from "@heroui/react"
|
||||
import { Refresh, VolumeNotice } from "@icon-park/react"
|
||||
import { Link, Skeleton } from "@heroui/react"
|
||||
import { Refresh, VolumeNotice, WebPage } from "@icon-park/react"
|
||||
import useSWR, { useSWRConfig } from "swr"
|
||||
import { ToolButton } from "../window/ToolButton"
|
||||
import { MarkdownRender } from "../markdown"
|
||||
|
||||
const Notice = () => {
|
||||
const { mutate } = useSWRConfig()
|
||||
@@ -23,6 +18,12 @@ const Notice = () => {
|
||||
<VolumeNotice /> 公告
|
||||
</CardIcon>
|
||||
<CardTool>
|
||||
<Link href="https://cstb.upup.cool" target="_blank" className="dark:text-white text-zinc-800" >
|
||||
<ToolButton>
|
||||
<WebPage />
|
||||
官网
|
||||
</ToolButton>
|
||||
</Link>
|
||||
<ToolButton onClick={() => mutate("/api/notice")}>
|
||||
<Refresh />
|
||||
刷新
|
||||
@@ -45,15 +46,13 @@ const NoticeBody = () => {
|
||||
.from("Notice")
|
||||
.select("created_at, content, url")
|
||||
.order("created_at", { ascending: false })
|
||||
.limit(1)
|
||||
.single()
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
const { data: notice /* , error */, isLoading } = useSWR(
|
||||
"/api/notice",
|
||||
noticeFetcher,
|
||||
)
|
||||
const { data: notice /* , error */, isLoading } = useSWR("/api/notice", noticeFetcher)
|
||||
|
||||
// if (error) return <>错误:{error}</>
|
||||
if (isLoading)
|
||||
@@ -64,11 +63,28 @@ const NoticeBody = () => {
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{notice?.content ||
|
||||
app.state.notice ||
|
||||
"不会真的有人要更新CSGO工具箱吧,不会吧不会吧 xswl"}
|
||||
</>
|
||||
<div className="flex flex-col h-full gap-2">
|
||||
<div className="">
|
||||
<MarkdownRender>
|
||||
{notice?.content ||
|
||||
app.state.notice ||
|
||||
"不会真的有人要更新CSGO工具箱吧,不会吧不会吧 xswl"}
|
||||
</MarkdownRender>
|
||||
</div>
|
||||
{/* {notice?.url && (
|
||||
<Button
|
||||
variant="flat"
|
||||
// color="default"
|
||||
as={Link}
|
||||
href={notice?.url}
|
||||
target="_blank"
|
||||
size="sm"
|
||||
className="bg-transparent w-fit"
|
||||
>
|
||||
传送门
|
||||
</Button>
|
||||
)} */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Key } from "@react-types/shared"
|
||||
import { useToolStore } from "@/store/tool"
|
||||
import { useEffect } from "react"
|
||||
|
||||
const PowerPlans = [
|
||||
export const PowerPlans = [
|
||||
{
|
||||
id: "0",
|
||||
title: "其他",
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useSteamStore } from "@/store/steam"
|
||||
import { open } from "@tauri-apps/plugin-dialog"
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { onOpenUrl } from "@tauri-apps/plugin-deep-link"
|
||||
import { useDebounce } from "ahooks"
|
||||
import { useAppStore } from "@/store/app"
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
import { FolderConversion, FolderPlus } from "@icon-park/react"
|
||||
import { Card, CardBody, CardHeader, CardIcon, CardTool } from "../window/Card"
|
||||
import { ToolButton } from "../window/ToolButton"
|
||||
import { Tooltip } from "@heroui/react"
|
||||
|
||||
const SmartTransfer = () => {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardIcon>
|
||||
<FolderConversion /> 智能中转
|
||||
</CardIcon>
|
||||
<CardTool>
|
||||
<ToolButton>
|
||||
<FolderPlus /> 选择文件{" "}
|
||||
</ToolButton>
|
||||
</CardTool>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="flex flex-col items-center justify-center w-full h-full p-5 text-lg font-medium transition rounded-lg cursor-pointer select-none bg-black/5 hover:bg-black/10">
|
||||
<p>点击或拖拽</p>
|
||||
<p>智能中转 .dem .cfg</p>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Tooltip content="功能测试中,尚未实装" showArrow={true} delay={300}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardIcon>
|
||||
<FolderConversion /> 智能中转
|
||||
</CardIcon>
|
||||
<CardTool>
|
||||
<ToolButton>
|
||||
<FolderPlus /> 选择文件{" "}
|
||||
</ToolButton>
|
||||
</CardTool>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="flex flex-col items-center justify-center w-full h-full p-5 text-lg font-medium transition rounded-lg cursor-pointer select-none bg-black/5 hover:bg-black/10">
|
||||
<p>点击或拖拽</p>
|
||||
<p>智能中转 .dem .cfg</p>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CloseSmall, Down, Edit, Plus, SettingConfig, Up } from "@icon-park/reac
|
||||
import { useState } from "react"
|
||||
import { Card, CardBody, CardHeader, CardIcon, CardTool } from "../window/Card"
|
||||
import { ToolButton } from "../window/ToolButton"
|
||||
import { addToast, NumberInput, Tab, Tabs } from "@heroui/react"
|
||||
import { addToast, NumberInput, Tab, Tabs, Tooltip } from "@heroui/react"
|
||||
import { motion } from "framer-motion"
|
||||
import { useToolStore } from "@/store/tool"
|
||||
|
||||
@@ -21,13 +21,14 @@ const VideoSetting = () => {
|
||||
const videoSettings = [
|
||||
{ type: "", title: "全屏", value: "全屏", options: ["窗口", "全屏"] },
|
||||
{ type: "", title: "垂直同步", value: "关闭", options: ["关闭", "开启"] },
|
||||
{ type: "", title: "低延迟模式", value: "关闭", options: ["关闭", "开启"] },
|
||||
{ type: "", title: "增强角色对比度", value: "禁用", options: ["禁用", "启用"] },
|
||||
{ type: "", title: "CMAA2抗锯齿", value: "关闭", options: ["关闭", "开启"] },
|
||||
{
|
||||
type: "",
|
||||
title: "多重采样抗锯齿",
|
||||
value: "2X MSAA",
|
||||
options: ["无", "CMAA2", "2X MSAA", "4X MSAA", "8X MSAA"],
|
||||
options: ["无", "2X MSAA", "4X MSAA", "8X MSAA"],
|
||||
},
|
||||
{ type: "", title: "全局阴影效果", value: "低", options: ["低", "中", "高", "非常高"] },
|
||||
{ type: "", title: "动态阴影", value: "全部", options: ["仅限日光", "全部"] },
|
||||
@@ -51,121 +52,127 @@ const VideoSetting = () => {
|
||||
]
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardIcon>
|
||||
<SettingConfig /> 视频设置
|
||||
</CardIcon>
|
||||
<CardTool>
|
||||
{/* {tool.state.VideoSettings.map((option, index) => (
|
||||
<Tooltip content="功能测试中,尚未实装" showArrow={true} delay={300}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardIcon>
|
||||
<SettingConfig /> 视频设置
|
||||
</CardIcon>
|
||||
<CardTool>
|
||||
{/* {tool.state.VideoSettings.map((option, index) => (
|
||||
<ToolButton key={index} onClick={() => tool.setLaunchIndex(index)}>
|
||||
{index + 1}
|
||||
</ToolButton>
|
||||
))} */}
|
||||
{edit && (
|
||||
<>
|
||||
<ToolButton>低</ToolButton>
|
||||
<ToolButton>中</ToolButton>
|
||||
<ToolButton>高</ToolButton>
|
||||
<ToolButton>非常高</ToolButton>
|
||||
<ToolButton>推荐</ToolButton>
|
||||
<ToolButton
|
||||
onClick={() => {
|
||||
addToast({ title: "测试中 功能完成后可应用设置到游戏" })
|
||||
}}
|
||||
>
|
||||
<Plus />
|
||||
应用
|
||||
</ToolButton>
|
||||
</>
|
||||
)}
|
||||
<ToolButton onClick={() => setEdit(!edit)}>
|
||||
{edit ? (
|
||||
{edit && (
|
||||
<>
|
||||
<CloseSmall />
|
||||
取消编辑
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Edit />
|
||||
编辑
|
||||
<ToolButton>低</ToolButton>
|
||||
<ToolButton>中</ToolButton>
|
||||
<ToolButton>高</ToolButton>
|
||||
<ToolButton>非常高</ToolButton>
|
||||
<ToolButton>推荐</ToolButton>
|
||||
<ToolButton
|
||||
onClick={() => {
|
||||
addToast({ title: "测试中 功能完成后可应用设置到游戏" })
|
||||
}}
|
||||
>
|
||||
<Plus />
|
||||
应用
|
||||
</ToolButton>
|
||||
</>
|
||||
)}
|
||||
</ToolButton>
|
||||
<ToolButton onClick={() => setHide(!hide)}>
|
||||
{hide ? (
|
||||
<>
|
||||
<Up />
|
||||
显示
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Down />
|
||||
隐藏
|
||||
</>
|
||||
)}
|
||||
</ToolButton>
|
||||
</CardTool>
|
||||
</CardHeader>
|
||||
{!hide && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<CardBody>
|
||||
<ul className="flex flex-wrap gap-3 mt-1">
|
||||
<li className="flex flex-col gap-1.5">
|
||||
<span className="ml-2">分辨率</span>
|
||||
<span className="flex gap-1.5">
|
||||
<NumberInput
|
||||
value={tool.state.videoSetting.width}
|
||||
onValueChange={(value) => {
|
||||
tool.setVideoSetting({
|
||||
...tool.state.videoSetting,
|
||||
width: value,
|
||||
})
|
||||
}}
|
||||
radius="full"
|
||||
className="max-w-28"
|
||||
classNames={{ inputWrapper: "h-10" }}
|
||||
/>
|
||||
<NumberInput
|
||||
value={tool.state.videoSetting.height}
|
||||
onValueChange={(value) => {
|
||||
tool.setVideoSetting({
|
||||
...tool.state.videoSetting,
|
||||
height: value,
|
||||
})
|
||||
}}
|
||||
radius="full"
|
||||
className="max-w-28"
|
||||
classNames={{ inputWrapper: "h-10" }}
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
{videoSettings.map((vid, index) => (
|
||||
<li className="flex flex-col gap-1.5" key={index}>
|
||||
<span className="ml-2">{vid.title}</span>
|
||||
<Tabs
|
||||
selectedKey={vid.value}
|
||||
size="md"
|
||||
radius="full"
|
||||
className="min-w-36"
|
||||
fullWidth
|
||||
>
|
||||
{vid.options.map((opt, index) => (
|
||||
<Tab key={opt} title={opt} />
|
||||
))}
|
||||
</Tabs>
|
||||
<ToolButton onClick={() => setEdit(!edit)}>
|
||||
{edit ? (
|
||||
<>
|
||||
<CloseSmall />
|
||||
取消编辑
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Edit />
|
||||
编辑
|
||||
</>
|
||||
)}
|
||||
</ToolButton>
|
||||
<ToolButton onClick={() => setHide(!hide)}>
|
||||
{hide ? (
|
||||
<>
|
||||
<Up />
|
||||
显示
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Down />
|
||||
隐藏
|
||||
</>
|
||||
)}
|
||||
</ToolButton>
|
||||
</CardTool>
|
||||
</CardHeader>
|
||||
{!hide && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<CardBody>
|
||||
<ul className="flex flex-wrap gap-3 mt-1">
|
||||
<li className="flex flex-col gap-1.5">
|
||||
<span className="ml-2">分辨率</span>
|
||||
<span className="flex gap-3">
|
||||
<NumberInput
|
||||
aria-label="width"
|
||||
value={tool.state.videoSetting.width}
|
||||
onValueChange={(value) => {
|
||||
tool.setVideoSetting({
|
||||
...tool.state.videoSetting,
|
||||
width: value,
|
||||
})
|
||||
}}
|
||||
radius="full"
|
||||
step={10}
|
||||
className="max-w-28"
|
||||
classNames={{ inputWrapper: "h-10" }}
|
||||
/>
|
||||
<NumberInput
|
||||
aria-label="height"
|
||||
value={tool.state.videoSetting.height}
|
||||
onValueChange={(value) => {
|
||||
tool.setVideoSetting({
|
||||
...tool.state.videoSetting,
|
||||
height: value,
|
||||
})
|
||||
}}
|
||||
radius="full"
|
||||
step={10}
|
||||
className="max-w-28"
|
||||
classNames={{ inputWrapper: "h-10" }}
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardBody>
|
||||
</motion.div>
|
||||
)}
|
||||
</Card>
|
||||
{videoSettings.map((vid, index) => (
|
||||
<li className="flex flex-col gap-1.5" key={index}>
|
||||
<span className="ml-2">{vid.title}</span>
|
||||
<Tabs
|
||||
selectedKey={vid.value}
|
||||
size="md"
|
||||
radius="full"
|
||||
className="min-w-36"
|
||||
fullWidth
|
||||
>
|
||||
{vid.options.map((opt, _) => (
|
||||
<Tab key={opt} title={opt} />
|
||||
))}
|
||||
</Tabs>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardBody>
|
||||
</motion.div>
|
||||
)}
|
||||
</Card>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
28
src/components/markdown/index.tsx
Normal file
28
src/components/markdown/index.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
// @ts-nocheck
|
||||
import { Code, Link } from '@heroui/react'
|
||||
import Markdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
|
||||
export const components = {
|
||||
a: ({ href, children }: { href: string; children: React.ReactNode }) => (
|
||||
<Link href={href} target="_blank" rel="noopener noreferrer">
|
||||
{children}
|
||||
</Link>
|
||||
),
|
||||
// img: ({ src, alt }: { src: string; alt: string }) => <Image src={src} alt={alt} className="object-cover w-full h-full" />,
|
||||
h1: ({ children }: { children: React.ReactNode }) => <h1 className="text-2xl font-bold mb-2.5">{children}</h1>,
|
||||
h2: ({ children }: { children: React.ReactNode }) => <h2 className="text-xl font-semibold mb-2.5">{children}</h2>,
|
||||
h3: ({ children }: { children: React.ReactNode }) => <h3 className="text-lg font-medium mb-2.5">{children}</h3>,
|
||||
p: ({ children }: { children: React.ReactNode }) => <p className="mb-2.5 text-base">{children}</p>,
|
||||
ul: ({ children }: { children: React.ReactNode }) => <ul className="list-disc pl-6 mb-2.5">{children}</ul>,
|
||||
li: ({ children }: { children: React.ReactNode }) => <li className="mb-2">{children}</li>,
|
||||
code: ({ children }: { children: React.ReactNode }) => <Code size="sm" >{children}</Code>,
|
||||
}
|
||||
|
||||
export function MarkdownRender({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<Markdown remarkPlugins={[remarkGfm]} components={components as any}>
|
||||
{children?.toString()}
|
||||
</Markdown>
|
||||
)
|
||||
}
|
||||
@@ -2,8 +2,17 @@
|
||||
import { setTheme as setTauriTheme } from "@/hooks/tauri/theme"
|
||||
import { useAppStore } from "@/store/app"
|
||||
import { useToolStore } from "@/store/tool"
|
||||
import { addToast, Button, useDisclosure } from "@heroui/react"
|
||||
import { Close, Minus, Moon, Refresh, RocketOne, Square, SunOne } from "@icon-park/react"
|
||||
import { addToast, Button, Link, Tooltip, useDisclosure } from "@heroui/react"
|
||||
import {
|
||||
Close,
|
||||
Communication,
|
||||
Minus,
|
||||
Moon,
|
||||
Refresh,
|
||||
RocketOne,
|
||||
Square,
|
||||
SunOne
|
||||
} from "@icon-park/react"
|
||||
import { type Theme, getCurrentWindow } from "@tauri-apps/api/window"
|
||||
import { /* relaunch, */ exit } from "@tauri-apps/plugin-process"
|
||||
import { useTheme } from "next-themes"
|
||||
@@ -11,6 +20,7 @@ import { usePathname, useRouter } from "next/navigation"
|
||||
import { saveAllNow } from "@tauri-store/valtio"
|
||||
import { useSteamStore } from "@/store/steam"
|
||||
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter } from "@heroui/react"
|
||||
import { window } from "@tauri-apps/api"
|
||||
|
||||
const Nav = () => {
|
||||
const { theme, setTheme } = useTheme()
|
||||
@@ -19,10 +29,12 @@ const Nav = () => {
|
||||
await setTauriTheme(theme)
|
||||
}
|
||||
|
||||
const app = useAppStore()
|
||||
const close = async () => {
|
||||
// (await window.hideOnClose) ? getCurrent().hide() : exit();
|
||||
await saveAllNow()
|
||||
await exit()
|
||||
// await exit()
|
||||
if (app.state.hiddenOnClose) await window.getCurrentWindow().hide()
|
||||
else await exit()
|
||||
}
|
||||
|
||||
const minimize = async () => {
|
||||
@@ -42,30 +54,42 @@ const Nav = () => {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
|
||||
const app = useAppStore()
|
||||
|
||||
return (
|
||||
<nav className="absolute top-0 right-0 flex flex-row h-16 gap-0.5 p-4" data-tauri-drag-region>
|
||||
{pathname !== "/" && (
|
||||
<Tooltip content="启动页确认设置" showArrow={true} delay={300}>
|
||||
{pathname !== "/" && (
|
||||
<button
|
||||
type="button"
|
||||
className="px-2 py-0 transition duration-150 rounded hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
onClick={() => {
|
||||
app.setInited(false)
|
||||
if (pathname !== "/") router.push("/")
|
||||
}}
|
||||
>
|
||||
<RocketOne size={16} />
|
||||
</button>
|
||||
)}
|
||||
</Tooltip>
|
||||
<Tooltip content="深色模式" showArrow={true} delay={300}>
|
||||
<button
|
||||
type="button"
|
||||
className="px-2 py-0 transition duration-150 rounded hover:bg-zinc-200/80 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
onClick={() => {
|
||||
app.setInited(false)
|
||||
if(pathname !== "/") router.push("/")
|
||||
}}
|
||||
className="px-2 py-0 transition duration-150 rounded hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
onClick={() => (theme === "light" ? setAppTheme("dark") : setAppTheme("light"))}
|
||||
>
|
||||
<RocketOne size={16} />
|
||||
{theme === "light" ? <SunOne size={16} /> : <Moon size={16} />}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="px-2 py-0 transition duration-150 rounded hover:bg-zinc-200/80 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
onClick={() => (theme === "light" ? setAppTheme("dark") : setAppTheme("light"))}
|
||||
>
|
||||
{theme === "light" ? <SunOne size={16} /> : <Moon size={16} />}
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip content="反馈" showArrow={true} delay={300}>
|
||||
<Link
|
||||
href="https://docs.qq.com/form/page/DZU1ieW9SQkxWU1RF"
|
||||
target="_blank"
|
||||
className="px-2 py-0 text-black transition duration-150 rounded dark:text-white hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
>
|
||||
<button type="button">
|
||||
<Communication size={16} />
|
||||
</button>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
|
||||
<ResetModal />
|
||||
|
||||
@@ -73,21 +97,21 @@ const Nav = () => {
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className="px-2 py-0 transition duration-150 rounded hover:bg-zinc-200/80 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
className="px-2 py-0 transition duration-150 rounded hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
onClick={minimize}
|
||||
>
|
||||
<Minus size={16} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="px-2 py-0 transition duration-150 rounded hover:bg-zinc-200/80 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
className="px-2 py-0 transition duration-150 rounded hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
onClick={toggleMaximize}
|
||||
>
|
||||
<Square size={16} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="px-2 py-0 transition duration-150 rounded hover:bg-zinc-200/80 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
className="px-2 py-0 transition duration-150 rounded hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
onClick={close}
|
||||
>
|
||||
<Close size={16} />
|
||||
@@ -119,13 +143,15 @@ function ResetModal() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className="px-2 py-0 transition duration-150 rounded hover:bg-zinc-200/80 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
onClick={onOpen}
|
||||
>
|
||||
<Refresh size={16} />
|
||||
</button>
|
||||
<Tooltip content="重置设置" showArrow={true} delay={300}>
|
||||
<button
|
||||
type="button"
|
||||
className="px-2 py-0 transition duration-150 rounded hover:bg-black/10 dark:hover:bg-zinc-100/10 active:scale-95"
|
||||
onClick={onOpen}
|
||||
>
|
||||
<Refresh size={16} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||
<ModalContent>
|
||||
{(onClose) => (
|
||||
|
||||
@@ -55,7 +55,7 @@ const Avatar = () => {
|
||||
<img
|
||||
src={
|
||||
steam.currentUser()?.avatar
|
||||
? `data:image/png;base64,${steam.currentUser()?.avatar || ''}`
|
||||
? `data:image/png;base64,${steam.currentUser()?.avatar || ""}`
|
||||
: "/logo_square.png"
|
||||
}
|
||||
alt="avatar"
|
||||
@@ -72,9 +72,10 @@ const Avatar = () => {
|
||||
const SideBar = () => {
|
||||
const app = useAppStore()
|
||||
|
||||
void getVersion().then((Value) => {
|
||||
app.setVersion(Value)
|
||||
})
|
||||
if (typeof window !== "undefined")
|
||||
void getVersion().then((Value) => {
|
||||
app.setVersion(Value)
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { store } from "@tauri-store/valtio"
|
||||
import { useSnapshot } from "valtio"
|
||||
import { DEFAULT_STORE_CONFIG } from "./config"
|
||||
import { enable, isEnabled, disable } from "@tauri-apps/plugin-autostart"
|
||||
import { enable, disable } from "@tauri-apps/plugin-autostart"
|
||||
import { LazyStore } from '@tauri-apps/plugin-store';
|
||||
|
||||
const defaultValue = {
|
||||
version: "0.0.1",
|
||||
@@ -10,6 +11,8 @@ const defaultValue = {
|
||||
notice: "",
|
||||
useMirror: true,
|
||||
autoStart: false,
|
||||
startHidden: false,
|
||||
hiddenOnClose: false,
|
||||
}
|
||||
|
||||
export const appStore = store("app", { ...defaultValue }, DEFAULT_STORE_CONFIG)
|
||||
@@ -27,10 +30,15 @@ export const useAppStore = () => {
|
||||
setNotice,
|
||||
setUseMirror,
|
||||
setAutoStart,
|
||||
setStartHidden,
|
||||
setHiddenOnClose,
|
||||
resetAppStore,
|
||||
}
|
||||
}
|
||||
|
||||
const launchStore = new LazyStore('cstb.json', { autoSave: true });
|
||||
if (typeof window !== 'undefined') void launchStore.save()
|
||||
|
||||
const setVersion = (version: string) => {
|
||||
appStore.state.version = version
|
||||
}
|
||||
@@ -56,6 +64,17 @@ const setAutoStart = (autoStart: boolean) => {
|
||||
appStore.state.autoStart = autoStart
|
||||
}
|
||||
|
||||
// 同步到 launchStore 使 start hidden 生效
|
||||
const setStartHidden = async (startHidden: boolean) => {
|
||||
appStore.state.startHidden = startHidden;
|
||||
await launchStore.set('hidden', startHidden);
|
||||
await launchStore.save();
|
||||
}
|
||||
|
||||
const setHiddenOnClose = (hiddenOnClose: boolean) => {
|
||||
appStore.state.hiddenOnClose = hiddenOnClose;
|
||||
}
|
||||
|
||||
const resetAppStore = () => {
|
||||
setVersion(defaultValue.version)
|
||||
setHasUpdate(defaultValue.hasUpdate)
|
||||
@@ -63,4 +82,6 @@ const resetAppStore = () => {
|
||||
setNotice(defaultValue.notice)
|
||||
setUseMirror(defaultValue.useMirror)
|
||||
setAutoStart(defaultValue.autoStart)
|
||||
void setStartHidden(defaultValue.startHidden)
|
||||
setHiddenOnClose(defaultValue.hiddenOnClose)
|
||||
}
|
||||
@@ -96,13 +96,11 @@ const currentUser = () => {
|
||||
|
||||
const getUsers = async () => {
|
||||
const users = await invoke<SteamUser[]>("get_steam_users", { steamDir: steamStore.state.steamDir })
|
||||
console.log(users)
|
||||
setUsers(users)
|
||||
}
|
||||
|
||||
const selectUser = (index: number) => {
|
||||
const user = steamStore.state.users.at(index)
|
||||
console.log(index, user)
|
||||
if (user) {
|
||||
setUsers([
|
||||
user,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { store } from "@tauri-store/valtio"
|
||||
import { useSnapshot } from "valtio"
|
||||
import { DEFAULT_STORE_CONFIG } from "./config"
|
||||
import { emit } from "@tauri-apps/api/event"
|
||||
import { send } from "process"
|
||||
|
||||
interface LaunchOption {
|
||||
option: string
|
||||
@@ -50,6 +52,13 @@ export const useToolStore = () => {
|
||||
void toolStore.start
|
||||
const state = useSnapshot(toolStore.state)
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
setTimeout(() => {
|
||||
sendCurrentLaunchOptionToTray(state.launchIndex)
|
||||
sendPowerPlanToTray(state.powerPlan)
|
||||
}, 500)
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
store: toolStore,
|
||||
@@ -77,10 +86,18 @@ const setLaunchOptions = (options: LaunchOption[]) => {
|
||||
|
||||
const setLaunchIndex = (index: number) => {
|
||||
toolStore.state.launchIndex = index
|
||||
sendCurrentLaunchOptionToTray(index)
|
||||
}
|
||||
|
||||
const sendCurrentLaunchOptionToTray = (index: number) => {
|
||||
void emit("tray://get_current_launch_option", toolStore.state.launchOptions[index].name || index + 1)
|
||||
}
|
||||
const setPowerPlan = (plan: number) => {
|
||||
toolStore.state.powerPlan = plan
|
||||
sendPowerPlanToTray(plan)
|
||||
}
|
||||
const sendPowerPlanToTray = (plan: number) => {
|
||||
void emit("tray://get_powerplan", plan)
|
||||
}
|
||||
|
||||
const setVideoSetting = (setting: VideoSetting) => {
|
||||
|
||||
Reference in New Issue
Block a user