|
@ -1,14 +1,14 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# Traymenu icons. Sometimes to wrong size is selected so remove 256 and 64.
|
# Traymenu icons. Sometimes the wrong size is selected, so leave just one.
|
||||||
convert pm_dark_green_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_dark_green.ico
|
convert pm_dark_green_512.png -resize 64x64 pm_dark_green_64.png
|
||||||
convert pm_dark_blue_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_dark_blue.ico
|
convert pm_dark_blue_512.png -resize 64x64 pm_dark_blue_64.png
|
||||||
convert pm_dark_red_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_dark_red.ico
|
convert pm_dark_red_512.png -resize 64x64 pm_dark_red_64.png
|
||||||
convert pm_dark_yellow_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_dark_yellow.ico
|
convert pm_dark_yellow_512.png -resize 64x64 pm_dark_yellow_64.png
|
||||||
convert pm_light_blue_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_light_blue.ico
|
convert pm_light_blue_512.png -resize 64x64 pm_light_blue_64.png
|
||||||
convert pm_light_green_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_light_green.ico
|
convert pm_light_green_512.png -resize 64x64 pm_light_green_64.png
|
||||||
convert pm_light_red_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_light_red.ico
|
convert pm_light_red_512.png -resize 64x64 pm_light_red_64.png
|
||||||
convert pm_light_yellow_512.png -colors 256 -define icon:auto-resize=48,32,16 pm_light_yellow.ico
|
convert pm_light_yellow_512.png -resize 64x64 pm_light_yellow_64.png
|
||||||
|
|
||||||
convert pm_dark_512.png -colors 256 -define icon:auto-resize=64,48,32,16 pm_dark.ico
|
convert pm_dark_512.png -colors 256 -define icon:auto-resize=64,48,32,16 pm_dark.ico
|
||||||
convert pm_light_512.png -colors 256 -define icon:auto-resize=64,48,32,16 pm_light.ico
|
convert pm_light_512.png -colors 256 -define icon:auto-resize=64,48,32,16 pm_light.ico
|
||||||
|
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 66 KiB |
BIN
assets/data/icons/pm_dark_blue.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/data/icons/pm_dark_blue_64.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 66 KiB |
BIN
assets/data/icons/pm_dark_green_64.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 66 KiB |
BIN
assets/data/icons/pm_dark_red.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/data/icons/pm_dark_red_64.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 66 KiB |
BIN
assets/data/icons/pm_dark_yellow.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
assets/data/icons/pm_dark_yellow_64.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 66 KiB |
BIN
assets/data/icons/pm_light_blue.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/data/icons/pm_light_blue_64.png
Normal file
After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 66 KiB |
BIN
assets/data/icons/pm_light_green.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/data/icons/pm_light_green_64.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 66 KiB |
BIN
assets/data/icons/pm_light_red.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/data/icons/pm_light_red_64.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 66 KiB |
BIN
assets/data/icons/pm_light_yellow.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/data/icons/pm_light_yellow_64.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
|
@ -1,7 +1,7 @@
|
||||||
<div class="flex flex-row items-center justify-start text-white w-full gap-2 p-2">
|
<div class="flex flex-row items-center justify-start text-white w-full gap-2 p-2">
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
<div class="relative w-24 h-20 flex-grow-0 flex-shrink-0 block" id="logo">
|
<div class="relative w-24 h-20 flex-grow-0 flex-shrink-0 block" id="logo">
|
||||||
<svg stroke="currentColor" data-name="Layer-1" viewBox="0 0 128 128" class="animate-spin">
|
<svg stroke="currentColor" data-name="Layer-1" viewBox="0 0 128 128">
|
||||||
<g data-name="Main" fill-rule="evenodd">
|
<g data-name="Main" fill-rule="evenodd">
|
||||||
<path shape-rendering="geometricPrecision" fill="#fff"
|
<path shape-rendering="geometricPrecision" fill="#fff"
|
||||||
d="M176.11 36.73l-5-8.61a41.53 41.53 0 00-14.73 57.22l8.55-5.12a31.58 31.58 0 0111.19-43.49z"
|
d="M176.11 36.73l-5-8.61a41.53 41.53 0 00-14.73 57.22l8.55-5.12a31.58 31.58 0 0111.19-43.49z"
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
transform="translate(-127.99 .01)" style="isolation:isolate" opacity=".8"></path>
|
transform="translate(-127.99 .01)" style="isolation:isolate" opacity=".8"></path>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
<svg data-name="layer-2" viewBox="0 0 128 128" class="animate-spin">
|
<svg data-name="layer-2" viewBox="0 0 128 128">
|
||||||
<g data-name="Main" fill-rule="evenodd">
|
<g data-name="Main" fill-rule="evenodd">
|
||||||
<path shape-rendering="geometricPrecision" fill="#fff"
|
<path shape-rendering="geometricPrecision" fill="#fff"
|
||||||
d="M197 83a19.66 19.66 0 01-19.25-32.57l-4.5-4.27A25.87 25.87 0 00198.59 89z"
|
d="M197 83a19.66 19.66 0 01-19.25-32.57l-4.5-4.27A25.87 25.87 0 00198.59 89z"
|
||||||
|
|
831
desktop/tauri/src-tauri/Cargo.lock
generated
|
@ -12,11 +12,11 @@ rust-version = "1.60"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2.0.0-beta.18", features = [] }
|
tauri-build = { version = "2.0.0-beta.19", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Tauri
|
# Tauri
|
||||||
tauri = { version = "2.0.0-beta.23", features = ["tray-icon", "image-png", "config-json5"] }
|
tauri = { version = "2.0.0-beta.24", features = ["tray-icon", "image-png", "config-json5", "devtools"] }
|
||||||
tauri-plugin-shell = "2.0.0-beta"
|
tauri-plugin-shell = "2.0.0-beta"
|
||||||
tauri-plugin-dialog = "2.0.0-beta"
|
tauri-plugin-dialog = "2.0.0-beta"
|
||||||
tauri-plugin-clipboard-manager = "2.0.0-beta"
|
tauri-plugin-clipboard-manager = "2.0.0-beta"
|
||||||
|
@ -25,8 +25,10 @@ tauri-plugin-single-instance = "2.0.0-beta"
|
||||||
tauri-plugin-cli = "2.0.0-beta"
|
tauri-plugin-cli = "2.0.0-beta"
|
||||||
tauri-plugin-notification = "2.0.0-beta"
|
tauri-plugin-notification = "2.0.0-beta"
|
||||||
tauri-plugin-log = "2.0.0-beta"
|
tauri-plugin-log = "2.0.0-beta"
|
||||||
|
tauri-plugin-window-state = "2.0.0-beta"
|
||||||
|
|
||||||
tauri-cli = "2.0.0-beta.21"
|
tauri-cli = "2.0.0-beta.21"
|
||||||
|
clap = { version = "4", features = [ "derive" ] }
|
||||||
|
|
||||||
# General
|
# General
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
@ -52,6 +54,8 @@ reqwest = { version = "0.12" }
|
||||||
rfd = { version = "*", default-features = false, features = [ "tokio", "gtk3", "common-controls-v6" ] }
|
rfd = { version = "*", default-features = false, features = [ "tokio", "gtk3", "common-controls-v6" ] }
|
||||||
open = "5.1.3"
|
open = "5.1.3"
|
||||||
|
|
||||||
|
dark-light = "1.1.1"
|
||||||
|
|
||||||
# Linux only
|
# Linux only
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
glib = "0.18.4"
|
glib = "0.18.4"
|
||||||
|
|
|
@ -21,12 +21,15 @@
|
||||||
"window:allow-show",
|
"window:allow-show",
|
||||||
"window:allow-is-visible",
|
"window:allow-is-visible",
|
||||||
"window:allow-set-focus",
|
"window:allow-set-focus",
|
||||||
|
"window:allow-close",
|
||||||
"app:default",
|
"app:default",
|
||||||
"image:default",
|
"image:default",
|
||||||
"resources:default",
|
"resources:default",
|
||||||
"menu:default",
|
"menu:default",
|
||||||
"tray:default",
|
"tray:default",
|
||||||
"shell:allow-open",
|
"shell:allow-open",
|
||||||
"notification:default"
|
"notification:default",
|
||||||
|
"window-state:allow-save-window-state",
|
||||||
|
"window-state:allow-restore-state"
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -1 +1 @@
|
||||||
{"default":{"identifier":"default","description":"Capability for the main window","remote":{"urls":["http://localhost:817"]},"local":true,"windows":["main","splash"],"permissions":["path:default","event:allow-listen","event:allow-unlisten","event:allow-emit","event:allow-emit-to","window:allow-hide","window:allow-show","window:allow-is-visible","window:allow-set-focus","app:default","image:default","resources:default","menu:default","tray:default","shell:allow-open","notification:default"]}}
|
{"default":{"identifier":"default","description":"Capability for the main window","remote":{"urls":["http://localhost:817"]},"local":true,"windows":["main","splash"],"permissions":["path:default","event:allow-listen","event:allow-unlisten","event:allow-emit","event:allow-emit-to","window:allow-hide","window:allow-show","window:allow-is-visible","window:allow-set-focus","window:allow-close","app:default","image:default","resources:default","menu:default","tray:default","shell:allow-open","notification:default","window-state:allow-save-window-state","window-state:allow-restore-state"]}}
|
|
@ -2286,6 +2286,13 @@
|
||||||
"window:allow-set-title"
|
"window:allow-set-title"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "window:allow-set-title-bar-style -> Enables the set_title_bar_style command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window:allow-set-title-bar-style"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "window:allow-set-visible-on-all-workspaces -> Enables the set_visible_on_all_workspaces command without any pre-configured scope.",
|
"description": "window:allow-set-visible-on-all-workspaces -> Enables the set_visible_on_all_workspaces command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -2727,6 +2734,13 @@
|
||||||
"window:deny-set-title"
|
"window:deny-set-title"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "window:deny-set-title-bar-style -> Denies the set_title_bar_style command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window:deny-set-title-bar-style"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "window:deny-set-visible-on-all-workspaces -> Denies the set_visible_on_all_workspaces command without any pre-configured scope.",
|
"description": "window:deny-set-visible-on-all-workspaces -> Denies the set_visible_on_all_workspaces command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -2789,6 +2803,55 @@
|
||||||
"enum": [
|
"enum": [
|
||||||
"window:deny-unminimize"
|
"window:deny-unminimize"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:default -> This permission set configures what kind of\noperations are available from the window state plugin.\n\n#### Granted Permissions\n\nAll operations are enabled by default.\n\n",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:default"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:allow-filename -> Enables the filename command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:allow-filename"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:allow-restore-state -> Enables the restore_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:allow-restore-state"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:allow-save-window-state -> Enables the save_window_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:allow-save-window-state"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:deny-filename -> Denies the filename command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:deny-filename"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:deny-restore-state -> Denies the restore_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:deny-restore-state"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:deny-save-window-state -> Denies the save_window_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:deny-save-window-state"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -2286,6 +2286,13 @@
|
||||||
"window:allow-set-title"
|
"window:allow-set-title"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "window:allow-set-title-bar-style -> Enables the set_title_bar_style command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window:allow-set-title-bar-style"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "window:allow-set-visible-on-all-workspaces -> Enables the set_visible_on_all_workspaces command without any pre-configured scope.",
|
"description": "window:allow-set-visible-on-all-workspaces -> Enables the set_visible_on_all_workspaces command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -2727,6 +2734,13 @@
|
||||||
"window:deny-set-title"
|
"window:deny-set-title"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "window:deny-set-title-bar-style -> Denies the set_title_bar_style command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window:deny-set-title-bar-style"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "window:deny-set-visible-on-all-workspaces -> Denies the set_visible_on_all_workspaces command without any pre-configured scope.",
|
"description": "window:deny-set-visible-on-all-workspaces -> Denies the set_visible_on_all_workspaces command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -2789,6 +2803,55 @@
|
||||||
"enum": [
|
"enum": [
|
||||||
"window:deny-unminimize"
|
"window:deny-unminimize"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:default -> This permission set configures what kind of\noperations are available from the window state plugin.\n\n#### Granted Permissions\n\nAll operations are enabled by default.\n\n",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:default"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:allow-filename -> Enables the filename command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:allow-filename"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:allow-restore-state -> Enables the restore_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:allow-restore-state"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:allow-save-window-state -> Enables the save_window_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:allow-save-window-state"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:deny-filename -> Denies the filename command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:deny-filename"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:deny-restore-state -> Denies the restore_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:deny-restore-state"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:deny-save-window-state -> Denies the save_window_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:deny-save-window-state"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -2286,6 +2286,13 @@
|
||||||
"window:allow-set-title"
|
"window:allow-set-title"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "window:allow-set-title-bar-style -> Enables the set_title_bar_style command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window:allow-set-title-bar-style"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "window:allow-set-visible-on-all-workspaces -> Enables the set_visible_on_all_workspaces command without any pre-configured scope.",
|
"description": "window:allow-set-visible-on-all-workspaces -> Enables the set_visible_on_all_workspaces command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -2727,6 +2734,13 @@
|
||||||
"window:deny-set-title"
|
"window:deny-set-title"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "window:deny-set-title-bar-style -> Denies the set_title_bar_style command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window:deny-set-title-bar-style"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "window:deny-set-visible-on-all-workspaces -> Denies the set_visible_on_all_workspaces command without any pre-configured scope.",
|
"description": "window:deny-set-visible-on-all-workspaces -> Denies the set_visible_on_all_workspaces command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -2789,6 +2803,55 @@
|
||||||
"enum": [
|
"enum": [
|
||||||
"window:deny-unminimize"
|
"window:deny-unminimize"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:default -> This permission set configures what kind of\noperations are available from the window state plugin.\n\n#### Granted Permissions\n\nAll operations are enabled by default.\n\n",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:default"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:allow-filename -> Enables the filename command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:allow-filename"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:allow-restore-state -> Enables the restore_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:allow-restore-state"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:allow-save-window-state -> Enables the save_window_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:allow-save-window-state"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:deny-filename -> Denies the filename command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:deny-filename"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:deny-restore-state -> Denies the restore_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:deny-restore-state"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "window-state:deny-save-window-state -> Denies the save_window_state command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"window-state:deny-save-window-state"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::{env, path::Path, time::Duration};
|
||||||
|
|
||||||
use tauri::{AppHandle, Manager, RunEvent, WindowEvent};
|
use clap::{command, Parser};
|
||||||
|
use tauri::{AppHandle, Emitter, Listener, Manager, RunEvent, WindowEvent};
|
||||||
use tauri_plugin_cli::CliExt;
|
use tauri_plugin_cli::CliExt;
|
||||||
|
|
||||||
// Library crates
|
// Library crates
|
||||||
|
@ -18,8 +19,9 @@ mod portmaster;
|
||||||
mod traymenu;
|
mod traymenu;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info, LevelFilter};
|
||||||
use portmaster::PortmasterExt;
|
use portmaster::PortmasterExt;
|
||||||
|
use tauri_plugin_log::RotationStrategy;
|
||||||
use traymenu::setup_tray_menu;
|
use traymenu::setup_tray_menu;
|
||||||
use window::{close_splash_window, create_main_window};
|
use window::{close_splash_window, create_main_window};
|
||||||
|
|
||||||
|
@ -28,6 +30,12 @@ extern crate lazy_static;
|
||||||
|
|
||||||
const FALLBACK_TO_OLD_UI_EXIT_CODE: i32 = 77;
|
const FALLBACK_TO_OLD_UI_EXIT_CODE: i32 = 77;
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
const LOG_LEVEL: LevelFilter = LevelFilter::Warn;
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
const LOG_LEVEL: LevelFilter = LevelFilter::Debug;
|
||||||
|
|
||||||
#[derive(Clone, serde::Serialize)]
|
#[derive(Clone, serde::Serialize)]
|
||||||
struct Payload {
|
struct Payload {
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
|
@ -41,6 +49,16 @@ struct WsHandler {
|
||||||
is_first_connect: bool,
|
is_first_connect: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct BasicCli {
|
||||||
|
#[arg(long)]
|
||||||
|
data: Option<String>,
|
||||||
|
|
||||||
|
#[arg(long)]
|
||||||
|
log: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
impl portmaster::Handler for WsHandler {
|
impl portmaster::Handler for WsHandler {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
"main-handler".to_string()
|
"main-handler".to_string()
|
||||||
|
@ -114,16 +132,49 @@ fn main() {
|
||||||
std::process::exit(show_webview_not_installed_dialog());
|
std::process::exit(show_webview_not_installed_dialog());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut target = tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Stdout);
|
||||||
|
|
||||||
|
let cli = BasicCli::parse();
|
||||||
|
if let Some(data_dir) = cli.data {
|
||||||
|
target = tauri_plugin_log::Target::new(tauri_plugin_log::TargetKind::Folder {
|
||||||
|
path: Path::new(&format!("{}/logs/app2", data_dir)).into(),
|
||||||
|
file_name: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut log_level = LOG_LEVEL;
|
||||||
|
if let Some(level) = cli.log {
|
||||||
|
match level.as_str() {
|
||||||
|
"off" => log_level = LevelFilter::Off,
|
||||||
|
"error" => log_level = LevelFilter::Error,
|
||||||
|
"warn" => log_level = LevelFilter::Warn,
|
||||||
|
"info" => log_level = LevelFilter::Info,
|
||||||
|
"debug" => log_level = LevelFilter::Debug,
|
||||||
|
"trace" => log_level = LevelFilter::Trace,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let app = tauri::Builder::default()
|
let app = tauri::Builder::default()
|
||||||
// Shell plugin for open_external support
|
// Shell plugin for open_external support
|
||||||
.plugin(tauri_plugin_shell::init())
|
.plugin(tauri_plugin_shell::init())
|
||||||
.plugin(tauri_plugin_log::Builder::default().build())
|
// Initialize Logging plugin.
|
||||||
|
.plugin(
|
||||||
|
tauri_plugin_log::Builder::default()
|
||||||
|
.level(log_level)
|
||||||
|
.rotation_strategy(RotationStrategy::KeepAll)
|
||||||
|
.clear_targets()
|
||||||
|
.target(target)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
// Clipboard support
|
// Clipboard support
|
||||||
.plugin(tauri_plugin_clipboard_manager::init())
|
.plugin(tauri_plugin_clipboard_manager::init())
|
||||||
// Dialog (Save/Open) support
|
// Dialog (Save/Open) support
|
||||||
.plugin(tauri_plugin_dialog::init())
|
.plugin(tauri_plugin_dialog::init())
|
||||||
// OS Version and Architecture support
|
// OS Version and Architecture support
|
||||||
.plugin(tauri_plugin_os::init())
|
.plugin(tauri_plugin_os::init())
|
||||||
|
// Initialize save windows state plugin.
|
||||||
|
.plugin(tauri_plugin_window_state::Builder::default().build())
|
||||||
// Single instance guard
|
// Single instance guard
|
||||||
.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
|
.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
|
||||||
let _ = app.emit("single-instance", Payload { args: argv, cwd });
|
let _ = app.emit("single-instance", Payload { args: argv, cwd });
|
||||||
|
@ -145,7 +196,6 @@ fn main() {
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
setup_tray_menu(app)?;
|
setup_tray_menu(app)?;
|
||||||
portmaster::setup(app.handle().clone());
|
portmaster::setup(app.handle().clone());
|
||||||
|
|
||||||
// Setup the single-instance event listener that will create/focus the main window
|
// Setup the single-instance event listener that will create/focus the main window
|
||||||
// or the splash-screen.
|
// or the splash-screen.
|
||||||
let handle = app.handle().clone();
|
let handle = app.handle().clone();
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::service::get_service_manager;
|
||||||
use crate::service::ServiceManager;
|
use crate::service::ServiceManager;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use tauri::{Manager, Runtime, State, Window};
|
use tauri::{Emitter, Runtime, State, Window};
|
||||||
|
|
||||||
pub type Result = std::result::Result<String, String>;
|
pub type Result = std::result::Result<String, String>;
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ use std::{
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use serde;
|
use serde;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use tauri::{AppHandle, Manager, Runtime};
|
use tauri::{AppHandle, Emitter, Manager, Runtime};
|
||||||
|
|
||||||
const PORTMASTER_BASE_URL: &'static str = "http://127.0.0.1:817/api/v1/";
|
const PORTMASTER_BASE_URL: &'static str = "http://127.0.0.1:817/api/v1/";
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ pub trait ServiceManager {
|
||||||
fn start(&self) -> Result<StatusResult>;
|
fn start(&self) -> Result<StatusResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
struct EmptyServiceManager();
|
struct EmptyServiceManager();
|
||||||
|
|
||||||
impl ServiceManager for EmptyServiceManager {
|
impl ServiceManager for EmptyServiceManager {
|
||||||
|
|
|
@ -10,6 +10,7 @@ use tauri::{
|
||||||
tray::{TrayIcon, TrayIconBuilder},
|
tray::{TrayIcon, TrayIconBuilder},
|
||||||
Wry,
|
Wry,
|
||||||
};
|
};
|
||||||
|
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
portapi::{
|
portapi::{
|
||||||
|
@ -40,13 +41,56 @@ lazy_static! {
|
||||||
const PM_TRAY_ICON_ID: &'static str = "pm_icon";
|
const PM_TRAY_ICON_ID: &'static str = "pm_icon";
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
//
|
|
||||||
const BLUE_ICON: &'static [u8] = include_bytes!("../../../../assets/data/icons/pm_light_blue.ico");
|
fn get_green_icon() -> &'static [u8] {
|
||||||
const RED_ICON: &'static [u8] = include_bytes!("../../../../assets/data/icons/pm_light_red.ico");
|
const LIGHT_GREEN_ICON: &'static [u8] =
|
||||||
const YELLOW_ICON: &'static [u8] =
|
include_bytes!("../../../../assets/data/icons/pm_light_green_64.png");
|
||||||
include_bytes!("../../../../assets/data/icons/pm_light_yellow.ico");
|
const DARK_GREEN_ICON: &'static [u8] =
|
||||||
const GREEN_ICON: &'static [u8] =
|
include_bytes!("../../../../assets/data/icons/pm_dark_green_64.png");
|
||||||
include_bytes!("../../../../assets/data/icons/pm_light_green.ico");
|
|
||||||
|
let mode = dark_light::detect();
|
||||||
|
match mode {
|
||||||
|
dark_light::Mode::Light => DARK_GREEN_ICON,
|
||||||
|
_ => LIGHT_GREEN_ICON,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_blue_icon() -> &'static [u8] {
|
||||||
|
const LIGHT_BLUE_ICON: &'static [u8] =
|
||||||
|
include_bytes!("../../../../assets/data/icons/pm_light_blue_64.png");
|
||||||
|
const DARK_BLUE_ICON: &'static [u8] =
|
||||||
|
include_bytes!("../../../../assets/data/icons/pm_dark_blue_64.png");
|
||||||
|
let mode = dark_light::detect();
|
||||||
|
match mode {
|
||||||
|
dark_light::Mode::Light => DARK_BLUE_ICON,
|
||||||
|
_ => LIGHT_BLUE_ICON,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_red_icon() -> &'static [u8] {
|
||||||
|
const LIGHT_RED_ICON: &'static [u8] =
|
||||||
|
include_bytes!("../../../../assets/data/icons/pm_light_red_64.png");
|
||||||
|
const DARK_RED_ICON: &'static [u8] =
|
||||||
|
include_bytes!("../../../../assets/data/icons/pm_dark_red_64.png");
|
||||||
|
let mode = dark_light::detect();
|
||||||
|
match mode {
|
||||||
|
dark_light::Mode::Light => DARK_RED_ICON,
|
||||||
|
_ => LIGHT_RED_ICON,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_yellow_icon() -> &'static [u8] {
|
||||||
|
const LIGHT_YELLOW_ICON: &'static [u8] =
|
||||||
|
include_bytes!("../../../../assets/data/icons/pm_light_yellow_64.png");
|
||||||
|
|
||||||
|
const DARK_YELLOW_ICON: &'static [u8] =
|
||||||
|
include_bytes!("../../../../assets/data/icons/pm_dark_yellow_64.png");
|
||||||
|
let mode = dark_light::detect();
|
||||||
|
match mode {
|
||||||
|
dark_light::Mode::Light => DARK_YELLOW_ICON,
|
||||||
|
_ => LIGHT_YELLOW_ICON,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn setup_tray_menu(
|
pub fn setup_tray_menu(
|
||||||
app: &mut tauri::App,
|
app: &mut tauri::App,
|
||||||
|
@ -105,7 +149,7 @@ pub fn setup_tray_menu(
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let icon = TrayIconBuilder::with_id(PM_TRAY_ICON_ID)
|
let icon = TrayIconBuilder::with_id(PM_TRAY_ICON_ID)
|
||||||
.icon(Image::from_bytes(RED_ICON).unwrap())
|
.icon(Image::from_bytes(get_red_icon()).unwrap())
|
||||||
.menu(&menu)
|
.menu(&menu)
|
||||||
.on_menu_event(move |app, event| match event.id().as_ref() {
|
.on_menu_event(move |app, event| match event.id().as_ref() {
|
||||||
"exit_ui" => {
|
"exit_ui" => {
|
||||||
|
@ -205,11 +249,11 @@ pub fn update_icon(icon: AppIcon, subsystems: HashMap<String, Subsystem>, spn_st
|
||||||
}
|
}
|
||||||
|
|
||||||
let next_icon = match failure.0 {
|
let next_icon = match failure.0 {
|
||||||
subsystem::FAILURE_WARNING => YELLOW_ICON,
|
subsystem::FAILURE_WARNING => get_yellow_icon(),
|
||||||
subsystem::FAILURE_ERROR => RED_ICON,
|
subsystem::FAILURE_ERROR => get_red_icon(),
|
||||||
_ => match spn_status.as_str() {
|
_ => match spn_status.as_str() {
|
||||||
"connected" | "connecting" => BLUE_ICON,
|
"connected" | "connecting" => get_blue_icon(),
|
||||||
_ => GREEN_ICON,
|
_ => get_green_icon(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -289,7 +333,7 @@ pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_ = icon.set_icon(Some(Image::from_bytes(BLUE_ICON).unwrap()));
|
_ = icon.set_icon(Some(Image::from_bytes(get_blue_icon()).unwrap()));
|
||||||
|
|
||||||
let mut subsystems: HashMap<String, Subsystem> = HashMap::new();
|
let mut subsystems: HashMap<String, Subsystem> = HashMap::new();
|
||||||
let mut spn_status: String = "".to_string();
|
let mut spn_status: String = "".to_string();
|
||||||
|
@ -395,7 +439,11 @@ pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) {
|
||||||
};
|
};
|
||||||
debug!("Shutdown request received: {:?}", msg);
|
debug!("Shutdown request received: {:?}", msg);
|
||||||
match msg {
|
match msg {
|
||||||
Response::Ok(_, _) | Response::New(_, _) | Response::Update(_, _) => app.exit(0),
|
Response::Ok(_, _) | Response::New(_, _) | Response::Update(_, _) => {
|
||||||
|
if let Err(err) = app.save_window_state(StateFlags::SIZE | StateFlags::POSITION) {
|
||||||
|
error!("failed to save window state: {}", err);
|
||||||
|
}
|
||||||
|
app.exit(0)},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -403,7 +451,7 @@ pub async fn tray_handler(cli: PortAPI, app: tauri::AppHandle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
update_spn_ui_state(false);
|
update_spn_ui_state(false);
|
||||||
_ = icon.set_icon(Some(Image::from_bytes(RED_ICON).unwrap()));
|
_ = icon.set_icon(Some(Image::from_bytes(get_red_icon()).unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_spn_ui_state(enabled: bool) {
|
fn update_spn_ui_state(enabled: bool) {
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use tauri::{
|
use tauri::{
|
||||||
AppHandle, Manager, Result, UserAttentionType, WebviewUrl, WebviewWindow, WebviewWindowBuilder,
|
image::Image, AppHandle, Listener, Manager, Result, Theme, UserAttentionType, WebviewUrl,
|
||||||
|
WebviewWindow, WebviewWindowBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::portmaster::PortmasterExt;
|
use crate::portmaster::PortmasterExt;
|
||||||
|
|
||||||
|
const LIGHT_PM_ICON: &'static [u8] =
|
||||||
|
include_bytes!("../../../../assets/data/icons/pm_light_512.png");
|
||||||
|
const DARK_PM_ICON: &'static [u8] = include_bytes!("../../../../assets/data/icons/pm_dark_512.png");
|
||||||
|
|
||||||
/// Either returns the existing "main" window or creates a new one.
|
/// Either returns the existing "main" window or creates a new one.
|
||||||
///
|
///
|
||||||
/// The window is not automatically shown (i.e it starts hidden).
|
/// The window is not automatically shown (i.e it starts hidden).
|
||||||
|
@ -16,7 +21,6 @@ use crate::portmaster::PortmasterExt;
|
||||||
pub fn create_main_window(app: &AppHandle) -> Result<WebviewWindow> {
|
pub fn create_main_window(app: &AppHandle) -> Result<WebviewWindow> {
|
||||||
let mut window = if let Some(window) = app.get_webview_window("main") {
|
let mut window = if let Some(window) = app.get_webview_window("main") {
|
||||||
debug!("[tauri] main window already created");
|
debug!("[tauri] main window already created");
|
||||||
|
|
||||||
window
|
window
|
||||||
} else {
|
} else {
|
||||||
debug!("[tauri] creating main window");
|
debug!("[tauri] creating main window");
|
||||||
|
@ -24,6 +28,9 @@ pub fn create_main_window(app: &AppHandle) -> Result<WebviewWindow> {
|
||||||
let res = WebviewWindowBuilder::new(app, "main", WebviewUrl::App("index.html".into()))
|
let res = WebviewWindowBuilder::new(app, "main", WebviewUrl::App("index.html".into()))
|
||||||
.title("Portmaster")
|
.title("Portmaster")
|
||||||
.visible(false)
|
.visible(false)
|
||||||
|
.inner_size(1200.0, 700.0)
|
||||||
|
.min_inner_size(800.0, 600.0)
|
||||||
|
.theme(Some(Theme::Dark))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
|
@ -45,6 +52,11 @@ pub fn create_main_window(app: &AppHandle) -> Result<WebviewWindow> {
|
||||||
// If the window is not yet navigated to the Portmaster UI, do it now.
|
// If the window is not yet navigated to the Portmaster UI, do it now.
|
||||||
may_navigate_to_ui(&mut window, false);
|
may_navigate_to_ui(&mut window, false);
|
||||||
|
|
||||||
|
let _ = match dark_light::detect() {
|
||||||
|
dark_light::Mode::Light => window.set_icon(Image::from_bytes(DARK_PM_ICON).unwrap()),
|
||||||
|
_ => window.set_icon(Image::from_bytes(LIGHT_PM_ICON).unwrap()),
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
if let Ok(_) = std::env::var("TAURI_SHOW_IMMEDIATELY") {
|
if let Ok(_) = std::env::var("TAURI_SHOW_IMMEDIATELY") {
|
||||||
debug!("[tauri] TAURI_SHOW_IMMEDIATELY is set, opening window");
|
debug!("[tauri] TAURI_SHOW_IMMEDIATELY is set, opening window");
|
||||||
|
@ -103,7 +115,7 @@ pub fn open_window(app: &AppHandle) -> Result<WebviewWindow> {
|
||||||
match app.get_webview_window("main") {
|
match app.get_webview_window("main") {
|
||||||
Some(win) => {
|
Some(win) => {
|
||||||
app.portmaster().show_window();
|
app.portmaster().show_window();
|
||||||
|
let _ = win.set_focus();
|
||||||
Ok(win)
|
Ok(win)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -169,7 +169,7 @@ pub fn get_app_info(process_info: ProcessInfo) -> Result<AppInfo> {
|
||||||
return Ok(info.0);
|
return Ok(info.0);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(
|
dbg!(
|
||||||
"{}: failed to get icon: {}",
|
"{}: failed to get icon: {}",
|
||||||
info.0.icon_name,
|
info.0.icon_name,
|
||||||
err.to_string()
|
err.to_string()
|
||||||
|
@ -409,7 +409,7 @@ fn get_icon_as_png_dataurl(name: &str, size: i8) -> Result<(String, String)> {
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
if icon_info.is_null() {
|
if icon_info.is_null() {
|
||||||
error!("failed to lookup icon {}", name);
|
dbg!("failed to lookup icon {}", name);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -423,7 +423,7 @@ fn get_icon_as_png_dataurl(name: &str, size: i8) -> Result<(String, String)> {
|
||||||
match read_and_convert_pixbuf(filename.clone()) {
|
match read_and_convert_pixbuf(filename.clone()) {
|
||||||
Ok(pb) => return Ok((filename, pb)),
|
Ok(pb) => return Ok((filename, pb)),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("failed to load icon from {}: {}", filename, err.to_string());
|
dbg!("failed to load icon from {}: {}", filename, err.to_string());
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,11 @@
|
||||||
"name": "background",
|
"name": "background",
|
||||||
"description": "Start in the background without opening a window"
|
"description": "Start in the background without opening a window"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "log",
|
||||||
|
"description": "Log level to use: off, error, warn, info, debug, trace",
|
||||||
|
"takesValue": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "with-notifications",
|
"name": "with-notifications",
|
||||||
"description": "Enable experimental notifications via Tauri. Replaces the notifier app."
|
"description": "Enable experimental notifications via Tauri. Replaces the notifier app."
|
||||||
|
@ -29,7 +34,7 @@
|
||||||
{
|
{
|
||||||
"name": "with-prompts",
|
"name": "with-prompts",
|
||||||
"description": "Enable experimental prompt support via Tauri. Replaces the notifier app."
|
"description": "Enable experimental prompt support via Tauri. Replaces the notifier app."
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|