From ea76f65579fbd1a72595a73bf35f394838f3357a Mon Sep 17 00:00:00 2001 From: Tristan Michael Date: Mon, 30 Mar 2026 07:54:17 -0700 Subject: [PATCH] Frameless transparent window with custom title bar Remove native window decorations and add transparent background with rounded corners, border, and drop shadow. Add custom close button (Linux) and minimize/maximize/close (Windows) in the header. Add programmatic window dragging via mousedown and double-click to maximize. Install tauri-plugin-os for platform detection. Move sync spinner to bottom-right corner. Convert drawer layout from vw to cqi units to support the padding-based shadow approach. --- apps/tauri/package-lock.json | 12 +- apps/tauri/package.json | 3 +- apps/tauri/src-tauri/Cargo.toml | 1 + .../tauri/src-tauri/capabilities/default.json | 15 + apps/tauri/src-tauri/src/lib.rs | 1 + apps/tauri/src-tauri/tauri.conf.json | 4 +- apps/tauri/src/App.svelte | 35 ++- apps/tauri/src/app.css | 11 + apps/tauri/src/lib/screens/TasksScreen.svelte | 296 +++++++++++------- 9 files changed, 250 insertions(+), 128 deletions(-) create mode 100644 apps/tauri/src-tauri/capabilities/default.json diff --git a/apps/tauri/package-lock.json b/apps/tauri/package-lock.json index 794946d..27a6f67 100644 --- a/apps/tauri/package-lock.json +++ b/apps/tauri/package-lock.json @@ -9,7 +9,8 @@ "version": "0.1.0", "dependencies": { "@tauri-apps/api": "^2.0.0", - "@tauri-apps/plugin-dialog": "^2.0.0" + "@tauri-apps/plugin-dialog": "^2.0.0", + "@tauri-apps/plugin-os": "^2.3.2" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", @@ -1421,6 +1422,15 @@ "@tauri-apps/api": "^2.8.0" } }, + "node_modules/@tauri-apps/plugin-os": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-os/-/plugin-os-2.3.2.tgz", + "integrity": "sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.8.0" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", diff --git a/apps/tauri/package.json b/apps/tauri/package.json index c3290d5..7229dab 100644 --- a/apps/tauri/package.json +++ b/apps/tauri/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@tauri-apps/api": "^2.0.0", - "@tauri-apps/plugin-dialog": "^2.0.0" + "@tauri-apps/plugin-dialog": "^2.0.0", + "@tauri-apps/plugin-os": "^2.3.2" } } diff --git a/apps/tauri/src-tauri/Cargo.toml b/apps/tauri/src-tauri/Cargo.toml index bc8a859..bee0a11 100644 --- a/apps/tauri/src-tauri/Cargo.toml +++ b/apps/tauri/src-tauri/Cargo.toml @@ -13,6 +13,7 @@ tauri-build = { version = "2", features = [] } [dependencies] tauri = { version = "2", features = [] } tauri-plugin-dialog = "2" +tauri-plugin-os = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" bevy-tasks-core = { path = "../../../crates/bevy-tasks-core" } diff --git a/apps/tauri/src-tauri/capabilities/default.json b/apps/tauri/src-tauri/capabilities/default.json new file mode 100644 index 0000000..e08700a --- /dev/null +++ b/apps/tauri/src-tauri/capabilities/default.json @@ -0,0 +1,15 @@ +{ + "identifier": "default", + "description": "Default capabilities for Bevy Tasks", + "windows": ["main"], + "permissions": [ + "core:default", + "dialog:default", + "os:default", + "core:window:allow-close", + "core:window:allow-minimize", + "core:window:allow-toggle-maximize", + "core:window:allow-start-dragging", + "core:window:allow-is-maximized" + ] +} diff --git a/apps/tauri/src-tauri/src/lib.rs b/apps/tauri/src-tauri/src/lib.rs index 7859b6c..b4ef687 100644 --- a/apps/tauri/src-tauri/src/lib.rs +++ b/apps/tauri/src-tauri/src/lib.rs @@ -353,6 +353,7 @@ pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_os::init()) .manage(Mutex::new(AppState { config, repo: None })) .invoke_handler(tauri::generate_handler![ get_config, diff --git a/apps/tauri/src-tauri/tauri.conf.json b/apps/tauri/src-tauri/tauri.conf.json index e60211c..ad02de7 100644 --- a/apps/tauri/src-tauri/tauri.conf.json +++ b/apps/tauri/src-tauri/tauri.conf.json @@ -18,7 +18,9 @@ "height": 700, "minWidth": 320, "minHeight": 500, - "resizable": true + "resizable": true, + "decorations": false, + "transparent": true } ], "security": { diff --git a/apps/tauri/src/App.svelte b/apps/tauri/src/App.svelte index c14a839..ecc3e47 100644 --- a/apps/tauri/src/App.svelte +++ b/apps/tauri/src/App.svelte @@ -11,22 +11,25 @@
-
- {#if app.error} -
- {app.error} - -
- {/if} +
+
+ {#if app.error} +
+ {app.error} + +
+ {/if} - {#if app.screen === "setup"} - - {:else} - - {/if} + {#if app.screen === "setup"} + + {:else} + + {/if} +
diff --git a/apps/tauri/src/app.css b/apps/tauri/src/app.css index 1804ee4..7636361 100644 --- a/apps/tauri/src/app.css +++ b/apps/tauri/src/app.css @@ -24,11 +24,16 @@ -moz-osx-font-smoothing: grayscale; } +html { + background: transparent; +} + body { margin: 0; overflow: hidden; user-select: none; -webkit-user-select: none; + background: transparent; } /* Scrollbar styling */ @@ -42,3 +47,9 @@ body { .dark ::-webkit-scrollbar-thumb { background: #4b5563; } + +/* Select dropdown theming */ +.dark select option { + background-color: #242424; + color: #e5e7eb; +} diff --git a/apps/tauri/src/lib/screens/TasksScreen.svelte b/apps/tauri/src/lib/screens/TasksScreen.svelte index c95d603..41bfb3e 100644 --- a/apps/tauri/src/lib/screens/TasksScreen.svelte +++ b/apps/tauri/src/lib/screens/TasksScreen.svelte @@ -1,8 +1,28 @@ -
+
-
+
{#each app.lists as list (list.id)} @@ -287,7 +313,7 @@
-
+
{ if (e.key === "Escape") closeDrawer(); }} >
- -
+
- - - - -
-

- {app.config?.current_workspace ?? ""} -

-

{app.activeList?.title ?? "Tasks"}

-
- - - {#if app.syncing} -
- {/if} -
- - -
- {#if app.lists.length === 0} -
-

No lists yet

-

Tap the list name above to create one

-
- {:else if !app.activeListId} -
- Select a list -
- {:else} - {#each app.pendingTasks as task (task.id)} - -
handleDragStart(e, task.id)} - ondragover={(e) => handleDragOver(e, task.id)} - ondragend={handleDragEnd} - ondrop={(e) => handleDrop(e, task.id)} - class="{dragId === task.id ? 'opacity-30' : ''} {dragOverId === task.id && dragId !== task.id ? 'border-t-2 border-t-primary' : ''}" - > - -
- {/each} - - {#if app.pendingTasks.length === 0} -
No tasks. Add one below.
- {/if} - - {#if app.completedTasks.length > 0} -
+ +
+ + +
{ if (isDesktop) appWindow.toggleMaximize(); }} + class="relative flex items-center border-b border-border-light px-4 py-3 dark:border-border-dark" + > + - {#if completedVisible} -
- {#each app.completedTasks as task (task.id)} - - {/each} + + +
+

+ {app.config?.current_workspace ?? ""} +

+

{app.activeList?.title ?? "Tasks"}

+
+ + + {#if isDesktop} +
+ {#if isWindows} + + + {/if} +
{/if} - {/if} - {/if} -
+ - -
- + +
+ {#if app.lists.length === 0} +
+

No lists yet

+

Tap the list name above to create one

+
+ {:else if !app.activeListId} +
+ Select a list +
+ {:else} + {#each app.pendingTasks as task (task.id)} + +
handleDragStart(e, task.id)} + ondragover={(e) => handleDragOver(e, task.id)} + ondragend={handleDragEnd} + ondrop={(e) => handleDrop(e, task.id)} + class="{dragId === task.id ? 'opacity-30' : ''} {dragOverId === task.id && dragId !== task.id ? 'border-t-2 border-t-primary' : ''}" + > + +
+ {/each} + + {#if app.pendingTasks.length === 0} +
No tasks. Add one below.
+ {/if} + + {#if app.completedTasks.length > 0} +
+ + {#if completedVisible} +
+ {#each app.completedTasks as task (task.id)} + + {/each} +
+ {/if} + {/if} + {/if} +
+ + +
+ +
+
+ + +
+ {#if selectedTask} + + {/if} +
+ + + {#if app.syncing} +
+ {/if}
@@ -412,7 +490,7 @@
@@ -431,6 +509,6 @@
-
+