mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
refactor(examples/api): new look (#4465)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
2
examples/api/dist/assets/index.css
vendored
2
examples/api/dist/assets/index.css
vendored
File diff suppressed because one or more lines are too long
72
examples/api/dist/assets/index.js
vendored
72
examples/api/dist/assets/index.js
vendored
File diff suppressed because one or more lines are too long
1
examples/api/dist/assets/vendor.css
vendored
1
examples/api/dist/assets/vendor.css
vendored
@@ -1 +0,0 @@
|
||||
ul.svelte-gbh3pt{list-style:none;margin:0;padding:0;padding-left:var(--nodePaddingLeft, 1rem);border-left:var(--nodeBorderLeft, 1px dotted #9ca3af);color:var(--nodeColor, #374151)}.hidden.svelte-gbh3pt{display:none}.bracket.svelte-gbh3pt{cursor:pointer}.bracket.svelte-gbh3pt:hover{background:var(--bracketHoverBackground, #d1d5db)}.comma.svelte-gbh3pt{color:var(--nodeColor, #374151)}.val.svelte-gbh3pt{color:var(--leafDefaultColor, #9ca3af)}.val.string.svelte-gbh3pt{color:var(--leafStringColor, #059669)}.val.number.svelte-gbh3pt{color:var(--leafNumberColor, #d97706)}.val.boolean.svelte-gbh3pt{color:var(--leafBooleanColor, #2563eb)}
|
||||
25
examples/api/dist/assets/vendor.js
vendored
25
examples/api/dist/assets/vendor.js
vendored
File diff suppressed because one or more lines are too long
196
examples/api/dist/global.css
vendored
196
examples/api/dist/global.css
vendored
@@ -1,196 +0,0 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Tauri&display=swap');
|
||||
|
||||
* {
|
||||
font-family: Tauri, Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background: rgb(24, 25, 26, 0.8);
|
||||
}
|
||||
|
||||
.noselect {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
width: 95%;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
.logo-link {
|
||||
font-weight: 700;
|
||||
position: absolute;
|
||||
top: 150px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#response {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
margin-top: 1em;
|
||||
background: rgb(36, 37, 38, 0.8);
|
||||
color: #f0f4f5;
|
||||
border: solid 1px rgba(255, 255, 255, 0.055);
|
||||
box-shadow: 0 1px 5px 0 rgb(0 0 0 / 20%);
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 12px;
|
||||
word-wrap: break-word;
|
||||
padding: 0px 15px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
background: rgb(53, 53, 53, 0.9);
|
||||
color: #fff;
|
||||
font-family: system-ui, sans-serif;
|
||||
border: none !important;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.2;
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin: 0.25rem;
|
||||
transition: 0.2s ease;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button:focus {
|
||||
background: #ffe07a;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px solid #fff;
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
.button {
|
||||
border: 0;
|
||||
border-radius: 0.25rem;
|
||||
background: #67d6ed;
|
||||
color: rgb(0, 0, 0);
|
||||
font-family: system-ui, sans-serif;
|
||||
font-size: 1rem;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin: 0.25rem;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease;
|
||||
}
|
||||
|
||||
.dark-link {
|
||||
color: white;
|
||||
text-decoration: none !important;
|
||||
padding: 0.5em;
|
||||
background: rgb(36, 37, 38);
|
||||
transition: 0.2s ease;
|
||||
border: solid 1px rgba(255, 255, 255, 0.055);
|
||||
box-shadow: 0 1px 5px 0 rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
.dark-link:hover {
|
||||
background: #3d392a;
|
||||
}
|
||||
|
||||
.nv {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: 0.25s ease;
|
||||
}
|
||||
|
||||
.nv:hover {
|
||||
color: #ffe07a;
|
||||
padding-left: 8px;
|
||||
border-left: solid 5px #ffe07a;
|
||||
}
|
||||
|
||||
.nv_selected {
|
||||
color: #67d6ed;
|
||||
padding-left: 8px;
|
||||
border-left: solid 5px #67d6ed;
|
||||
}
|
||||
|
||||
.content {
|
||||
background: rgb(36, 37, 38, 0.5);
|
||||
color: #f0f4f5;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
border: solid 1px rgba(255, 255, 255, 0.055);
|
||||
box-shadow: 0 1px 5px 0 rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
main {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[type='radio']:checked~label {
|
||||
background: rgb(36, 37, 38);
|
||||
color: #67d6ed;
|
||||
border-bottom: 1px solid transparent;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
[type='radio']:checked~label~.content {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.just-around {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alert {
|
||||
width: auto;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
align-items: center;
|
||||
border-radius: 5px;
|
||||
padding-left: 10px;
|
||||
padding-right: 40px;
|
||||
font-size: 15px;
|
||||
color: #000;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
box-shadow: rgba(0, 0, 0, 0.06) 0px 0px 10px;
|
||||
border-left: 6px solid #ff0000;
|
||||
background: #f0f4f5;
|
||||
}
|
||||
|
||||
#file-response {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
span.key {
|
||||
color: #fff;
|
||||
}
|
||||
5
examples/api/dist/index.html
vendored
5
examples/api/dist/index.html
vendored
@@ -1,13 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="stylesheet" href="/global.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Svelte + Vite App</title>
|
||||
<script type="module" crossorigin src="/assets/index.js"></script>
|
||||
<link rel="modulepreload" href="/assets/vendor.js">
|
||||
<link rel="stylesheet" href="/assets/vendor.css">
|
||||
<link rel="stylesheet" href="/assets/index.css">
|
||||
</head>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -1,8 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="stylesheet" href="/global.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Svelte + Vite App</title>
|
||||
</head>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Isolation Secure Script</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Isolation Secure Script</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
window.__TAURI_ISOLATION_HOOK__= (payload) => {
|
||||
window.__TAURI_ISOLATION_HOOK__ = (payload) => {
|
||||
return payload
|
||||
}
|
||||
|
||||
@@ -10,12 +10,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "../../tooling/api/dist",
|
||||
"@zerodevx/svelte-json-view": "0.2.0",
|
||||
"hotkeys-js": "^3.8.5"
|
||||
"@zerodevx/svelte-json-view": "0.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.11",
|
||||
"svelte": "3.35.0",
|
||||
"vite": "^2.6.4"
|
||||
"@iconify-json/codicon": "^1.1.10",
|
||||
"@iconify-json/ph": "^1.1.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.49",
|
||||
"svelte": "^3.48.0",
|
||||
"unocss": "^0.39.3",
|
||||
"vite": "^2.9.12"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Tauri&display=swap');
|
||||
|
||||
* {
|
||||
font-family: Tauri, Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background: rgb(24, 25, 26, 0.8);
|
||||
}
|
||||
|
||||
.noselect {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
width: 95%;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
.logo-link {
|
||||
font-weight: 700;
|
||||
position: absolute;
|
||||
top: 150px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#response {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
margin-top: 1em;
|
||||
background: rgb(36, 37, 38, 0.8);
|
||||
color: #f0f4f5;
|
||||
border: solid 1px rgba(255, 255, 255, 0.055);
|
||||
box-shadow: 0 1px 5px 0 rgb(0 0 0 / 20%);
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 12px;
|
||||
word-wrap: break-word;
|
||||
padding: 0px 15px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
background: rgb(53, 53, 53, 0.9);
|
||||
color: #fff;
|
||||
font-family: system-ui, sans-serif;
|
||||
border: none !important;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.2;
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin: 0.25rem;
|
||||
transition: 0.2s ease;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button:focus {
|
||||
background: #ffe07a;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px solid #fff;
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
.button {
|
||||
border: 0;
|
||||
border-radius: 0.25rem;
|
||||
background: #67d6ed;
|
||||
color: rgb(0, 0, 0);
|
||||
font-family: system-ui, sans-serif;
|
||||
font-size: 1rem;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
padding: 0.25rem 0.5rem;
|
||||
margin: 0.25rem;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease;
|
||||
}
|
||||
|
||||
.dark-link {
|
||||
color: white;
|
||||
text-decoration: none !important;
|
||||
padding: 0.5em;
|
||||
background: rgb(36, 37, 38);
|
||||
transition: 0.2s ease;
|
||||
border: solid 1px rgba(255, 255, 255, 0.055);
|
||||
box-shadow: 0 1px 5px 0 rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
.dark-link:hover {
|
||||
background: #3d392a;
|
||||
}
|
||||
|
||||
.nv {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: 0.25s ease;
|
||||
}
|
||||
|
||||
.nv:hover {
|
||||
color: #ffe07a;
|
||||
padding-left: 8px;
|
||||
border-left: solid 5px #ffe07a;
|
||||
}
|
||||
|
||||
.nv_selected {
|
||||
color: #67d6ed;
|
||||
padding-left: 8px;
|
||||
border-left: solid 5px #67d6ed;
|
||||
}
|
||||
|
||||
.content {
|
||||
background: rgb(36, 37, 38, 0.5);
|
||||
color: #f0f4f5;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
border: solid 1px rgba(255, 255, 255, 0.055);
|
||||
box-shadow: 0 1px 5px 0 rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
main {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[type='radio']:checked~label {
|
||||
background: rgb(36, 37, 38);
|
||||
color: #67d6ed;
|
||||
border-bottom: 1px solid transparent;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
[type='radio']:checked~label~.content {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.col {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.just-around {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alert {
|
||||
width: auto;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
align-items: center;
|
||||
border-radius: 5px;
|
||||
padding-left: 10px;
|
||||
padding-right: 40px;
|
||||
font-size: 15px;
|
||||
color: #000;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
box-shadow: rgba(0, 0, 0, 0.06) 0px 0px 10px;
|
||||
border-left: 6px solid #ff0000;
|
||||
background: #f0f4f5;
|
||||
}
|
||||
|
||||
#file-response {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
span.key {
|
||||
color: #fff;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
26
examples/api/src-tauri/Cargo.lock
generated
26
examples/api/src-tauri/Cargo.lock
generated
@@ -97,6 +97,8 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tiny_http",
|
||||
"window-shadows",
|
||||
"window-vibrancy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3948,6 +3950,30 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "window-shadows"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "796156ad1a67853e927727809bb6138ddc1f19ebced0dc976c0d109d5e2576f7"
|
||||
dependencies = [
|
||||
"cocoa",
|
||||
"objc",
|
||||
"raw-window-handle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "window-vibrancy"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b66a7f578d164c3f53425ecb73241246ed56a7c3383a15e741684c949a88c86"
|
||||
dependencies = [
|
||||
"cocoa",
|
||||
"objc",
|
||||
"raw-window-handle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.24.0"
|
||||
|
||||
@@ -31,6 +31,10 @@ features = [
|
||||
"updater"
|
||||
]
|
||||
|
||||
[target."cfg(target_os = \"windows\")".dependencies]
|
||||
window-vibrancy = "0.1"
|
||||
window-shadows= "0.1"
|
||||
|
||||
[features]
|
||||
default = [ "custom-protocol" ]
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
)]
|
||||
|
||||
mod cmd;
|
||||
mod menu;
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
@@ -23,11 +22,6 @@ struct Reply {
|
||||
data: String,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn menu_toggle(window: tauri::Window) {
|
||||
window.menu_handle().toggle().unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let tray_menu1 = SystemTrayMenu::new()
|
||||
.add_item(CustomMenuItem::new("toggle", "Toggle"))
|
||||
@@ -44,10 +38,30 @@ fn main() {
|
||||
let is_menu1 = AtomicBool::new(true);
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut app = tauri::Builder::default()
|
||||
let mut builder = tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
#[allow(unused_mut)]
|
||||
let mut window_builder = WindowBuilder::new(app, "main", WindowUrl::default())
|
||||
.title("Tauri API Validation")
|
||||
.inner_size(1000., 800.)
|
||||
.min_inner_size(600., 400.);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
window_builder = window_builder.transparent(true);
|
||||
window_builder = window_builder.decorations(false);
|
||||
}
|
||||
|
||||
let window = window_builder.build().unwrap();
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let _ = window_shadows::set_shadow(&window, true);
|
||||
let _ = window_vibrancy::apply_blur(&window, Some((0, 0, 0, 0)));
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
app.get_window("main").unwrap().open_devtools();
|
||||
window.open_devtools();
|
||||
|
||||
std::thread::spawn(|| {
|
||||
let server = match tiny_http::Server::http("localhost:3003") {
|
||||
@@ -88,10 +102,6 @@ fn main() {
|
||||
.expect("failed to emit");
|
||||
});
|
||||
})
|
||||
.menu(menu::get_menu())
|
||||
.on_menu_event(|event| {
|
||||
println!("{:?}", event.menu_item_id());
|
||||
})
|
||||
.system_tray(SystemTray::new().with_menu(tray_menu1.clone()))
|
||||
.on_system_tray_event(move |app, event| match event {
|
||||
SystemTrayEvent::LeftClick {
|
||||
@@ -165,11 +175,18 @@ fn main() {
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
});
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
builder = builder.menu(tauri::Menu::os_default("Tauri API Validation"));
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut app = builder
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
cmd::log_operation,
|
||||
cmd::perform_request,
|
||||
menu_toggle,
|
||||
])
|
||||
.build(tauri::generate_context!())
|
||||
.expect("error while building tauri application");
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use tauri::{CustomMenuItem, Menu, MenuItem, Submenu};
|
||||
|
||||
pub fn get_menu() -> Menu {
|
||||
#[allow(unused_mut)]
|
||||
let mut disable_item =
|
||||
CustomMenuItem::new("disable-menu", "Disable menu").accelerator("CmdOrControl+D");
|
||||
#[allow(unused_mut)]
|
||||
let mut test_item = CustomMenuItem::new("test", "Test").accelerator("CmdOrControl+T");
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
disable_item = disable_item.native_image(tauri::NativeImage::MenuOnState);
|
||||
test_item = test_item.native_image(tauri::NativeImage::Add);
|
||||
}
|
||||
|
||||
// create a submenu
|
||||
let my_sub_menu = Menu::with_items([disable_item.into()]);
|
||||
|
||||
let my_app_menu = Menu::new()
|
||||
.add_native_item(MenuItem::Copy)
|
||||
.add_submenu(Submenu::new("Sub menu", my_sub_menu));
|
||||
|
||||
let test_menu = Menu::new()
|
||||
.add_item(CustomMenuItem::new(
|
||||
"selected/disabled",
|
||||
"Selected and disabled",
|
||||
))
|
||||
.add_native_item(MenuItem::Separator)
|
||||
.add_item(test_item);
|
||||
|
||||
// add all our childs to the menu (order is how they'll appear)
|
||||
Menu::new()
|
||||
.add_submenu(Submenu::new("My app", my_app_menu))
|
||||
.add_submenu(Submenu::new("Other menu", test_menu))
|
||||
}
|
||||
@@ -117,12 +117,7 @@
|
||||
"scope": ["http://localhost:3003"]
|
||||
}
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"title": "Tauri API Validation",
|
||||
"transparent": true
|
||||
}
|
||||
],
|
||||
"windows": [],
|
||||
"security": {
|
||||
"csp": {
|
||||
"default-src": "'self' customprotocol: asset:",
|
||||
|
||||
@@ -1,178 +1,380 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { writable } from "svelte/store";
|
||||
import hotkeys from "hotkeys-js";
|
||||
import { open } from "@tauri-apps/api/shell";
|
||||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
import { appWindow } from "@tauri-apps/api/window";
|
||||
import { writable } from 'svelte/store'
|
||||
import { open } from '@tauri-apps/api/shell'
|
||||
import { appWindow, getCurrent } from '@tauri-apps/api/window'
|
||||
import * as os from '@tauri-apps/api/os'
|
||||
|
||||
import Welcome from "./components/Welcome.svelte";
|
||||
import Cli from "./components/Cli.svelte";
|
||||
import Communication from "./components/Communication.svelte";
|
||||
import Dialog from "./components/Dialog.svelte";
|
||||
import FileSystem from "./components/FileSystem.svelte";
|
||||
import Http from "./components/Http.svelte";
|
||||
import Notifications from "./components/Notifications.svelte";
|
||||
import Window from "./components/Window.svelte";
|
||||
import Shortcuts from "./components/Shortcuts.svelte";
|
||||
import Shell from "./components/Shell.svelte";
|
||||
import Updater from "./components/Updater.svelte";
|
||||
import Clipboard from "./components/Clipboard.svelte";
|
||||
import WebRTC from './components/WebRTC.svelte'
|
||||
import HttpForm from "./components/HttpForm.svelte";
|
||||
import Welcome from './views/Welcome.svelte'
|
||||
import Cli from './views/Cli.svelte'
|
||||
import Communication from './views/Communication.svelte'
|
||||
import Dialog from './views/Dialog.svelte'
|
||||
import FileSystem from './views/FileSystem.svelte'
|
||||
import Http from './views/Http.svelte'
|
||||
import Notifications from './views/Notifications.svelte'
|
||||
import Window from './views/Window.svelte'
|
||||
import Shortcuts from './views/Shortcuts.svelte'
|
||||
import Shell from './views/Shell.svelte'
|
||||
import Updater from './views/Updater.svelte'
|
||||
import Clipboard from './views/Clipboard.svelte'
|
||||
import WebRTC from './views/WebRTC.svelte'
|
||||
|
||||
const MENU_TOGGLE_HOTKEY = 'ctrl+b';
|
||||
|
||||
onMount(() => {
|
||||
hotkeys(MENU_TOGGLE_HOTKEY, () => {
|
||||
invoke('menu_toggle');
|
||||
});
|
||||
});
|
||||
import { onMount } from 'svelte'
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
import { ask } from '@tauri-apps/api/dialog'
|
||||
|
||||
appWindow.listen('tauri://file-drop', function (event) {
|
||||
onMessage(`File drop: ${event.payload}`);
|
||||
});
|
||||
onMessage(`File drop: ${event.payload}`)
|
||||
})
|
||||
|
||||
const views = [
|
||||
{
|
||||
label: "Welcome",
|
||||
label: 'Welcome',
|
||||
component: Welcome,
|
||||
icon: 'i-ph-hand-waving'
|
||||
},
|
||||
{
|
||||
label: "Messages",
|
||||
label: 'Communication',
|
||||
component: Communication,
|
||||
icon: 'i-codicon-radio-tower'
|
||||
},
|
||||
{
|
||||
label: "CLI",
|
||||
label: 'CLI',
|
||||
component: Cli,
|
||||
icon: 'i-codicon-terminal'
|
||||
},
|
||||
{
|
||||
label: "Dialog",
|
||||
label: 'Dialog',
|
||||
component: Dialog,
|
||||
icon: 'i-codicon-multiple-windows'
|
||||
},
|
||||
{
|
||||
label: "File system",
|
||||
label: 'File system',
|
||||
component: FileSystem,
|
||||
icon: 'i-codicon-files'
|
||||
},
|
||||
{
|
||||
label: "HTTP",
|
||||
label: 'HTTP',
|
||||
component: Http,
|
||||
icon: 'i-ph-globe-hemisphere-west'
|
||||
},
|
||||
{
|
||||
label: "HTTP Form",
|
||||
component: HttpForm,
|
||||
},
|
||||
{
|
||||
label: "Notifications",
|
||||
label: 'Notifications',
|
||||
component: Notifications,
|
||||
icon: 'i-codicon-bell-dot'
|
||||
},
|
||||
{
|
||||
label: "Window",
|
||||
label: 'Window',
|
||||
component: Window,
|
||||
icon: 'i-codicon-window'
|
||||
},
|
||||
{
|
||||
label: "Shortcuts",
|
||||
label: 'Shortcuts',
|
||||
component: Shortcuts,
|
||||
icon: 'i-codicon-record-keys'
|
||||
},
|
||||
{
|
||||
label: "Shell",
|
||||
label: 'Shell',
|
||||
component: Shell,
|
||||
icon: 'i-codicon-terminal-bash'
|
||||
},
|
||||
{
|
||||
label: "Updater",
|
||||
label: 'Updater',
|
||||
component: Updater,
|
||||
icon: 'i-codicon-cloud-download'
|
||||
},
|
||||
{
|
||||
label: "Clipboard",
|
||||
label: 'Clipboard',
|
||||
component: Clipboard,
|
||||
icon: 'i-codicon-clippy'
|
||||
},
|
||||
{
|
||||
label: "WebRTC",
|
||||
label: 'WebRTC',
|
||||
component: WebRTC,
|
||||
},
|
||||
];
|
||||
|
||||
let selected = views[0];
|
||||
|
||||
let responses = writable([]);
|
||||
icon: 'i-ph-broadcast'
|
||||
}
|
||||
]
|
||||
|
||||
let selected = views[0]
|
||||
function select(view) {
|
||||
selected = view;
|
||||
selected = view
|
||||
}
|
||||
|
||||
// Window controls
|
||||
let isWindowMaximized
|
||||
onMount(async () => {
|
||||
const window = getCurrent()
|
||||
isWindowMaximized = await window.isMaximized()
|
||||
listen('tauri://resize', async () => {
|
||||
isWindowMaximized = await window.isMaximized()
|
||||
})
|
||||
})
|
||||
|
||||
function minimize() {
|
||||
getCurrent().minimize()
|
||||
}
|
||||
|
||||
async function toggleMaximize() {
|
||||
const window = getCurrent()
|
||||
;(await window.isMaximized()) ? window.unmaximize() : window.maximize()
|
||||
}
|
||||
|
||||
let confirmed_close = false
|
||||
async function close() {
|
||||
if (!confirmed_close) {
|
||||
confirmed_close = await ask(
|
||||
'Are you sure that you want to close this window?',
|
||||
{
|
||||
title: 'Tauri API'
|
||||
}
|
||||
)
|
||||
if (confirmed_close) {
|
||||
getCurrent().close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dark/light
|
||||
let isDark
|
||||
onMount(() => {
|
||||
isDark = localStorage.getItem('theme') == 'dark'
|
||||
applyTheme(isDark)
|
||||
})
|
||||
function applyTheme(isDark) {
|
||||
const html = document.querySelector('html')
|
||||
isDark ? html.classList.add('dark') : html.classList.remove('dark')
|
||||
localStorage.setItem('theme', isDark ? 'dark' : '')
|
||||
}
|
||||
function toggleDark() {
|
||||
isDark = !isDark
|
||||
applyTheme(isDark)
|
||||
}
|
||||
|
||||
// Console
|
||||
let messages = writable([])
|
||||
function onMessage(value) {
|
||||
responses.update(r => [{ text: `[${new Date().toLocaleTimeString()}]` + ': ' + (typeof value === "string" ? value : JSON.stringify(value)) }, ...r])
|
||||
messages.update((r) => [
|
||||
{
|
||||
html:
|
||||
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
|
||||
(typeof value === 'string' ? value : JSON.stringify(value, null, 1)) +
|
||||
'</pre>'
|
||||
},
|
||||
...r
|
||||
])
|
||||
}
|
||||
|
||||
// this function is renders HTML without sanitizing it so it's insecure
|
||||
// we only use it with our own input data
|
||||
function insecureRenderHtml(html) {
|
||||
responses.update(r => [{ html }, ...r])
|
||||
messages.update((r) => [
|
||||
{
|
||||
html:
|
||||
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
|
||||
html +
|
||||
'</pre>'
|
||||
},
|
||||
...r
|
||||
])
|
||||
}
|
||||
|
||||
function clear() {
|
||||
responses.update(() => []);
|
||||
messages.update(() => [])
|
||||
}
|
||||
|
||||
function onLogoClick() {
|
||||
open("https://tauri.app/");
|
||||
let consoleEl, consoleH, cStartY
|
||||
let minConsoleHeight = 50
|
||||
function startResizingConsole(e) {
|
||||
cStartY = e.clientY
|
||||
|
||||
const styles = window.getComputedStyle(consoleEl)
|
||||
consoleH = parseInt(styles.height, 10)
|
||||
|
||||
const moveHandler = (e) => {
|
||||
const dy = e.clientY - cStartY
|
||||
const newH = consoleH - dy
|
||||
consoleEl.style.height = `${
|
||||
newH < minConsoleHeight ? minConsoleHeight : newH
|
||||
}px`
|
||||
}
|
||||
const upHandler = () => {
|
||||
document.removeEventListener('mouseup', upHandler)
|
||||
document.removeEventListener('mousemove', moveHandler)
|
||||
}
|
||||
document.addEventListener('mouseup', upHandler)
|
||||
document.addEventListener('mousemove', moveHandler)
|
||||
}
|
||||
|
||||
let isWindows
|
||||
onMount(async () => {
|
||||
isWindows = (await os.platform()) === 'win32'
|
||||
})
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<div class="flex row noselect just-around container" data-tauri-drag-region>
|
||||
<img class="logo" src="tauri logo.png" height="60" on:click={onLogoClick} alt="logo" />
|
||||
<div>
|
||||
<a class="dark-link" target="_blank" href="https://tauri.app/en/docs/get-started/intro">
|
||||
Documentation
|
||||
</a>
|
||||
<a class="dark-link" target="_blank" href="https://github.com/tauri-apps/tauri">
|
||||
Github
|
||||
</a>
|
||||
<a class="dark-link" target="_blank" href="https://github.com/tauri-apps/tauri/tree/dev/tauri/examples/api">
|
||||
Source
|
||||
</a>
|
||||
</div>
|
||||
{#if isWindows}
|
||||
<div
|
||||
class="w-screen select-none h-8 pl-2 flex justify-between items-center absolute text-primaryText dark:text-darkPrimaryText"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<span class="text-darkPrimaryText">Tauri API Validation</span>
|
||||
<span
|
||||
class="
|
||||
h-100%
|
||||
children:h-100% children:w-12 children:inline-flex
|
||||
children:items-center children:justify-center"
|
||||
>
|
||||
<span
|
||||
title={isDark ? 'Switch to Light mode' : 'Switch to Dark mode'}
|
||||
class="hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
|
||||
on:click={toggleDark}
|
||||
>
|
||||
{#if isDark}
|
||||
<div class="i-ph-sun" />
|
||||
{:else}
|
||||
<div class="i-ph-moon" />
|
||||
{/if}
|
||||
</span>
|
||||
<span
|
||||
title="Minimize"
|
||||
class="hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
|
||||
on:click={minimize}
|
||||
>
|
||||
<div class="i-codicon-chrome-minimize" />
|
||||
</span>
|
||||
<span
|
||||
title={isWindowMaximized ? 'Restore' : 'Maximize'}
|
||||
class="hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
|
||||
on:click={toggleMaximize}
|
||||
>
|
||||
{#if isWindowMaximized}
|
||||
<div class="i-codicon-chrome-restore" />
|
||||
{:else}
|
||||
<div class="i-codicon-chrome-maximize" />
|
||||
{/if}
|
||||
</span>
|
||||
<span
|
||||
title="Close"
|
||||
class="hover:bg-red-700 dark:hover:bg-red-700 hover:text-darkPrimaryText active:bg-red-700/90 dark:active:bg-red-700/90 active:text-darkPrimaryText "
|
||||
on:click={close}
|
||||
>
|
||||
<div class="i-codicon-chrome-close" />
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex row">
|
||||
<div class="view-container">
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class="flex h-screen w-screen overflow-hidden children-pt8 children-pb-2 text-primaryText dark:text-darkPrimaryText"
|
||||
>
|
||||
<aside
|
||||
class="w-75 {isWindows
|
||||
? 'bg-darkPrimaryLighter/60'
|
||||
: 'bg-darkPrimaryLighter'} transition-colors-250 overflow-hidden grid select-none px-2"
|
||||
>
|
||||
<img
|
||||
on:click={() => open('https://tauri.app/')}
|
||||
class="self-center p-7 cursor-pointer"
|
||||
src="tauri_logo.png"
|
||||
alt="Tauri logo"
|
||||
/>
|
||||
{#if !isWindows}
|
||||
<a href="##" class="nv justify-between h-8" on:click={toggleDark}>
|
||||
{#if isDark}
|
||||
Switch to Light mode
|
||||
<div class="i-ph-sun" />
|
||||
{:else}
|
||||
Switch to Dark mode
|
||||
<div class="i-ph-moon" />
|
||||
{/if}
|
||||
</a>
|
||||
<br />
|
||||
<div class="bg-white/5 h-2px" />
|
||||
<br />
|
||||
{/if}
|
||||
|
||||
<a
|
||||
class="nv justify-between h-8"
|
||||
target="_blank"
|
||||
href="https://tauri.app/v1/guides/"
|
||||
>
|
||||
Documentation
|
||||
<span class="i-codicon-link-external" />
|
||||
</a>
|
||||
<a
|
||||
class="nv justify-between h-8"
|
||||
target="_blank"
|
||||
href="https://github.com/tauri-apps/tauri"
|
||||
>
|
||||
Github
|
||||
<span class="i-codicon-link-external" />
|
||||
</a>
|
||||
<a
|
||||
class="nv justify-between h-8"
|
||||
target="_blank"
|
||||
href="https://github.com/tauri-apps/tauri/tree/dev/examples/api"
|
||||
>
|
||||
Source
|
||||
<span class="i-codicon-link-external" />
|
||||
</a>
|
||||
<br />
|
||||
<div class="bg-white/5 h-2px" />
|
||||
<br />
|
||||
<div
|
||||
class="flex flex-col overflow-y-auto children-h-10 children-flex-none gap-1"
|
||||
>
|
||||
{#each views as view}
|
||||
<p class="nv noselect {selected === view ? 'nv_selected' : ''}" on:click={()=> select(view)}
|
||||
<a
|
||||
href="##"
|
||||
class="nv {selected === view ? 'nv_selected' : ''}"
|
||||
on:click={() => select(view)}
|
||||
>
|
||||
<div class="{view.icon} mr-2" />
|
||||
<p>{view.label}</p></a
|
||||
>
|
||||
{view.label}
|
||||
</p>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="content">
|
||||
<svelte:component this={selected.component} {onMessage} {insecureRenderHtml} />
|
||||
</aside>
|
||||
<main
|
||||
class="flex-1 bg-primary dark:bg-darkPrimary transition-colors-250 grid grid-rows-[2fr_auto]"
|
||||
>
|
||||
<div class="px-5 overflow-hidden grid grid-rows-[auto_1fr]">
|
||||
<h1>{selected.label}</h1>
|
||||
<div class="overflow-y-auto">
|
||||
<div class="mr-2">
|
||||
<svelte:component
|
||||
this={selected.component}
|
||||
{onMessage}
|
||||
{insecureRenderHtml}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="response">
|
||||
<p class="flex row just-around">
|
||||
<strong>Tauri Console</strong>
|
||||
<span class="nv" on:click={clear}>clear</span>
|
||||
</p>
|
||||
{#each $responses as r}
|
||||
{#if r.text}
|
||||
<p>{r.text}</p>
|
||||
{:else}
|
||||
{@html r.html}
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.view-container {
|
||||
width:15em;
|
||||
margin-left:0.5em;
|
||||
}
|
||||
|
||||
#response {
|
||||
white-space: pre-line;
|
||||
}
|
||||
</style>
|
||||
<div
|
||||
bind:this={consoleEl}
|
||||
id="console"
|
||||
class="select-none h-15rem grid grid-rows-[2px_2rem_1fr] gap-1 overflow-hidden"
|
||||
>
|
||||
<div
|
||||
on:mousedown={startResizingConsole}
|
||||
class="bg-black/20 h-2px cursor-ns-resize"
|
||||
/>
|
||||
<div class="flex justify-between items-center px-2">
|
||||
<p class="font-semibold">Console</p>
|
||||
<div
|
||||
class="cursor-pointer h-85% rd-1 p-1 flex justify-center items-center
|
||||
hover:bg-hoverOverlay dark:hover:bg-darkHoverOverlay
|
||||
active:bg-hoverOverlay/25 dark:active:bg-darkHoverOverlay/25
|
||||
"
|
||||
on:click={clear}
|
||||
>
|
||||
<div class="i-codicon-clear-all" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-2 overflow-y-auto all:font-mono code-block all:text-xs">
|
||||
{#each $messages as r}
|
||||
{@html r.html}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
30
examples/api/src/app.css
Normal file
30
examples/api/src/app.css
Normal file
@@ -0,0 +1,30 @@
|
||||
*:not(h1, h2, h3, h4, h5, h6) {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
font-family: 'Rubik', sans-serif;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0.25rem;
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 0.05rem 0.25rem;
|
||||
}
|
||||
|
||||
code.code-block {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<script>
|
||||
import { getMatches } from "@tauri-apps/api/cli";
|
||||
|
||||
export let onMessage;
|
||||
|
||||
function cliMatches() {
|
||||
getMatches().then(onMessage).catch(onMessage);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
This binary can be run on the terminal and takes the following arguments:
|
||||
<ul>
|
||||
<li>--config PATH</li>
|
||||
<li>--theme light|dark|system</li>
|
||||
<li>--verbose</li>
|
||||
</ul>
|
||||
Additionally, it has a <i>update --background</i> subcommand.
|
||||
Note that the arguments are only parsed, not implemented.
|
||||
<br>
|
||||
<button class="button" id="cli-matches" on:click={cliMatches}>
|
||||
Get matches
|
||||
</button>
|
||||
</div>
|
||||
@@ -1,36 +0,0 @@
|
||||
<script>
|
||||
import {
|
||||
writeText,
|
||||
readText
|
||||
} from "@tauri-apps/api/clipboard";
|
||||
|
||||
export let onMessage;
|
||||
let text = "clipboard message";
|
||||
|
||||
function write() {
|
||||
writeText(text)
|
||||
.then(() => {
|
||||
onMessage('Wrote to the clipboard');
|
||||
})
|
||||
.catch(onMessage);
|
||||
}
|
||||
|
||||
function read() {
|
||||
readText()
|
||||
.then((contents) => {
|
||||
onMessage(`Clipboard contents: ${contents}`);
|
||||
})
|
||||
.catch(onMessage);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<input
|
||||
placeholder="Text to write to the clipboard"
|
||||
bind:value={text}
|
||||
/>
|
||||
<button type="button" on:click={write}>Write</button>
|
||||
</div>
|
||||
<button type="button" on:click={read}>Read</button>
|
||||
</div>
|
||||
@@ -1,50 +0,0 @@
|
||||
<script>
|
||||
import { listen, emit } from "@tauri-apps/api/event";
|
||||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
|
||||
export let onMessage;
|
||||
let unlisten;
|
||||
|
||||
onMount(async () => {
|
||||
unlisten = await listen("rust-event", onMessage)
|
||||
})
|
||||
onDestroy(() => {
|
||||
if (unlisten) {
|
||||
unlisten()
|
||||
}
|
||||
})
|
||||
|
||||
function log() {
|
||||
invoke("log_operation", {
|
||||
event: "tauri-click",
|
||||
payload: "this payload is optional because we used Option in Rust",
|
||||
});
|
||||
}
|
||||
|
||||
function performRequest() {
|
||||
invoke("perform_request", {
|
||||
endpoint: "dummy endpoint arg",
|
||||
body: {
|
||||
id: 5,
|
||||
name: "test",
|
||||
},
|
||||
})
|
||||
.then(onMessage)
|
||||
.catch(onMessage);
|
||||
}
|
||||
|
||||
function emitEvent() {
|
||||
emit("js-event", "this is the payload string");
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<button class="button" id="log" on:click={log}>Call Log API</button>
|
||||
<button class="button" id="request" on:click={performRequest}>
|
||||
Call Request (async) API
|
||||
</button>
|
||||
<button class="button" id="event" on:click={emitEvent}>
|
||||
Send event to Rust
|
||||
</button>
|
||||
</div>
|
||||
@@ -1,122 +0,0 @@
|
||||
<script>
|
||||
import { open, save } from "@tauri-apps/api/dialog";
|
||||
import { readBinaryFile } from "@tauri-apps/api/fs";
|
||||
|
||||
export let onMessage;
|
||||
export let insecureRenderHtml;
|
||||
let defaultPath = null;
|
||||
let filter = null;
|
||||
let multiple = false;
|
||||
let directory = false;
|
||||
|
||||
function arrayBufferToBase64(buffer, callback) {
|
||||
var blob = new Blob([buffer], {
|
||||
type: "application/octet-binary",
|
||||
});
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (evt) {
|
||||
var dataurl = evt.target.result;
|
||||
callback(dataurl.substr(dataurl.indexOf(",") + 1));
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
}
|
||||
|
||||
function openDialog() {
|
||||
open({
|
||||
title: 'My wonderful open dialog',
|
||||
defaultPath,
|
||||
filters: filter
|
||||
? [
|
||||
{
|
||||
name: "Tauri Example",
|
||||
extensions: filter.split(",").map((f) => f.trim()),
|
||||
},
|
||||
]
|
||||
: [],
|
||||
multiple,
|
||||
directory,
|
||||
})
|
||||
.then(function (res) {
|
||||
if (Array.isArray(res)) {
|
||||
onMessage(res);
|
||||
} else {
|
||||
var pathToRead = res;
|
||||
var isFile = pathToRead.match(/\S+\.\S+$/g);
|
||||
readBinaryFile(pathToRead)
|
||||
.then(function (response) {
|
||||
if (isFile) {
|
||||
if (
|
||||
pathToRead.includes(".png") ||
|
||||
pathToRead.includes(".jpg")
|
||||
) {
|
||||
arrayBufferToBase64(
|
||||
new Uint8Array(response),
|
||||
function (base64) {
|
||||
var src = "data:image/png;base64," + base64;
|
||||
insecureRenderHtml('<img src="' + src + '"></img>');
|
||||
}
|
||||
);
|
||||
} else {
|
||||
onMessage(res);
|
||||
}
|
||||
} else {
|
||||
onMessage(res);
|
||||
}
|
||||
})
|
||||
.catch(onMessage(res));
|
||||
}
|
||||
})
|
||||
.catch(onMessage);
|
||||
}
|
||||
|
||||
function saveDialog() {
|
||||
save({
|
||||
title: 'My wonderful save dialog',
|
||||
defaultPath,
|
||||
filters: filter
|
||||
? [
|
||||
{
|
||||
name: "Tauri Example",
|
||||
extensions: filter.split(",").map((f) => f.trim()),
|
||||
},
|
||||
]
|
||||
: [],
|
||||
})
|
||||
.then(onMessage)
|
||||
.catch(onMessage);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<input
|
||||
id="dialog-default-path"
|
||||
placeholder="Default path"
|
||||
bind:value={defaultPath}
|
||||
/>
|
||||
<input
|
||||
id="dialog-filter"
|
||||
placeholder="Extensions filter, comma-separated"
|
||||
bind:value={filter}
|
||||
/>
|
||||
<div>
|
||||
<input type="checkbox" id="dialog-multiple" bind:checked={multiple} />
|
||||
<label for="dialog-multiple">Multiple</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="dialog-directory" bind:checked={directory} />
|
||||
<label for="dialog-directory">Directory</label>
|
||||
</div>
|
||||
|
||||
<button class="button" id="open-dialog" on:click={openDialog}
|
||||
>Open dialog</button
|
||||
>
|
||||
<button class="button" id="save-dialog" on:click={saveDialog}
|
||||
>Open save dialog</button
|
||||
>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#dialog-filter {
|
||||
width: 260px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,57 +0,0 @@
|
||||
<script>
|
||||
import { getClient, Body } from "@tauri-apps/api/http";
|
||||
let httpMethod = "GET";
|
||||
let httpBody = "";
|
||||
|
||||
export let onMessage;
|
||||
|
||||
async function makeHttpRequest() {
|
||||
const client = await getClient().catch(e => {
|
||||
onMessage(e)
|
||||
throw e
|
||||
});
|
||||
let method = httpMethod || "GET";
|
||||
|
||||
const options = {
|
||||
url: "http://localhost:3003",
|
||||
method: method || "GET",
|
||||
};
|
||||
|
||||
if (
|
||||
(httpBody.startsWith("{") && httpBody.endsWith("}")) ||
|
||||
(httpBody.startsWith("[") && httpBody.endsWith("]"))
|
||||
) {
|
||||
options.body = Body.json(JSON.parse(httpBody));
|
||||
} else if (httpBody !== "") {
|
||||
options.body = Body.text(httpBody);
|
||||
}
|
||||
|
||||
client.request(options).then(onMessage).catch(onMessage);
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={makeHttpRequest}>
|
||||
<select class="button" id="request-method" bind:value={httpMethod}>
|
||||
<option value="GET">GET</option>
|
||||
<option value="POST">POST</option>
|
||||
<option value="PUT">PUT</option>
|
||||
<option value="PATCH">PATCH</option>
|
||||
<option value="DELETE">DELETE</option>
|
||||
</select>
|
||||
<br />
|
||||
<textarea
|
||||
id="request-body"
|
||||
placeholder="Request body"
|
||||
rows="5"
|
||||
bind:value={httpBody}
|
||||
/>
|
||||
<button class="button" id="make-request"> Make request </button>
|
||||
</form>
|
||||
|
||||
<style>
|
||||
#request-body {
|
||||
width:100%;
|
||||
margin-right:10px;
|
||||
font-size:12px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,39 +0,0 @@
|
||||
<script>
|
||||
import { getClient, Body, ResponseType } from "@tauri-apps/api/http"
|
||||
import { JsonView } from '@zerodevx/svelte-json-view'
|
||||
let foo = 'baz'
|
||||
let bar = 'qux'
|
||||
let result = null
|
||||
let multipart = true
|
||||
|
||||
async function doPost () {
|
||||
const client = await getClient().catch(e => {
|
||||
onMessage(e)
|
||||
throw e
|
||||
})
|
||||
|
||||
result = await client.request({
|
||||
url: 'http://localhost:3003',
|
||||
method: 'POST',
|
||||
body: Body.form({
|
||||
foo,
|
||||
bar
|
||||
}),
|
||||
headers: multipart ? { 'Content-Type': 'multipart/form-data' } : undefined,
|
||||
responseType: ResponseType.Text
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<input bind:value={foo} />
|
||||
<input bind:value={bar} />
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={multipart} />
|
||||
Multipart
|
||||
</label>
|
||||
<button type="button" on:click={doPost}>
|
||||
Post it.
|
||||
</button>
|
||||
<JsonView json={result} />
|
||||
</div>
|
||||
@@ -1,34 +0,0 @@
|
||||
<script>
|
||||
export let onMessage;
|
||||
|
||||
// send the notification directly
|
||||
// the backend is responsible for checking the permission
|
||||
function _sendNotification() {
|
||||
new Notification("Notification title", {
|
||||
body: "This is the notification body",
|
||||
});
|
||||
}
|
||||
|
||||
// alternatively, check the permission ourselves
|
||||
function sendNotification() {
|
||||
if (Notification.permission === "default") {
|
||||
Notification.requestPermission()
|
||||
.then(function (response) {
|
||||
if (response === "granted") {
|
||||
_sendNotification();
|
||||
} else {
|
||||
onMessage("Permission is " + response);
|
||||
}
|
||||
})
|
||||
.catch(onMessage);
|
||||
} else if (Notification.permission === "granted") {
|
||||
_sendNotification();
|
||||
} else {
|
||||
onMessage("Permission is denied");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<button class="button" id="notification" on:click={_sendNotification}>
|
||||
Send test notification
|
||||
</button>
|
||||
@@ -1,74 +0,0 @@
|
||||
<script>
|
||||
import { Command } from "@tauri-apps/api/shell"
|
||||
const windows = navigator.userAgent.includes('Windows')
|
||||
let cmd = windows ? 'cmd' : 'sh'
|
||||
let args = windows ? ['/C'] : ['-c']
|
||||
|
||||
export let onMessage;
|
||||
|
||||
let script = 'echo "hello world"'
|
||||
let cwd = null
|
||||
let env = 'SOMETHING=value ANOTHER=2'
|
||||
let stdin = ''
|
||||
let child
|
||||
|
||||
function _getEnv() {
|
||||
return env.split(' ').reduce((env, clause) => {
|
||||
let [key, value] = clause.split('=')
|
||||
return {
|
||||
...env,
|
||||
[key]: value
|
||||
}
|
||||
}, {})
|
||||
}
|
||||
|
||||
function spawn() {
|
||||
child = null
|
||||
const command = new Command(cmd, [...args, script], { cwd: cwd || null, env: _getEnv() })
|
||||
|
||||
command.on('close', data => {
|
||||
onMessage(`command finished with code ${data.code} and signal ${data.signal}`)
|
||||
child = null
|
||||
})
|
||||
command.on('error', error => onMessage(`command error: "${error}"`))
|
||||
|
||||
command.stdout.on('data', line => onMessage(`command stdout: "${line}"`))
|
||||
command.stderr.on('data', line => onMessage(`command stderr: "${line}"`))
|
||||
|
||||
command.spawn()
|
||||
.then(c => {
|
||||
child = c
|
||||
})
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function kill() {
|
||||
child.kill().then(() => onMessage('killed child process')).catch(onMessage)
|
||||
}
|
||||
|
||||
function writeToStdin() {
|
||||
child.write(stdin).catch(onMessage)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<input bind:value={script}>
|
||||
<button class="button" on:click={spawn}>Run</button>
|
||||
<button class="button" on:click={kill}>Kill</button>
|
||||
{#if child}
|
||||
<input placeholder="write to stdin" bind:value={stdin}>
|
||||
<button class="button" on:click={writeToStdin}>Write</button>
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
<input bind:value={cwd} placeholder="Working directory">
|
||||
<input class="env-vars" bind:value={env} placeholder="Environment variables">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.env-vars {
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,68 +0,0 @@
|
||||
<script>
|
||||
import { writable } from "svelte/store";
|
||||
import {
|
||||
register as registerShortcut,
|
||||
unregister as unregisterShortcut,
|
||||
unregisterAll as unregisterAllShortcuts,
|
||||
} from "@tauri-apps/api/globalShortcut";
|
||||
|
||||
export let onMessage;
|
||||
const shortcuts = writable([]);
|
||||
let shortcut = "CmdOrControl+X";
|
||||
|
||||
function register() {
|
||||
const shortcut_ = shortcut;
|
||||
registerShortcut(shortcut_, () => {
|
||||
onMessage(`Shortcut ${shortcut_} triggered`);
|
||||
})
|
||||
.then(() => {
|
||||
shortcuts.update((shortcuts_) => [...shortcuts_, shortcut_]);
|
||||
onMessage(`Shortcut ${shortcut_} registered successfully`);
|
||||
})
|
||||
.catch(onMessage);
|
||||
}
|
||||
|
||||
function unregister(shortcut) {
|
||||
const shortcut_ = shortcut;
|
||||
unregisterShortcut(shortcut_)
|
||||
.then(() => {
|
||||
shortcuts.update((shortcuts_) =>
|
||||
shortcuts_.filter((s) => s !== shortcut_)
|
||||
);
|
||||
onMessage(`Shortcut ${shortcut_} unregistered`);
|
||||
})
|
||||
.catch(onMessage);
|
||||
}
|
||||
|
||||
function unregisterAll() {
|
||||
unregisterAllShortcuts()
|
||||
.then(() => {
|
||||
shortcuts.update(() => []);
|
||||
onMessage(`Unregistered all shortcuts`);
|
||||
})
|
||||
.catch(onMessage);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<input
|
||||
placeholder="Type a shortcut with '+' as separator..."
|
||||
bind:value={shortcut}
|
||||
/>
|
||||
<button type="button" on:click={register}>Register</button>
|
||||
</div>
|
||||
<div>
|
||||
{#each $shortcuts as savedShortcut}
|
||||
<div>
|
||||
{savedShortcut}
|
||||
<button type="button" on:click={() => unregister(savedShortcut)}
|
||||
>Unregister</button
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
{#if $shortcuts.length}
|
||||
<button type="button" on:click={unregisterAll}>Unregister all</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,59 +0,0 @@
|
||||
<script>
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
|
||||
// This example show how updater events work when dialog is disabled.
|
||||
// This allow you to use custom dialog for the updater.
|
||||
// This is your responsability to restart the application after you receive the STATUS: DONE.
|
||||
|
||||
import { checkUpdate, installUpdate } from "@tauri-apps/api/updater";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { relaunch } from "@tauri-apps/api/process";
|
||||
|
||||
export let onMessage;
|
||||
let unlisten;
|
||||
|
||||
onMount(async () => {
|
||||
unlisten = await listen("tauri://update-status", onMessage)
|
||||
})
|
||||
onDestroy(() => {
|
||||
if (unlisten) {
|
||||
unlisten()
|
||||
}
|
||||
})
|
||||
|
||||
async function check() {
|
||||
try {
|
||||
document.getElementById("check_update").classList.add("hidden");
|
||||
|
||||
const {shouldUpdate, manifest} = await checkUpdate();
|
||||
onMessage(`Should update: ${shouldUpdate}`);
|
||||
onMessage(manifest);
|
||||
|
||||
if (shouldUpdate) {
|
||||
document.getElementById("start_update").classList.remove("hidden");
|
||||
}
|
||||
} catch(e) {
|
||||
onMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function install() {
|
||||
try {
|
||||
document.getElementById("start_update").classList.add("hidden");
|
||||
|
||||
await installUpdate();
|
||||
onMessage("Installation complete, restart required.");
|
||||
await relaunch();
|
||||
|
||||
} catch(e) {
|
||||
onMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<button class="button" id="check_update" on:click={check}>Check update</button>
|
||||
<button class="button hidden" id="start_update" on:click={install}>Install update</button>
|
||||
</div>
|
||||
@@ -1,53 +0,0 @@
|
||||
<script>
|
||||
import { onMount,onDestroy } from "svelte";
|
||||
export let onMessage;
|
||||
|
||||
const constraints = window.constraints = {
|
||||
audio: true,
|
||||
video: true
|
||||
};
|
||||
|
||||
function handleSuccess(stream) {
|
||||
const video = document.querySelector('video');
|
||||
const videoTracks = stream.getVideoTracks();
|
||||
onMessage('Got stream with constraints:', constraints);
|
||||
onMessage(`Using video device: ${videoTracks[0].label}`);
|
||||
window.stream = stream; // make variable available to browser console
|
||||
video.srcObject = stream;
|
||||
}
|
||||
|
||||
function handleError(error) {
|
||||
if (error.name === 'ConstraintNotSatisfiedError') {
|
||||
const v = constraints.video;
|
||||
onMessage(`The resolution ${v.width.exact}x${v.height.exact} px is not supported by your device.`);
|
||||
} else if (error.name === 'PermissionDeniedError') {
|
||||
onMessage('Permissions have not been granted to use your camera and ' +
|
||||
'microphone, you need to allow the page access to your devices in ' +
|
||||
'order for the demo to work.');
|
||||
}
|
||||
onMessage(`getUserMedia error: ${error.name}`, error);
|
||||
}
|
||||
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
handleSuccess(stream);
|
||||
} catch (e) {
|
||||
handleError(e);
|
||||
}
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
window.stream.getTracks().forEach(function(track) {
|
||||
track.stop();
|
||||
});
|
||||
})
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="alert"><p>Not available for Linux</p></div>
|
||||
<video id="localVideo" autoplay playsinline>
|
||||
<track kind="captions" />
|
||||
</video>
|
||||
</div>
|
||||
@@ -1,33 +0,0 @@
|
||||
<script>
|
||||
import { getName, getVersion, getTauriVersion } from "@tauri-apps/api/app";
|
||||
import { relaunch, exit } from "@tauri-apps/api/process";
|
||||
|
||||
let version = 0.0;
|
||||
let tauriVersion = 0.0;
|
||||
let appName = 'Unknown';
|
||||
|
||||
getName().then(n => { appName = n });
|
||||
getVersion().then(v => { version = v });
|
||||
getTauriVersion().then(v => { tauriVersion = v });
|
||||
|
||||
async function closeApp() {
|
||||
await exit();
|
||||
}
|
||||
|
||||
async function relaunchApp() {
|
||||
await relaunch();
|
||||
}
|
||||
</script>
|
||||
<h1>Welcome</h1>
|
||||
<p>
|
||||
Tauri's API capabilities using the ` @tauri-apps/api ` package. It's used as
|
||||
the main validation app, serving as the testbed of our development process. In
|
||||
the future, this app will be used on Tauri's integration tests.
|
||||
</p>
|
||||
|
||||
<p>Current App version: {version}</p>
|
||||
<p>Current Tauri version: {tauriVersion}</p>
|
||||
<p>Current App name: {appName}</p>
|
||||
|
||||
<button class="button" on:click={closeApp}>Close application</button>
|
||||
<button class="button" on:click={relaunchApp}>Relaunch application</button>
|
||||
@@ -1,410 +0,0 @@
|
||||
<script>
|
||||
import {
|
||||
appWindow,
|
||||
WebviewWindow,
|
||||
LogicalSize,
|
||||
UserAttentionType,
|
||||
PhysicalSize,
|
||||
PhysicalPosition
|
||||
} from '@tauri-apps/api/window'
|
||||
import { open as openDialog } from '@tauri-apps/api/dialog'
|
||||
import { open } from '@tauri-apps/api/shell'
|
||||
|
||||
let selectedWindow = appWindow.label
|
||||
const windowMap = {
|
||||
[selectedWindow]: appWindow
|
||||
}
|
||||
const cursorIconOptions = [
|
||||
'default',
|
||||
'crosshair',
|
||||
'hand',
|
||||
'arrow',
|
||||
'move',
|
||||
'text',
|
||||
'wait',
|
||||
'help',
|
||||
'progress',
|
||||
// something cannot be done
|
||||
'notAllowed',
|
||||
'contextMenu',
|
||||
'cell',
|
||||
'verticalText',
|
||||
'alias',
|
||||
'copy',
|
||||
'noDrop',
|
||||
// something can be grabbed
|
||||
'grab',
|
||||
/// something is grabbed
|
||||
'grabbing',
|
||||
'allScroll',
|
||||
'zoomIn',
|
||||
'zoomOut',
|
||||
// edge is to be moved
|
||||
'eResize',
|
||||
'nResize',
|
||||
'neResize',
|
||||
'nwResize',
|
||||
'sResize',
|
||||
'seResize',
|
||||
'swResize',
|
||||
'wResize',
|
||||
'ewResize',
|
||||
'nsResize',
|
||||
'neswResize',
|
||||
'nwseResize',
|
||||
'colResize',
|
||||
'rowResize'
|
||||
]
|
||||
|
||||
export let onMessage
|
||||
|
||||
let urlValue = 'https://tauri.app'
|
||||
let resizable = true
|
||||
let maximized = false
|
||||
let transparent = false
|
||||
let decorations = true
|
||||
let alwaysOnTop = false
|
||||
let fullscreen = false
|
||||
let width = 900
|
||||
let height = 700
|
||||
let minWidth = 600
|
||||
let minHeight = 600
|
||||
let maxWidth = null
|
||||
let maxHeight = null
|
||||
let x = 100
|
||||
let y = 100
|
||||
let scaleFactor = 1
|
||||
let innerPosition = new PhysicalPosition(x, y)
|
||||
let outerPosition = new PhysicalPosition(x, y)
|
||||
let innerSize = new PhysicalSize(width, height)
|
||||
let outerSize = new PhysicalSize(width, height)
|
||||
let resizeEventUnlisten
|
||||
let moveEventUnlisten
|
||||
let cursorGrab = false
|
||||
let cursorVisible = true
|
||||
let cursorX = 600
|
||||
let cursorY = 800
|
||||
let cursorIcon = 'default'
|
||||
|
||||
let windowTitle = 'Awesome Tauri Example!'
|
||||
|
||||
function openUrl() {
|
||||
open(urlValue)
|
||||
}
|
||||
|
||||
function setTitle_() {
|
||||
windowMap[selectedWindow].setTitle(windowTitle)
|
||||
}
|
||||
|
||||
function hide_() {
|
||||
windowMap[selectedWindow].hide()
|
||||
setTimeout(windowMap[selectedWindow].show, 2000)
|
||||
}
|
||||
|
||||
function minimize_() {
|
||||
windowMap[selectedWindow].minimize()
|
||||
setTimeout(windowMap[selectedWindow].unminimize, 2000)
|
||||
}
|
||||
|
||||
function getIcon() {
|
||||
openDialog({
|
||||
multiple: false
|
||||
}).then((path) => {
|
||||
if (typeof path === 'string') {
|
||||
windowMap[selectedWindow].setIcon(path)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
const label = Math.random().toString().replace('.', '')
|
||||
const webview = new WebviewWindow(label)
|
||||
windowMap[label] = webview
|
||||
webview.once('tauri://error', function () {
|
||||
onMessage('Error creating new webview')
|
||||
})
|
||||
}
|
||||
|
||||
function handleWindowResize() {
|
||||
windowMap[selectedWindow].innerSize().then((response) => {
|
||||
innerSize = response
|
||||
width = innerSize.width
|
||||
height = innerSize.height
|
||||
})
|
||||
windowMap[selectedWindow].outerSize().then((response) => {
|
||||
outerSize = response
|
||||
})
|
||||
}
|
||||
|
||||
function handleWindowMove() {
|
||||
windowMap[selectedWindow].innerPosition().then((response) => {
|
||||
innerPosition = response
|
||||
})
|
||||
windowMap[selectedWindow].outerPosition().then((response) => {
|
||||
outerPosition = response
|
||||
x = outerPosition.x
|
||||
y = outerPosition.y
|
||||
})
|
||||
}
|
||||
|
||||
async function addWindowEventListeners(window) {
|
||||
if (resizeEventUnlisten) {
|
||||
resizeEventUnlisten()
|
||||
}
|
||||
if (moveEventUnlisten) {
|
||||
moveEventUnlisten()
|
||||
}
|
||||
moveEventUnlisten = await window.listen('tauri://move', handleWindowMove)
|
||||
resizeEventUnlisten = await window.listen(
|
||||
'tauri://resize',
|
||||
handleWindowResize
|
||||
)
|
||||
}
|
||||
|
||||
async function requestUserAttention_() {
|
||||
await windowMap[selectedWindow].minimize()
|
||||
await windowMap[selectedWindow].requestUserAttention(
|
||||
UserAttentionType.Critical
|
||||
)
|
||||
await new Promise((resolve) => setTimeout(resolve, 3000))
|
||||
await windowMap[selectedWindow].requestUserAttention(null)
|
||||
}
|
||||
|
||||
$: windowMap[selectedWindow].setResizable(resizable)
|
||||
$: maximized
|
||||
? windowMap[selectedWindow].maximize()
|
||||
: windowMap[selectedWindow].unmaximize()
|
||||
$: windowMap[selectedWindow].setDecorations(decorations)
|
||||
$: windowMap[selectedWindow].setAlwaysOnTop(alwaysOnTop)
|
||||
$: windowMap[selectedWindow].setFullscreen(fullscreen)
|
||||
|
||||
$: windowMap[selectedWindow].setSize(new PhysicalSize(width, height))
|
||||
$: minWidth && minHeight
|
||||
? windowMap[selectedWindow].setMinSize(new LogicalSize(minWidth, minHeight))
|
||||
: windowMap[selectedWindow].setMinSize(null)
|
||||
$: maxWidth && maxHeight
|
||||
? windowMap[selectedWindow].setMaxSize(new LogicalSize(maxWidth, maxHeight))
|
||||
: windowMap[selectedWindow].setMaxSize(null)
|
||||
$: windowMap[selectedWindow].setPosition(new PhysicalPosition(x, y))
|
||||
$: windowMap[selectedWindow]
|
||||
.scaleFactor()
|
||||
.then((factor) => (scaleFactor = factor))
|
||||
$: addWindowEventListeners(windowMap[selectedWindow])
|
||||
|
||||
$: windowMap[selectedWindow].setCursorGrab(cursorGrab)
|
||||
$: windowMap[selectedWindow].setCursorVisible(cursorVisible)
|
||||
$: windowMap[selectedWindow].setCursorIcon(cursorIcon)
|
||||
$: windowMap[selectedWindow].setCursorPosition(
|
||||
new PhysicalPosition(cursorX, cursorY)
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="flex col">
|
||||
<select class="button" bind:value={selectedWindow}>
|
||||
{#each Object.keys(windowMap) as label}
|
||||
<option value={label}>{label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<div>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={resizable} />
|
||||
Resizable
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={maximized} />
|
||||
Maximize
|
||||
</label>
|
||||
<button
|
||||
title="Unminimizes after 2 seconds"
|
||||
on:click={() => windowMap[selectedWindow].center()}
|
||||
>
|
||||
Center
|
||||
</button>
|
||||
<button title="Unminimizes after 2 seconds" on:click={minimize_}>
|
||||
Minimize
|
||||
</button>
|
||||
<button title="Visible again after 2 seconds" on:click={hide_}>
|
||||
Hide
|
||||
</button>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={transparent} />
|
||||
Transparent
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={decorations} />
|
||||
Has decorations
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={alwaysOnTop} />
|
||||
Always on top
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={fullscreen} />
|
||||
Fullscreen
|
||||
</label>
|
||||
<button on:click={getIcon}> Change icon </button>
|
||||
</div>
|
||||
<div>
|
||||
<div class="window-controls flex flex-row">
|
||||
<div class="flex col grow">
|
||||
<div>
|
||||
X
|
||||
<input type="number" bind:value={x} min="0" />
|
||||
</div>
|
||||
<div>
|
||||
Y
|
||||
<input type="number" bind:value={y} min="0" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex col grow">
|
||||
<div>
|
||||
Width
|
||||
<input type="number" bind:value={width} min="400" />
|
||||
</div>
|
||||
<div>
|
||||
Height
|
||||
<input type="number" bind:value={height} min="400" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex col grow">
|
||||
<div>
|
||||
Min width
|
||||
<input type="number" bind:value={minWidth} />
|
||||
</div>
|
||||
<div>
|
||||
Min height
|
||||
<input type="number" bind:value={minHeight} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex col grow">
|
||||
<div>
|
||||
Max width
|
||||
<input type="number" bind:value={maxWidth} min="400" />
|
||||
</div>
|
||||
<div>
|
||||
Max height
|
||||
<input type="number" bind:value={maxHeight} min="400" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex">
|
||||
<div class="grow window-property">
|
||||
<div>Inner Size</div>
|
||||
<span>Width: {innerSize.width}</span>
|
||||
<span>Height: {innerSize.height}</span>
|
||||
</div>
|
||||
<div class="grow window-property">
|
||||
<div>Outer Size</div>
|
||||
<span>Width: {outerSize.width}</span>
|
||||
<span>Height: {outerSize.height}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="grow window-property">
|
||||
<div>Inner Logical Size</div>
|
||||
<span>Width: {innerSize.toLogical(scaleFactor).width}</span>
|
||||
<span>Height: {innerSize.toLogical(scaleFactor).height}</span>
|
||||
</div>
|
||||
<div class="grow window-property">
|
||||
<div>Outer Logical Size</div>
|
||||
<span>Width: {outerSize.toLogical(scaleFactor).width}</span>
|
||||
<span>Height: {outerSize.toLogical(scaleFactor).height}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="grow window-property">
|
||||
<div>Inner Position</div>
|
||||
<span>x: {innerPosition.x}</span>
|
||||
<span>y: {innerPosition.y}</span>
|
||||
</div>
|
||||
<div class="grow window-property">
|
||||
<div>Outer Position</div>
|
||||
<span>x: {outerPosition.x}</span>
|
||||
<span>y: {outerPosition.y}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="grow window-property">
|
||||
<div>Inner Logical Position</div>
|
||||
<span>x: {innerPosition.toLogical(scaleFactor).x}</span>
|
||||
<span>y: {innerPosition.toLogical(scaleFactor).y}</span>
|
||||
</div>
|
||||
<div class="grow window-property">
|
||||
<div>Outer Logical Position</div>
|
||||
<span>x: {outerPosition.toLogical(scaleFactor).x}</span>
|
||||
<span>y: {outerPosition.toLogical(scaleFactor).y}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Cursor</h4>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={cursorGrab} />
|
||||
Grab
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={cursorVisible} />
|
||||
Visible
|
||||
</label>
|
||||
<select class="button" bind:value={cursorIcon}>
|
||||
{#each cursorIconOptions as kind}
|
||||
<option value={kind}>{kind}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<div class="flex col grow">
|
||||
<div>
|
||||
X position
|
||||
<input type="number" bind:value={cursorX} />
|
||||
</div>
|
||||
<div>
|
||||
Y position
|
||||
<input type="number" bind:value={cursorY} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form on:submit|preventDefault={setTitle_}>
|
||||
<input id="title" bind:value={windowTitle} />
|
||||
<button class="button" type="submit">Set title</button>
|
||||
</form>
|
||||
<form on:submit|preventDefault={openUrl}>
|
||||
<input id="url" bind:value={urlValue} />
|
||||
<button class="button" id="open-url"> Open URL </button>
|
||||
</form>
|
||||
<button
|
||||
class="button"
|
||||
on:click={requestUserAttention_}
|
||||
title="Minimizes the window, requests attention for 3s and then resets it"
|
||||
>Request attention</button
|
||||
>
|
||||
<button class="button" on:click={createWindow}>New window</button>
|
||||
|
||||
<style>
|
||||
form {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.grow {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.window-controls input {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.window-property {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.window-property span {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'uno.css'
|
||||
import './app.css'
|
||||
import App from './App.svelte'
|
||||
|
||||
const app = new App({
|
||||
target: document.body
|
||||
target: document.querySelector('#app')
|
||||
})
|
||||
|
||||
export default app
|
||||
|
||||
29
examples/api/src/views/Cli.svelte
Normal file
29
examples/api/src/views/Cli.svelte
Normal file
@@ -0,0 +1,29 @@
|
||||
<script>
|
||||
import { getMatches } from '@tauri-apps/api/cli'
|
||||
|
||||
export let onMessage
|
||||
|
||||
function cliMatches() {
|
||||
getMatches().then(onMessage).catch(onMessage)
|
||||
}
|
||||
</script>
|
||||
|
||||
<p>
|
||||
This binary can be run from the terminal and takes the following arguments:
|
||||
<code class="code-block flex flex-wrap my-2">
|
||||
<pre>
|
||||
--config <PATH>
|
||||
--theme <light|dark|system>
|
||||
--verbose</pre>
|
||||
</code>
|
||||
Additionally, it has a <code>update --background</code> subcommand.
|
||||
</p>
|
||||
<br />
|
||||
<div class="note">
|
||||
Note that the arguments are only parsed, not implemented.
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<button class="btn" id="cli-matches" on:click={cliMatches}>
|
||||
Get matches
|
||||
</button>
|
||||
32
examples/api/src/views/Clipboard.svelte
Normal file
32
examples/api/src/views/Clipboard.svelte
Normal file
@@ -0,0 +1,32 @@
|
||||
<script>
|
||||
import { writeText, readText } from '@tauri-apps/api/clipboard'
|
||||
|
||||
export let onMessage
|
||||
let text = 'clipboard message'
|
||||
|
||||
function write() {
|
||||
writeText(text)
|
||||
.then(() => {
|
||||
onMessage('Wrote to the clipboard')
|
||||
})
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function read() {
|
||||
readText()
|
||||
.then((contents) => {
|
||||
onMessage(`Clipboard contents: ${contents}`)
|
||||
})
|
||||
.catch(onMessage)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex gap-1">
|
||||
<input
|
||||
class="grow input"
|
||||
placeholder="Text to write to the clipboard"
|
||||
bind:value={text}
|
||||
/>
|
||||
<button class="btn" type="button" on:click={write}>Write</button>
|
||||
<button class="btn" type="button" on:click={read}>Read</button>
|
||||
</div>
|
||||
50
examples/api/src/views/Communication.svelte
Normal file
50
examples/api/src/views/Communication.svelte
Normal file
@@ -0,0 +1,50 @@
|
||||
<script>
|
||||
import { listen, emit } from '@tauri-apps/api/event'
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
|
||||
export let onMessage
|
||||
let unlisten
|
||||
|
||||
onMount(async () => {
|
||||
unlisten = await listen('rust-event', onMessage)
|
||||
})
|
||||
onDestroy(() => {
|
||||
if (unlisten) {
|
||||
unlisten()
|
||||
}
|
||||
})
|
||||
|
||||
function log() {
|
||||
invoke('log_operation', {
|
||||
event: 'tauri-click',
|
||||
payload: 'this payload is optional because we used Option in Rust'
|
||||
})
|
||||
}
|
||||
|
||||
function performRequest() {
|
||||
invoke('perform_request', {
|
||||
endpoint: 'dummy endpoint arg',
|
||||
body: {
|
||||
id: 5,
|
||||
name: 'test'
|
||||
}
|
||||
})
|
||||
.then(onMessage)
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function emitEvent() {
|
||||
emit('js-event', 'this is the payload string')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<button class="btn" id="log" on:click={log}>Call Log API</button>
|
||||
<button class="btn" id="request" on:click={performRequest}>
|
||||
Call Request (async) API
|
||||
</button>
|
||||
<button class="btn" id="event" on:click={emitEvent}>
|
||||
Send event to Rust
|
||||
</button>
|
||||
</div>
|
||||
118
examples/api/src/views/Dialog.svelte
Normal file
118
examples/api/src/views/Dialog.svelte
Normal file
@@ -0,0 +1,118 @@
|
||||
<script>
|
||||
import { open, save } from '@tauri-apps/api/dialog'
|
||||
import { readBinaryFile } from '@tauri-apps/api/fs'
|
||||
|
||||
export let onMessage
|
||||
export let insecureRenderHtml
|
||||
let defaultPath = null
|
||||
let filter = null
|
||||
let multiple = false
|
||||
let directory = false
|
||||
|
||||
function arrayBufferToBase64(buffer, callback) {
|
||||
var blob = new Blob([buffer], {
|
||||
type: 'application/octet-binary'
|
||||
})
|
||||
var reader = new FileReader()
|
||||
reader.onload = function (evt) {
|
||||
var dataurl = evt.target.result
|
||||
callback(dataurl.substr(dataurl.indexOf(',') + 1))
|
||||
}
|
||||
reader.readAsDataURL(blob)
|
||||
}
|
||||
|
||||
function openDialog() {
|
||||
open({
|
||||
title: 'My wonderful open dialog',
|
||||
defaultPath,
|
||||
filters: filter
|
||||
? [
|
||||
{
|
||||
name: 'Tauri Example',
|
||||
extensions: filter.split(',').map((f) => f.trim())
|
||||
}
|
||||
]
|
||||
: [],
|
||||
multiple,
|
||||
directory
|
||||
})
|
||||
.then(function (res) {
|
||||
if (Array.isArray(res)) {
|
||||
onMessage(res)
|
||||
} else {
|
||||
var pathToRead = res
|
||||
var isFile = pathToRead.match(/\S+\.\S+$/g)
|
||||
readBinaryFile(pathToRead)
|
||||
.then(function (response) {
|
||||
if (isFile) {
|
||||
if (
|
||||
pathToRead.includes('.png') ||
|
||||
pathToRead.includes('.jpg')
|
||||
) {
|
||||
arrayBufferToBase64(
|
||||
new Uint8Array(response),
|
||||
function (base64) {
|
||||
var src = 'data:image/png;base64,' + base64
|
||||
insecureRenderHtml('<img src="' + src + '"></img>')
|
||||
}
|
||||
)
|
||||
} else {
|
||||
onMessage(res)
|
||||
}
|
||||
} else {
|
||||
onMessage(res)
|
||||
}
|
||||
})
|
||||
.catch(onMessage(res))
|
||||
}
|
||||
})
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function saveDialog() {
|
||||
save({
|
||||
title: 'My wonderful save dialog',
|
||||
defaultPath,
|
||||
filters: filter
|
||||
? [
|
||||
{
|
||||
name: 'Tauri Example',
|
||||
extensions: filter.split(',').map((f) => f.trim())
|
||||
}
|
||||
]
|
||||
: []
|
||||
})
|
||||
.then(onMessage)
|
||||
.catch(onMessage)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex gap-2 children:grow">
|
||||
<input
|
||||
class="input"
|
||||
id="dialog-default-path"
|
||||
placeholder="Default path"
|
||||
bind:value={defaultPath}
|
||||
/>
|
||||
<input
|
||||
class="input"
|
||||
id="dialog-filter"
|
||||
placeholder="Extensions filter, comma-separated"
|
||||
bind:value={filter}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<div>
|
||||
<input type="checkbox" id="dialog-multiple" bind:checked={multiple} />
|
||||
<label for="dialog-multiple">Multiple</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="dialog-directory" bind:checked={directory} />
|
||||
<label for="dialog-directory">Directory</label>
|
||||
</div>
|
||||
<br />
|
||||
<button class="btn" id="open-dialog" on:click={openDialog}>Open dialog</button>
|
||||
<button class="btn" id="save-dialog" on:click={saveDialog}
|
||||
>Open save dialog</button
|
||||
>
|
||||
@@ -79,20 +79,28 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={read}>
|
||||
<select class="button" id="dir">
|
||||
<option value="">None</option>
|
||||
{#each DirOptions as dir}
|
||||
<option value={dir[1]}>{dir[0]}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<input
|
||||
id="path-to-read"
|
||||
placeholder="Type the path to read..."
|
||||
bind:value={pathToRead}
|
||||
/>
|
||||
<button class="button" id="read">Read</button>
|
||||
<button class="button" type="button" on:click={setSrc}>Use as img src</button>
|
||||
|
||||
<img alt="file" bind:this={img} />
|
||||
<form class="flex flex-col" on:submit|preventDefault={read}>
|
||||
<div class="flex gap-1">
|
||||
<select class="input" id="dir">
|
||||
<option value="">None</option>
|
||||
{#each DirOptions as dir}
|
||||
<option value={dir[1]}>{dir[0]}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<input
|
||||
class="input grow"
|
||||
id="path-to-read"
|
||||
placeholder="Type the path to read..."
|
||||
bind:value={pathToRead}
|
||||
/>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<button class="btn" id="read">Read</button>
|
||||
<button class="btn" type="button" on:click={setSrc}>Use as img src</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<br />
|
||||
|
||||
<img alt="" bind:this={img} />
|
||||
99
examples/api/src/views/Http.svelte
Normal file
99
examples/api/src/views/Http.svelte
Normal file
@@ -0,0 +1,99 @@
|
||||
<script>
|
||||
import { getClient, Body, ResponseType } from '@tauri-apps/api/http'
|
||||
import { JsonView } from '@zerodevx/svelte-json-view'
|
||||
|
||||
let httpMethod = 'GET'
|
||||
let httpBody = ''
|
||||
|
||||
export let onMessage
|
||||
|
||||
async function makeHttpRequest() {
|
||||
const client = await getClient().catch((e) => {
|
||||
onMessage(e)
|
||||
throw e
|
||||
})
|
||||
let method = httpMethod || 'GET'
|
||||
|
||||
const options = {
|
||||
url: 'http://localhost:3003',
|
||||
method: method || 'GET'
|
||||
}
|
||||
|
||||
if (
|
||||
(httpBody.startsWith('{') && httpBody.endsWith('}')) ||
|
||||
(httpBody.startsWith('[') && httpBody.endsWith(']'))
|
||||
) {
|
||||
options.body = Body.json(JSON.parse(httpBody))
|
||||
} else if (httpBody !== '') {
|
||||
options.body = Body.text(httpBody)
|
||||
}
|
||||
|
||||
client.request(options).then(onMessage).catch(onMessage)
|
||||
}
|
||||
|
||||
/// http form
|
||||
let foo = 'baz'
|
||||
let bar = 'qux'
|
||||
let result = null
|
||||
let multipart = true
|
||||
|
||||
async function doPost() {
|
||||
const client = await getClient().catch((e) => {
|
||||
onMessage(e)
|
||||
throw e
|
||||
})
|
||||
|
||||
result = await client.request({
|
||||
url: 'http://localhost:3003',
|
||||
method: 'POST',
|
||||
body: Body.form({
|
||||
foo,
|
||||
bar
|
||||
}),
|
||||
headers: multipart
|
||||
? { 'Content-Type': 'multipart/form-data' }
|
||||
: undefined,
|
||||
responseType: ResponseType.Text
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={makeHttpRequest}>
|
||||
<select class="input" id="request-method" bind:value={httpMethod}>
|
||||
<option value="GET">GET</option>
|
||||
<option value="POST">POST</option>
|
||||
<option value="PUT">PUT</option>
|
||||
<option value="PATCH">PATCH</option>
|
||||
<option value="DELETE">DELETE</option>
|
||||
</select>
|
||||
<br />
|
||||
<textarea
|
||||
class="input h-auto w-100%"
|
||||
id="request-body"
|
||||
placeholder="Request body"
|
||||
rows="5"
|
||||
bind:value={httpBody}
|
||||
/>
|
||||
<br />
|
||||
<button class="btn" id="make-request"> Make request </button>
|
||||
</form>
|
||||
|
||||
<br />
|
||||
|
||||
<h3>HTTP Form</h3>
|
||||
|
||||
<div class="flex gap-2 children:grow">
|
||||
<input class="input" bind:value={foo} />
|
||||
<input class="input" bind:value={bar} />
|
||||
</div>
|
||||
<br />
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={multipart} />
|
||||
Multipart
|
||||
</label>
|
||||
<br />
|
||||
<br />
|
||||
<button class="btn" type="button" on:click={doPost}> Post it</button>
|
||||
<br />
|
||||
<br />
|
||||
<JsonView json={result} />
|
||||
34
examples/api/src/views/Notifications.svelte
Normal file
34
examples/api/src/views/Notifications.svelte
Normal file
@@ -0,0 +1,34 @@
|
||||
<script>
|
||||
export let onMessage
|
||||
|
||||
// send the notification directly
|
||||
// the backend is responsible for checking the permission
|
||||
function _sendNotification() {
|
||||
new Notification('Notification title', {
|
||||
body: 'This is the notification body'
|
||||
})
|
||||
}
|
||||
|
||||
// alternatively, check the permission ourselves
|
||||
function sendNotification() {
|
||||
if (Notification.permission === 'default') {
|
||||
Notification.requestPermission()
|
||||
.then(function (response) {
|
||||
if (response === 'granted') {
|
||||
_sendNotification()
|
||||
} else {
|
||||
onMessage('Permission is ' + response)
|
||||
}
|
||||
})
|
||||
.catch(onMessage)
|
||||
} else if (Notification.permission === 'granted') {
|
||||
_sendNotification()
|
||||
} else {
|
||||
onMessage('Permission is denied')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<button class="btn" id="notification" on:click={_sendNotification}>
|
||||
Send test notification
|
||||
</button>
|
||||
93
examples/api/src/views/Shell.svelte
Normal file
93
examples/api/src/views/Shell.svelte
Normal file
@@ -0,0 +1,93 @@
|
||||
<script>
|
||||
import { Command } from '@tauri-apps/api/shell'
|
||||
const windows = navigator.userAgent.includes('Windows')
|
||||
let cmd = windows ? 'cmd' : 'sh'
|
||||
let args = windows ? ['/C'] : ['-c']
|
||||
|
||||
export let onMessage
|
||||
|
||||
let script = 'echo "hello world"'
|
||||
let cwd = null
|
||||
let env = 'SOMETHING=value ANOTHER=2'
|
||||
let stdin = ''
|
||||
let child
|
||||
|
||||
function _getEnv() {
|
||||
return env.split(' ').reduce((env, clause) => {
|
||||
let [key, value] = clause.split('=')
|
||||
return {
|
||||
...env,
|
||||
[key]: value
|
||||
}
|
||||
}, {})
|
||||
}
|
||||
|
||||
function spawn() {
|
||||
child = null
|
||||
const command = new Command(cmd, [...args, script], {
|
||||
cwd: cwd || null,
|
||||
env: _getEnv()
|
||||
})
|
||||
|
||||
command.on('close', (data) => {
|
||||
onMessage(
|
||||
`command finished with code ${data.code} and signal ${data.signal}`
|
||||
)
|
||||
child = null
|
||||
})
|
||||
command.on('error', (error) => onMessage(`command error: "${error}"`))
|
||||
|
||||
command.stdout.on('data', (line) => onMessage(`command stdout: "${line}"`))
|
||||
command.stderr.on('data', (line) => onMessage(`command stderr: "${line}"`))
|
||||
|
||||
command
|
||||
.spawn()
|
||||
.then((c) => {
|
||||
child = c
|
||||
})
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function kill() {
|
||||
child
|
||||
.kill()
|
||||
.then(() => onMessage('killed child process'))
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function writeToStdin() {
|
||||
child.write(stdin).catch(onMessage)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col childre:grow gap-1">
|
||||
<div class="flex items-center gap-1">
|
||||
Script:
|
||||
<input class="grow input" bind:value={script} />
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
Working directory:
|
||||
<input
|
||||
class="grow input"
|
||||
bind:value={cwd}
|
||||
placeholder="Working directory"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
Arguments:
|
||||
<input
|
||||
class="grow input"
|
||||
bind:value={env}
|
||||
placeholder="Environment variables"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex children:grow gap-1">
|
||||
<button class="btn" on:click={spawn}>Run</button>
|
||||
<button class="btn" on:click={kill}>Kill</button>
|
||||
</div>
|
||||
{#if child}
|
||||
<br />
|
||||
<input class="input" placeholder="write to stdin" bind:value={stdin} />
|
||||
<button class="btn" on:click={writeToStdin}>Write</button>
|
||||
{/if}
|
||||
</div>
|
||||
73
examples/api/src/views/Shortcuts.svelte
Normal file
73
examples/api/src/views/Shortcuts.svelte
Normal file
@@ -0,0 +1,73 @@
|
||||
<script>
|
||||
import { writable } from 'svelte/store'
|
||||
import {
|
||||
register as registerShortcut,
|
||||
unregister as unregisterShortcut,
|
||||
unregisterAll as unregisterAllShortcuts
|
||||
} from '@tauri-apps/api/globalShortcut'
|
||||
|
||||
export let onMessage
|
||||
const shortcuts = writable([])
|
||||
let shortcut = 'CmdOrControl+X'
|
||||
|
||||
function register() {
|
||||
const shortcut_ = shortcut
|
||||
registerShortcut(shortcut_, () => {
|
||||
onMessage(`Shortcut ${shortcut_} triggered`)
|
||||
})
|
||||
.then(() => {
|
||||
shortcuts.update((shortcuts_) => [...shortcuts_, shortcut_])
|
||||
onMessage(`Shortcut ${shortcut_} registered successfully`)
|
||||
})
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function unregister(shortcut) {
|
||||
const shortcut_ = shortcut
|
||||
unregisterShortcut(shortcut_)
|
||||
.then(() => {
|
||||
shortcuts.update((shortcuts_) =>
|
||||
shortcuts_.filter((s) => s !== shortcut_)
|
||||
)
|
||||
onMessage(`Shortcut ${shortcut_} unregistered`)
|
||||
})
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function unregisterAll() {
|
||||
unregisterAllShortcuts()
|
||||
.then(() => {
|
||||
shortcuts.update(() => [])
|
||||
onMessage(`Unregistered all shortcuts`)
|
||||
})
|
||||
.catch(onMessage)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex gap-1">
|
||||
<input
|
||||
class="input grow"
|
||||
placeholder="Type a shortcut with '+' as separator..."
|
||||
bind:value={shortcut}
|
||||
/>
|
||||
<button class="btn" type="button" on:click={register}>Register</button>
|
||||
</div>
|
||||
<br />
|
||||
<div class="flex flex-col gap-1">
|
||||
{#each $shortcuts as savedShortcut}
|
||||
<div class="flex justify-between">
|
||||
{savedShortcut}
|
||||
<button
|
||||
class="btn"
|
||||
type="button"
|
||||
on:click={() => unregister(savedShortcut)}>Unregister</button
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
{#if $shortcuts.length > 1}
|
||||
<br />
|
||||
<button class="btn" type="button" on:click={unregisterAll}
|
||||
>Unregister all</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
76
examples/api/src/views/Updater.svelte
Normal file
76
examples/api/src/views/Updater.svelte
Normal file
@@ -0,0 +1,76 @@
|
||||
<script>
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
|
||||
// This example show how updater events work when dialog is disabled.
|
||||
// This allow you to use custom dialog for the updater.
|
||||
// This is your responsability to restart the application after you receive the STATUS: DONE.
|
||||
|
||||
import { checkUpdate, installUpdate } from '@tauri-apps/api/updater'
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
import { relaunch } from '@tauri-apps/api/process'
|
||||
|
||||
export let onMessage
|
||||
let unlisten
|
||||
|
||||
onMount(async () => {
|
||||
unlisten = await listen('tauri://update-status', onMessage)
|
||||
})
|
||||
onDestroy(() => {
|
||||
if (unlisten) {
|
||||
unlisten()
|
||||
}
|
||||
})
|
||||
|
||||
let isChecking, isInstalling, newUpdate
|
||||
|
||||
async function check() {
|
||||
isChecking = true
|
||||
try {
|
||||
const { shouldUpdate, manifest } = await checkUpdate()
|
||||
onMessage(`Should update: ${shouldUpdate}`)
|
||||
onMessage(manifest)
|
||||
|
||||
newUpdate = shouldUpdate
|
||||
} catch (e) {
|
||||
onMessage(e)
|
||||
} finally {
|
||||
isChecking = false
|
||||
}
|
||||
}
|
||||
|
||||
async function install() {
|
||||
isInstalling = true
|
||||
try {
|
||||
await installUpdate()
|
||||
onMessage('Installation complete, restart required.')
|
||||
await relaunch()
|
||||
} catch (e) {
|
||||
onMessage(e)
|
||||
} finally {
|
||||
isInstalling = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex children:grow children:h10">
|
||||
{#if !isChecking && !newUpdate}
|
||||
<button class="btn" on:click={check}>Check update</button>
|
||||
{:else if !isInstalling && newUpdate}
|
||||
<button class="btn" on:click={install}>Install update</button>
|
||||
{:else}
|
||||
<button
|
||||
class="btn text-accentText dark:text-darkAccentText flex items-center justify-center"
|
||||
><div class="spinner animate-spin" /></button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.spinner {
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
border-radius: 50rem;
|
||||
color: currentColor;
|
||||
border: 2px dashed currentColor;
|
||||
}
|
||||
</style>
|
||||
56
examples/api/src/views/WebRTC.svelte
Normal file
56
examples/api/src/views/WebRTC.svelte
Normal file
@@ -0,0 +1,56 @@
|
||||
<script>
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
export let onMessage
|
||||
|
||||
const constraints = (window.constraints = {
|
||||
audio: true,
|
||||
video: true
|
||||
})
|
||||
|
||||
function handleSuccess(stream) {
|
||||
const video = document.querySelector('video')
|
||||
const videoTracks = stream.getVideoTracks()
|
||||
onMessage('Got stream with constraints:', constraints)
|
||||
onMessage(`Using video device: ${videoTracks[0].label}`)
|
||||
window.stream = stream // make variable available to browser console
|
||||
video.srcObject = stream
|
||||
}
|
||||
|
||||
function handleError(error) {
|
||||
if (error.name === 'ConstraintNotSatisfiedError') {
|
||||
const v = constraints.video
|
||||
onMessage(
|
||||
`The resolution ${v.width.exact}x${v.height.exact} px is not supported by your device.`
|
||||
)
|
||||
} else if (error.name === 'PermissionDeniedError') {
|
||||
onMessage(
|
||||
'Permissions have not been granted to use your camera and ' +
|
||||
'microphone, you need to allow the page access to your devices in ' +
|
||||
'order for the demo to work.'
|
||||
)
|
||||
}
|
||||
onMessage(`getUserMedia error: ${error.name}`, error)
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia(constraints)
|
||||
handleSuccess(stream)
|
||||
} catch (e) {
|
||||
handleError(e)
|
||||
}
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
window.stream.getTracks().forEach(function (track) {
|
||||
track.stop()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="note-red grow">Not available for Linux</div>
|
||||
<video id="localVideo" autoplay playsinline>
|
||||
<track kind="captions" />
|
||||
</video>
|
||||
</div>
|
||||
47
examples/api/src/views/Welcome.svelte
Normal file
47
examples/api/src/views/Welcome.svelte
Normal file
@@ -0,0 +1,47 @@
|
||||
<script>
|
||||
import { getName, getVersion, getTauriVersion } from '@tauri-apps/api/app'
|
||||
import { relaunch, exit } from '@tauri-apps/api/process'
|
||||
|
||||
let version = '0.0.0'
|
||||
let tauriVersion = '0.0.0'
|
||||
let appName = 'Unknown'
|
||||
|
||||
getName().then((n) => {
|
||||
appName = n
|
||||
})
|
||||
getVersion().then((v) => {
|
||||
version = v
|
||||
})
|
||||
getTauriVersion().then((v) => {
|
||||
tauriVersion = v
|
||||
})
|
||||
|
||||
async function closeApp() {
|
||||
await exit()
|
||||
}
|
||||
|
||||
async function relaunchApp() {
|
||||
await relaunch()
|
||||
}
|
||||
</script>
|
||||
|
||||
<p>
|
||||
This is a demo of Tauri's API capabilities using the <code
|
||||
>@tauri-apps/api</code
|
||||
> package. It's used as the main validation app, serving as the test bed of our
|
||||
development process. In the future, this app will be used on Tauri's integration
|
||||
tests.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<pre>
|
||||
App name: <code>{appName}</code>
|
||||
App version: <code>{version}</code>
|
||||
Tauri version: <code>{tauriVersion}</code>
|
||||
</pre>
|
||||
<br />
|
||||
<div class="flex flex-wrap gap-1 shadow-">
|
||||
<button class="btn" on:click={closeApp}>Close application</button>
|
||||
<button class="btn" on:click={relaunchApp}>Relaunch application</button>
|
||||
</div>
|
||||
447
examples/api/src/views/Window.svelte
Normal file
447
examples/api/src/views/Window.svelte
Normal file
@@ -0,0 +1,447 @@
|
||||
<script>
|
||||
import {
|
||||
appWindow,
|
||||
WebviewWindow,
|
||||
LogicalSize,
|
||||
UserAttentionType,
|
||||
PhysicalSize,
|
||||
PhysicalPosition
|
||||
} from '@tauri-apps/api/window'
|
||||
import { open as openDialog } from '@tauri-apps/api/dialog'
|
||||
import { open } from '@tauri-apps/api/shell'
|
||||
|
||||
let selectedWindow = appWindow.label
|
||||
const windowMap = {
|
||||
[appWindow.label]: appWindow
|
||||
}
|
||||
|
||||
const cursorIconOptions = [
|
||||
'default',
|
||||
'crosshair',
|
||||
'hand',
|
||||
'arrow',
|
||||
'move',
|
||||
'text',
|
||||
'wait',
|
||||
'help',
|
||||
'progress',
|
||||
// something cannot be done
|
||||
'notAllowed',
|
||||
'contextMenu',
|
||||
'cell',
|
||||
'verticalText',
|
||||
'alias',
|
||||
'copy',
|
||||
'noDrop',
|
||||
// something can be grabbed
|
||||
'grab',
|
||||
/// something is grabbed
|
||||
'grabbing',
|
||||
'allScroll',
|
||||
'zoomIn',
|
||||
'zoomOut',
|
||||
// edge is to be moved
|
||||
'eResize',
|
||||
'nResize',
|
||||
'neResize',
|
||||
'nwResize',
|
||||
'sResize',
|
||||
'seResize',
|
||||
'swResize',
|
||||
'wResize',
|
||||
'ewResize',
|
||||
'nsResize',
|
||||
'neswResize',
|
||||
'nwseResize',
|
||||
'colResize',
|
||||
'rowResize'
|
||||
]
|
||||
|
||||
export let onMessage
|
||||
|
||||
let newWindowLabel
|
||||
|
||||
let urlValue = 'https://tauri.app'
|
||||
let resizable = true
|
||||
let maximized = false
|
||||
let decorations = true
|
||||
let alwaysOnTop = false
|
||||
let fullscreen = false
|
||||
let width = null
|
||||
let height = null
|
||||
let minWidth = null
|
||||
let minHeight = null
|
||||
let maxWidth = null
|
||||
let maxHeight = null
|
||||
let x = null
|
||||
let y = null
|
||||
let scaleFactor = 1
|
||||
let innerPosition = new PhysicalPosition(x, y)
|
||||
let outerPosition = new PhysicalPosition(x, y)
|
||||
let innerSize = new PhysicalSize(width, height)
|
||||
let outerSize = new PhysicalSize(width, height)
|
||||
let resizeEventUnlisten
|
||||
let moveEventUnlisten
|
||||
let cursorGrab = false
|
||||
let cursorVisible = true
|
||||
let cursorX = null
|
||||
let cursorY = null
|
||||
let cursorIcon = 'default'
|
||||
let windowTitle = 'Awesome Tauri Example!'
|
||||
|
||||
function openUrl() {
|
||||
open(urlValue)
|
||||
}
|
||||
|
||||
function setTitle_() {
|
||||
windowMap[selectedWindow].setTitle(windowTitle)
|
||||
}
|
||||
|
||||
function hide_() {
|
||||
windowMap[selectedWindow].hide()
|
||||
setTimeout(windowMap[selectedWindow].show, 2000)
|
||||
}
|
||||
|
||||
function minimize_() {
|
||||
windowMap[selectedWindow].minimize()
|
||||
setTimeout(windowMap[selectedWindow].unminimize, 2000)
|
||||
}
|
||||
|
||||
function getIcon() {
|
||||
openDialog({
|
||||
multiple: false
|
||||
}).then((path) => {
|
||||
if (typeof path === 'string') {
|
||||
windowMap[selectedWindow].setIcon(path)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
if (!newWindowLabel) return
|
||||
|
||||
const webview = new WebviewWindow(newWindowLabel)
|
||||
windowMap[newWindowLabel] = webview
|
||||
webview.once('tauri://error', function () {
|
||||
onMessage('Error creating new webview')
|
||||
})
|
||||
}
|
||||
|
||||
function loadWindowSize() {
|
||||
windowMap[selectedWindow].innerSize().then((response) => {
|
||||
innerSize = response
|
||||
width = innerSize.width
|
||||
height = innerSize.height
|
||||
})
|
||||
windowMap[selectedWindow].outerSize().then((response) => {
|
||||
outerSize = response
|
||||
})
|
||||
}
|
||||
|
||||
function loadWindowPosition() {
|
||||
windowMap[selectedWindow].innerPosition().then((response) => {
|
||||
innerPosition = response
|
||||
})
|
||||
windowMap[selectedWindow].outerPosition().then((response) => {
|
||||
outerPosition = response
|
||||
x = outerPosition.x
|
||||
y = outerPosition.y
|
||||
})
|
||||
}
|
||||
|
||||
async function addWindowEventListeners(window) {
|
||||
if (!window) return
|
||||
if (resizeEventUnlisten) {
|
||||
resizeEventUnlisten()
|
||||
}
|
||||
if (moveEventUnlisten) {
|
||||
moveEventUnlisten()
|
||||
}
|
||||
moveEventUnlisten = await window.listen('tauri://move', loadWindowPosition)
|
||||
resizeEventUnlisten = await window.listen('tauri://resize', loadWindowSize)
|
||||
}
|
||||
|
||||
async function requestUserAttention_() {
|
||||
await windowMap[selectedWindow].minimize()
|
||||
await windowMap[selectedWindow].requestUserAttention(
|
||||
UserAttentionType.Critical
|
||||
)
|
||||
await new Promise((resolve) => setTimeout(resolve, 3000))
|
||||
await windowMap[selectedWindow].requestUserAttention(null)
|
||||
}
|
||||
|
||||
$: {
|
||||
windowMap[selectedWindow]
|
||||
loadWindowPosition()
|
||||
loadWindowSize()
|
||||
}
|
||||
$: windowMap[selectedWindow]?.setResizable(resizable)
|
||||
$: maximized
|
||||
? windowMap[selectedWindow]?.maximize()
|
||||
: windowMap[selectedWindow]?.unmaximize()
|
||||
$: windowMap[selectedWindow]?.setDecorations(decorations)
|
||||
$: windowMap[selectedWindow]?.setAlwaysOnTop(alwaysOnTop)
|
||||
$: windowMap[selectedWindow]?.setFullscreen(fullscreen)
|
||||
|
||||
$: width &&
|
||||
height &&
|
||||
windowMap[selectedWindow]?.setSize(new PhysicalSize(width, height))
|
||||
$: minWidth && minHeight
|
||||
? windowMap[selectedWindow]?.setMinSize(
|
||||
new LogicalSize(minWidth, minHeight)
|
||||
)
|
||||
: windowMap[selectedWindow]?.setMinSize(null)
|
||||
$: maxWidth > 800 && maxHeight > 400
|
||||
? windowMap[selectedWindow]?.setMaxSize(
|
||||
new LogicalSize(maxWidth, maxHeight)
|
||||
)
|
||||
: windowMap[selectedWindow]?.setMaxSize(null)
|
||||
$: x !== null &&
|
||||
y !== null &&
|
||||
windowMap[selectedWindow]?.setPosition(new PhysicalPosition(x, y))
|
||||
$: windowMap[selectedWindow]
|
||||
?.scaleFactor()
|
||||
.then((factor) => (scaleFactor = factor))
|
||||
$: addWindowEventListeners(windowMap[selectedWindow])
|
||||
|
||||
$: windowMap[selectedWindow]?.setCursorGrab(cursorGrab)
|
||||
$: windowMap[selectedWindow]?.setCursorVisible(cursorVisible)
|
||||
$: windowMap[selectedWindow]?.setCursorIcon(cursorIcon)
|
||||
$: cursorX !== null &&
|
||||
cursorY !== null &&
|
||||
windowMap[selectedWindow]?.setCursorPosition(
|
||||
new PhysicalPosition(cursorX, cursorY)
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col children:grow gap-2">
|
||||
<div class="flex gap-1">
|
||||
<input
|
||||
class="input grow"
|
||||
type="text"
|
||||
placeholder="New Window label.."
|
||||
bind:value={newWindowLabel}
|
||||
/>
|
||||
<button class="btn" on:click={createWindow}>New window</button>
|
||||
</div>
|
||||
<br />
|
||||
{#if Object.keys(windowMap).length >= 1}
|
||||
<span class="font-700 text-sm">Selected window:</span>
|
||||
<select class="input" bind:value={selectedWindow}>
|
||||
<option value="" disabled selected>Choose a window...</option>
|
||||
{#each Object.keys(windowMap) as label}
|
||||
<option value={label}>{label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{/if}
|
||||
{#if windowMap[selectedWindow]}
|
||||
<br />
|
||||
<div>
|
||||
<button
|
||||
class="btn"
|
||||
title="Unminimizes after 2 seconds"
|
||||
on:click={() => windowMap[selectedWindow].center()}
|
||||
>
|
||||
Center
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
title="Unminimizes after 2 seconds"
|
||||
on:click={minimize_}
|
||||
>
|
||||
Minimize
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
title="Visible again after 2 seconds"
|
||||
on:click={hide_}
|
||||
>
|
||||
Hide
|
||||
</button>
|
||||
<button class="btn" on:click={getIcon}> Change icon </button>
|
||||
<button
|
||||
class="btn"
|
||||
on:click={requestUserAttention_}
|
||||
title="Minimizes the window, requests attention for 3s and then resets it"
|
||||
>Request attention</button
|
||||
>
|
||||
</div>
|
||||
<br />
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<label>
|
||||
Maximized
|
||||
<input type="checkbox" bind:checked={maximized} />
|
||||
</label>
|
||||
<label>
|
||||
Resizable
|
||||
<input type="checkbox" bind:checked={resizable} />
|
||||
</label>
|
||||
<label>
|
||||
Has decorations
|
||||
<input type="checkbox" bind:checked={decorations} />
|
||||
</label>
|
||||
<label>
|
||||
Always on top
|
||||
<input type="checkbox" bind:checked={alwaysOnTop} />
|
||||
</label>
|
||||
<label>
|
||||
Fullscreen
|
||||
<input type="checkbox" bind:checked={fullscreen} />
|
||||
</label>
|
||||
</div>
|
||||
<br />
|
||||
<div class="flex flex-row gap-2 flex-wrap">
|
||||
<div class="flex children:grow flex-col">
|
||||
<div>
|
||||
X
|
||||
<input class="input" type="number" bind:value={x} min="0" />
|
||||
</div>
|
||||
<div>
|
||||
Y
|
||||
<input class="input" type="number" bind:value={y} min="0" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex children:grow flex-col">
|
||||
<div>
|
||||
Width
|
||||
<input class="input" type="number" bind:value={width} min="400" />
|
||||
</div>
|
||||
<div>
|
||||
Height
|
||||
<input class="input" type="number" bind:value={height} min="400" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex children:grow flex-col">
|
||||
<div>
|
||||
Min width
|
||||
<input class="input" type="number" bind:value={minWidth} />
|
||||
</div>
|
||||
<div>
|
||||
Min height
|
||||
<input class="input" type="number" bind:value={minHeight} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex children:grow flex-col">
|
||||
<div>
|
||||
Max width
|
||||
<input class="input" type="number" bind:value={maxWidth} min="800" />
|
||||
</div>
|
||||
<div>
|
||||
Max height
|
||||
<input class="input" type="number" bind:value={maxHeight} min="400" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<div class="flex">
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Inner Size
|
||||
</div>
|
||||
<span>Width: {innerSize.width}</span>
|
||||
<span>Height: {innerSize.height}</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Outer Size
|
||||
</div>
|
||||
<span>Width: {outerSize.width}</span>
|
||||
<span>Height: {outerSize.height}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Inner Logical Size
|
||||
</div>
|
||||
<span>Width: {innerSize.toLogical(scaleFactor).width}</span>
|
||||
<span>Height: {innerSize.toLogical(scaleFactor).height}</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Outer Logical Size
|
||||
</div>
|
||||
<span>Width: {outerSize.toLogical(scaleFactor).width}</span>
|
||||
<span>Height: {outerSize.toLogical(scaleFactor).height}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Inner Position
|
||||
</div>
|
||||
<span>x: {innerPosition.x}</span>
|
||||
<span>y: {innerPosition.y}</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Outer Position
|
||||
</div>
|
||||
<span>x: {outerPosition.x}</span>
|
||||
<span>y: {outerPosition.y}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Inner Logical Position
|
||||
</div>
|
||||
<span>x: {innerPosition.toLogical(scaleFactor).x}</span>
|
||||
<span>y: {innerPosition.toLogical(scaleFactor).y}</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="text-accent dark:text-darkAccent font-700">
|
||||
Outer Logical Position
|
||||
</div>
|
||||
<span>x: {outerPosition.toLogical(scaleFactor).x}</span>
|
||||
<span>y: {outerPosition.toLogical(scaleFactor).y}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<h4 class="mb-2">Cursor</h4>
|
||||
<div class="flex gap-2">
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={cursorGrab} />
|
||||
Grab
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={cursorVisible} />
|
||||
Visible
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<label>
|
||||
Icon
|
||||
<select class="input" bind:value={cursorIcon}>
|
||||
{#each cursorIconOptions as kind}
|
||||
<option value={kind}>{kind}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
X position
|
||||
<input class="input" type="number" bind:value={cursorX} />
|
||||
</label>
|
||||
<label>
|
||||
Y position
|
||||
<input class="input" type="number" bind:value={cursorY} />
|
||||
</label>
|
||||
</div>
|
||||
<br />
|
||||
<div class="flex flex-col gap-1">
|
||||
<form class="flex gap-1" on:submit|preventDefault={setTitle_}>
|
||||
<input class="input grow" id="title" bind:value={windowTitle} />
|
||||
<button class="btn" type="submit">Set title</button>
|
||||
</form>
|
||||
<form class="flex gap-1" on:submit|preventDefault={openUrl}>
|
||||
<input class="input grow" id="url" bind:value={urlValue} />
|
||||
<button class="btn" id="open-url"> Open URL </button>
|
||||
</form>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
98
examples/api/unocss.config.js
Normal file
98
examples/api/unocss.config.js
Normal file
@@ -0,0 +1,98 @@
|
||||
import {
|
||||
defineConfig,
|
||||
presetIcons,
|
||||
presetUno,
|
||||
extractorSvelte,
|
||||
presetWebFonts,
|
||||
} from 'unocss'
|
||||
|
||||
export default defineConfig({
|
||||
theme: {
|
||||
colors: {
|
||||
primary: '#FFFFFF',
|
||||
primaryLighter: '#e9ecef',
|
||||
darkPrimary: '#1B1B1D',
|
||||
darkPrimaryLighter: '#242526',
|
||||
primaryText: '#1C1E21',
|
||||
darkPrimaryText: '#E3E3E3',
|
||||
secondaryText: '#858A91',
|
||||
darkSecondaryText: '#C2C5CA',
|
||||
accent: '#3578E5',
|
||||
accentDark: '#306cce',
|
||||
accentDarker: '#2d66c3',
|
||||
accentDarkest: '#2554a0',
|
||||
accentLight: '#538ce9',
|
||||
accentLighter: '#72a1ed',
|
||||
accentLightest: '#9abcf2',
|
||||
accentText: '#FFFFFF',
|
||||
darkAccent: '#67d6ed',
|
||||
darkAccentDark: '#49cee9',
|
||||
darkAccentDarker: '#39cae8',
|
||||
darkAccentDarkest: '#19b5d5',
|
||||
darkAccentLight: '#85def1',
|
||||
darkAccentLighter: '#95e2f2',
|
||||
darkAccentLightest: '#c2eff8',
|
||||
darkAccentText: '#1C1E21',
|
||||
code: '#d6d8da',
|
||||
codeDark: '#282a2e',
|
||||
hoverOverlay: 'rgba(0,0,0,.05)',
|
||||
hoverOverlayDarker: 'rgba(0,0,0,.1)',
|
||||
darkHoverOverlay: 'hsla(0,0%,100%,.05)',
|
||||
darkHoverOverlayDarker: 'hsla(0,0%,100%,.1)'
|
||||
}
|
||||
},
|
||||
preflights: [
|
||||
{
|
||||
|
||||
getCSS: ({ theme }) => `
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: ${theme.colors.accent};
|
||||
}
|
||||
|
||||
.dark ::-webkit-scrollbar-thumb {
|
||||
background-color: ${theme.colors.darkAccent};
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: ${theme.fontSize.xs[0]};
|
||||
font-family: ${theme.fontFamily.mono};
|
||||
border-radius: ${theme.borderRadius['DEFAULT']};
|
||||
background-color: ${theme.colors.code};
|
||||
}
|
||||
|
||||
.code-block {
|
||||
font-family: ${theme.fontFamily.mono};
|
||||
font-size: ${theme.fontSize.sm[0]};
|
||||
}
|
||||
|
||||
.dark code {
|
||||
background-color: ${theme.colors.codeDark};
|
||||
}
|
||||
`
|
||||
}
|
||||
],
|
||||
shortcuts: {
|
||||
btn: `select-none outline-none shadow-md p-2 rd-1 text-primaryText border-none font-400 dark:font-600
|
||||
bg-accent hover:bg-accentDarker active:bg-accentDarkest text-accentText
|
||||
dark:bg-darkAccent dark:hover:bg-darkAccentDarker dark:active:bg-darkAccentDarkest dark:text-darkAccentText`,
|
||||
nv: `decoration-none flex items-center relative p-2 rd-1 transition-all-125 ease
|
||||
text-darkSecondaryText
|
||||
hover:text-accent dark:hover:text-darkAccent
|
||||
hover:bg-darkHoverOverlay hover:border-l-4`,
|
||||
nv_selected: `nv bg-darkHoverOverlay text-accent dark:text-darkAccent border-l-4`,
|
||||
note: `decoration-none flex-inline items-center relative p-2 rd-1
|
||||
border-l-4 border-accent dark:border-darkAccent
|
||||
bg-accent/10 dark:bg-darkAccent/10`,
|
||||
'note-red':
|
||||
'note bg-red-700/10 dark:bg-red-700/10 after:bg-red-700 dark:after:bg-red-700',
|
||||
input:
|
||||
'h-10 flex items-center outline-none border-none p-2 rd-1 shadow-md bg-primaryLighter dark:bg-darkPrimaryLighter text-primaryText dark:text-darkPrimaryText',
|
||||
},
|
||||
presets: [presetUno(), presetIcons(), presetWebFonts({
|
||||
fonts: {
|
||||
sans: 'Rubik',
|
||||
mono: ['Fira Code', 'Fira Mono:400,700'],
|
||||
}
|
||||
})],
|
||||
extractors: [extractorSvelte]
|
||||
})
|
||||
@@ -1,9 +1,10 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import Unocss from 'unocss/vite'
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
plugins: [Unocss(), svelte()],
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user