feat: setup mode selection, per-workspace settings in kebab menu

Setup screen now offers Local vs WebDAV mode choice with cancel button
when workspaces exist. Settings moved from drawer bottom into workspace
kebab menu, scoped per-workspace. WebDAV section hidden for local
workspaces, theme dropdown replaces dark mode toggle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tristan Michael 2026-04-03 03:58:12 -07:00
parent a1e97bc0fe
commit 12afc91110
3 changed files with 293 additions and 144 deletions

View file

@ -2,7 +2,10 @@
import { invoke } from "@tauri-apps/api/core";
import { app } from "../stores/app.svelte";
let { onclose }: { onclose?: () => void } = $props();
let { onclose, workspaceName }: { onclose?: () => void; workspaceName: string } = $props();
let ws = $derived(app.config?.workspaces[workspaceName]);
let isWebdav = $derived(ws?.mode === "webdav");
let webdavUrl = $state("");
let webdavUser = $state("");
@ -10,19 +13,15 @@
let testStatus = $state<"idle" | "testing" | "ok" | "fail">("idle");
$effect(() => {
const ws = app.config?.current_workspace;
if (!ws) return;
const cfg = app.config?.workspaces[ws];
if (cfg?.webdav_url) {
webdavUrl = cfg.webdav_url;
try {
const domain = new URL(cfg.webdav_url).hostname;
invoke<[string, string]>("load_credentials", { domain }).then(([u, p]) => {
webdavUser = u;
webdavPass = p;
}).catch(() => {});
} catch {}
}
if (!ws?.webdav_url) return;
webdavUrl = ws.webdav_url;
try {
const domain = new URL(ws.webdav_url).hostname;
invoke<[string, string]>("load_credentials", { domain }).then(([u, p]) => {
webdavUser = u;
webdavPass = p;
}).catch(() => {});
} catch {}
});
async function testConnection() {
@ -40,9 +39,9 @@
}
async function saveWebdav() {
if (!app.config?.current_workspace || !webdavUrl.trim()) return;
if (!webdavUrl.trim()) return;
await invoke("set_webdav_config", {
workspaceName: app.config.current_workspace,
workspaceName,
webdavUrl: webdavUrl.trim(),
});
if (webdavUser && webdavPass) {
@ -55,13 +54,12 @@
}
await app.loadConfig();
}
</script>
<header
class="flex items-center justify-between border-b border-border-light px-4 py-3 dark:border-border-dark"
>
<h1 class="text-lg font-bold">Settings</h1>
<h1 class="text-lg font-bold">{workspaceName} Settings</h1>
<button
onclick={() => onclose?.()}
class="rounded-lg p-1.5 hover:bg-black/5 dark:hover:bg-white/10"
@ -75,53 +73,53 @@
</header>
<main class="flex-1 overflow-y-auto p-4">
<!-- WebDAV Sync -->
<section class="mb-6">
<h2 class="mb-3 text-sm font-semibold uppercase tracking-wide opacity-50">
WebDAV Sync
</h2>
<div class="rounded-xl border border-border-light p-4 dark:border-border-dark">
<label class="mb-1 block text-xs font-medium opacity-60">Server URL</label>
<input
type="url"
bind:value={webdavUrl}
placeholder="https://dav.example.com/tasks/"
class="mb-3 w-full rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm outline-none focus:border-primary dark:border-border-dark"
/>
<!-- WebDAV Sync (only for webdav workspaces) -->
{#if isWebdav}
<section class="mb-6">
<h2 class="mb-3 text-sm font-semibold uppercase tracking-wide opacity-50">
WebDAV Sync
</h2>
<div class="rounded-xl border border-border-light p-4 dark:border-border-dark">
<label class="mb-1 block text-xs font-medium opacity-60">Server URL</label>
<input
type="url"
bind:value={webdavUrl}
placeholder="https://dav.example.com/tasks/"
class="mb-3 w-full rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm outline-none focus:border-primary dark:border-border-dark"
/>
<label class="mb-1 block text-xs font-medium opacity-60">Username</label>
<input
type="text"
bind:value={webdavUser}
class="mb-3 w-full rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm outline-none focus:border-primary dark:border-border-dark"
/>
<label class="mb-1 block text-xs font-medium opacity-60">Username</label>
<input
type="text"
bind:value={webdavUser}
class="mb-3 w-full rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm outline-none focus:border-primary dark:border-border-dark"
/>
<label class="mb-1 block text-xs font-medium opacity-60">Password</label>
<input
type="password"
bind:value={webdavPass}
class="mb-4 w-full rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm outline-none focus:border-primary dark:border-border-dark"
/>
<label class="mb-1 block text-xs font-medium opacity-60">Password</label>
<input
type="password"
bind:value={webdavPass}
class="mb-4 w-full rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm outline-none focus:border-primary dark:border-border-dark"
/>
<div class="flex gap-2">
<button
onclick={testConnection}
disabled={!webdavUrl.trim()}
class="rounded-lg border border-border-light px-4 py-2 text-sm font-medium hover:bg-black/5 disabled:opacity-40 dark:border-border-dark dark:hover:bg-white/10"
>
{testStatus === "testing" ? "Testing…" : testStatus === "ok" ? "Connected" : testStatus === "fail" ? "Failed — Retry" : "Test Connection"}
</button>
<button
onclick={saveWebdav}
disabled={!webdavUrl.trim()}
class="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-white hover:bg-primary-hover disabled:opacity-40"
>
Save
</button>
<div class="flex gap-2">
<button
onclick={testConnection}
disabled={!webdavUrl.trim()}
class="rounded-lg border border-border-light px-4 py-2 text-sm font-medium hover:bg-black/5 disabled:opacity-40 dark:border-border-dark dark:hover:bg-white/10"
>
{testStatus === "testing" ? "Testing..." : testStatus === "ok" ? "Connected" : testStatus === "fail" ? "Failed -- Retry" : "Test Connection"}
</button>
<button
onclick={saveWebdav}
disabled={!webdavUrl.trim()}
class="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-white hover:bg-primary-hover disabled:opacity-40"
>
Save
</button>
</div>
</div>
</div>
{#if app.config?.current_workspace}
<div class="mt-3 flex items-center gap-2">
<select
value={app.syncMode}
@ -137,11 +135,11 @@
disabled={app.syncing}
class="flex-1 rounded-lg bg-primary py-2 text-sm font-medium text-white hover:bg-primary-hover disabled:opacity-40"
>
{app.syncing ? "Syncing" : "Sync Now"}
{app.syncing ? "Syncing..." : "Sync Now"}
</button>
</div>
{#if app.config.workspaces[app.config.current_workspace]?.last_sync}
{@const lastSync = new Date(app.config.workspaces[app.config.current_workspace].last_sync!)}
{#if ws?.last_sync}
{@const lastSync = new Date(ws.last_sync)}
{@const secsAgo = Math.floor((Date.now() - lastSync.getTime()) / 1000)}
{@const relTime = secsAgo < 60 ? "just now" : secsAgo < 3600 ? `${Math.floor(secsAgo / 60)}m ago` : `${Math.floor(secsAgo / 3600)}h ago`}
<p class="mt-1.5 text-xs opacity-40">
@ -151,27 +149,32 @@
{/if}
</p>
{/if}
{/if}
</section>
</section>
{/if}
<!-- Theme -->
<section>
<h2 class="mb-3 text-sm font-semibold uppercase tracking-wide opacity-50">
Appearance
</h2>
<button
onclick={() => app.toggleDarkMode()}
class="flex w-full items-center justify-between rounded-xl border border-border-light p-4 dark:border-border-dark"
>
<span class="text-sm font-medium">Dark mode</span>
<div
class="h-6 w-11 rounded-full transition-colors {app.darkMode ? 'bg-primary' : 'bg-gray-300 dark:bg-gray-600'}"
<div class="rounded-xl border border-border-light p-4 dark:border-border-dark">
<label class="mb-1 block text-xs font-medium opacity-60">Theme</label>
<select
value={ws?.theme ?? ""}
onchange={(e) => {
const val = (e.target as HTMLSelectElement).value;
app.setTheme(val || null);
}}
class="w-full appearance-none rounded-lg border border-border-light bg-surface-light px-3 py-2 text-sm text-text-light outline-none focus:border-primary dark:border-border-dark dark:bg-surface-dark dark:text-text-dark"
>
<div
class="h-5 w-5 translate-y-0.5 rounded-full bg-white shadow transition-transform {app.darkMode ? 'translate-x-5.5' : 'translate-x-0.5'}"
></div>
</div>
</button>
<option value="">System default</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="nord">Nord</option>
<option value="dracula">Dracula</option>
<option value="solarized">Solarized Dark</option>
</select>
</div>
</section>
<p class="mt-8 text-center text-xs opacity-30">Tauri v2 + Svelte</p>

View file

@ -1,16 +1,25 @@
<script lang="ts">
import { invoke } from "@tauri-apps/api/core";
import { open } from "@tauri-apps/plugin-dialog";
import { app } from "../stores/app.svelte";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { platform } from "@tauri-apps/plugin-os";
let { cancellable = false }: { cancellable?: boolean } = $props();
const appWindow = getCurrentWindow();
const currentPlatform = platform();
const isDesktop = currentPlatform === "linux" || currentPlatform === "windows";
const isWindows = currentPlatform === "windows";
const isMobile = currentPlatform === "android" || currentPlatform === "ios";
let mode = $state<"local" | "webdav" | null>(isMobile ? "webdav" : null);
let name = $state("");
let path = $state("");
let webdavUrl = $state("");
let webdavUser = $state("");
let webdavPass = $state("");
let testStatus = $state<"idle" | "testing" | "ok" | "fail">("idle");
async function pickFolder() {
const selected = await open({ directory: true, multiple: false });
@ -26,23 +35,63 @@
const selected = await open({ directory: true, multiple: false });
if (!selected) return;
const folder = selected as string;
// Derive workspace name from folder name
const parts = folder.replace(/\\/g, "/").split("/");
const wsName = parts[parts.length - 1] || "workspace";
await app.addWorkspace(wsName, folder);
}
async function testConnection() {
testStatus = "testing";
try {
await invoke("test_webdav_connection", {
url: webdavUrl,
username: webdavUser,
password: webdavPass,
});
testStatus = "ok";
} catch {
testStatus = "fail";
}
}
async function handleCreateWebdav() {
if (!name.trim() || !webdavUrl.trim()) return;
await app.addWebdavWorkspace(name.trim(), webdavUrl.trim(), webdavUser, webdavPass);
}
function handleDrag(e: MouseEvent) {
if (e.button !== 0) return;
if ((e.target as HTMLElement).closest("button, input")) return;
if (isDesktop) appWindow.startDragging();
}
function goBack() {
mode = null;
name = "";
path = "";
webdavUrl = "";
webdavUser = "";
webdavPass = "";
testStatus = "idle";
}
</script>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="flex h-full flex-col" onmousedown={handleDrag}>
<!-- Title bar area with window controls -->
<header class="flex h-11 shrink-0 items-center justify-end px-2">
<header class="flex h-11 shrink-0 items-center justify-between px-2">
<div>
{#if cancellable}
<button
onclick={() => app.setScreen("tasks")}
class="rounded-lg p-1.5 opacity-50 hover:bg-black/10 hover:opacity-80 dark:hover:bg-white/10"
>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z" />
</svg>
</button>
{/if}
</div>
{#if isDesktop}
<div class="flex items-center gap-0.5">
{#if isWindows}
@ -72,58 +121,162 @@
class="w-full max-w-sm rounded-2xl bg-card-light p-8 shadow-lg dark:bg-card-dark"
>
<h1 class="mb-1 text-2xl font-bold">Onyx</h1>
<p class="mb-6 text-sm text-text-secondary-light dark:text-text-secondary-dark">
Create a new workspace or open an existing one.
</p>
<label class="mb-1 block text-sm font-medium">
Workspace name
<input
type="text"
bind:value={name}
placeholder="My Tasks"
class="mt-1 mb-4 w-full rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm font-normal outline-none focus:border-primary dark:border-border-dark"
/>
</label>
{#if mode === null}
<!-- Step 1: Choose mode -->
<p class="mb-6 text-sm text-text-secondary-light dark:text-text-secondary-dark">
How would you like to store your tasks?
</p>
<!-- svelte-ignore a11y_label_has_associated_control -->
<label class="mb-1 block text-sm font-medium">Folder</label>
<div class="mb-6 flex gap-2">
<input
type="text"
bind:value={path}
readonly
placeholder="Select a folder…"
class="min-w-0 flex-1 rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm dark:border-border-dark"
/>
<button
onclick={pickFolder}
class="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-white hover:bg-primary-hover"
onclick={() => (mode = "local")}
class="mb-3 w-full rounded-xl border border-border-light p-4 text-left hover:bg-black/5 dark:border-border-dark dark:hover:bg-white/10"
>
Browse
<p class="text-sm font-semibold">Local Folder</p>
<p class="mt-0.5 text-xs text-text-secondary-light dark:text-text-secondary-dark">
Pick a folder on your computer. Files stay local.
</p>
</button>
</div>
<button
onclick={handleCreate}
disabled={!name.trim() || !path.trim()}
class="w-full rounded-lg bg-primary py-2.5 text-sm font-medium text-white hover:bg-primary-hover disabled:opacity-40"
>
Create Workspace
</button>
<button
onclick={() => (mode = "webdav")}
class="w-full rounded-xl border border-border-light p-4 text-left hover:bg-black/5 dark:border-border-dark dark:hover:bg-white/10"
>
<p class="text-sm font-semibold">WebDAV Server</p>
<p class="mt-0.5 text-xs text-text-secondary-light dark:text-text-secondary-dark">
Connect to a WebDAV server. The app manages local files automatically.
</p>
</button>
<div class="my-4 flex items-center gap-3">
<div class="h-px flex-1 bg-border-light dark:bg-border-dark"></div>
<span class="text-xs opacity-40">or</span>
<div class="h-px flex-1 bg-border-light dark:bg-border-dark"></div>
</div>
{:else if mode === "local"}
<!-- Step 2a: Local workspace -->
<p class="mb-6 text-sm text-text-secondary-light dark:text-text-secondary-dark">
Create a new workspace or open an existing one.
</p>
<button
onclick={handleOpen}
class="w-full rounded-lg border border-border-light py-2.5 text-sm font-medium hover:bg-black/5 dark:border-border-dark dark:hover:bg-white/10"
>
Open Existing Folder
</button>
<label class="mb-1 block text-sm font-medium">
Workspace name
<input
type="text"
bind:value={name}
placeholder="My Tasks"
class="mt-1 mb-4 w-full rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm font-normal outline-none focus:border-primary dark:border-border-dark"
/>
</label>
<!-- svelte-ignore a11y_label_has_associated_control -->
<label class="mb-1 block text-sm font-medium">Folder</label>
<div class="mb-6 flex gap-2">
<input
type="text"
bind:value={path}
readonly
placeholder="Select a folder..."
class="min-w-0 flex-1 rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm dark:border-border-dark"
/>
<button
onclick={pickFolder}
class="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-white hover:bg-primary-hover"
>
Browse
</button>
</div>
<button
onclick={handleCreate}
disabled={!name.trim() || !path.trim()}
class="w-full rounded-lg bg-primary py-2.5 text-sm font-medium text-white hover:bg-primary-hover disabled:opacity-40"
>
Create Workspace
</button>
<div class="my-4 flex items-center gap-3">
<div class="h-px flex-1 bg-border-light dark:bg-border-dark"></div>
<span class="text-xs opacity-40">or</span>
<div class="h-px flex-1 bg-border-light dark:bg-border-dark"></div>
</div>
<button
onclick={handleOpen}
class="mb-3 w-full rounded-lg border border-border-light py-2.5 text-sm font-medium hover:bg-black/5 dark:border-border-dark dark:hover:bg-white/10"
>
Open Existing Folder
</button>
{#if !isMobile}
<button
onclick={goBack}
class="w-full rounded-lg py-2 text-sm opacity-50 hover:opacity-80"
>
Back
</button>
{/if}
{:else}
<!-- Step 2b: WebDAV workspace -->
<p class="mb-6 text-sm text-text-secondary-light dark:text-text-secondary-dark">
Connect to a WebDAV server for cloud-synced tasks.
</p>
<label class="mb-1 block text-sm font-medium">
Workspace name
<input
type="text"
bind:value={name}
placeholder="My Tasks"
class="mt-1 mb-4 w-full rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm font-normal outline-none focus:border-primary dark:border-border-dark"
/>
</label>
<label class="mb-1 block text-xs font-medium opacity-60">Server URL</label>
<input
type="url"
bind:value={webdavUrl}
placeholder="https://dav.example.com/tasks/"
class="mb-3 w-full rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm outline-none focus:border-primary dark:border-border-dark"
/>
<label class="mb-1 block text-xs font-medium opacity-60">Username</label>
<input
type="text"
bind:value={webdavUser}
class="mb-3 w-full rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm outline-none focus:border-primary dark:border-border-dark"
/>
<label class="mb-1 block text-xs font-medium opacity-60">Password</label>
<input
type="password"
bind:value={webdavPass}
class="mb-4 w-full rounded-lg border border-border-light bg-transparent px-3 py-2 text-sm outline-none focus:border-primary dark:border-border-dark"
/>
<div class="mb-4 flex gap-2">
<button
onclick={testConnection}
disabled={!webdavUrl.trim()}
class="rounded-lg border border-border-light px-4 py-2 text-sm font-medium hover:bg-black/5 disabled:opacity-40 dark:border-border-dark dark:hover:bg-white/10"
>
{testStatus === "testing" ? "Testing..." : testStatus === "ok" ? "Connected" : testStatus === "fail" ? "Failed -- Retry" : "Test Connection"}
</button>
</div>
<button
onclick={handleCreateWebdav}
disabled={!name.trim() || !webdavUrl.trim()}
class="w-full rounded-lg bg-primary py-2.5 text-sm font-medium text-white hover:bg-primary-hover disabled:opacity-40"
>
Create Workspace
</button>
{#if !isMobile}
<button
onclick={goBack}
class="mt-3 w-full rounded-lg py-2 text-sm opacity-50 hover:opacity-80"
>
Back
</button>
{/if}
{/if}
</div>
</div>
</div>

View file

@ -35,6 +35,7 @@
let showDrawer = $state(false);
let showSettings = $state(false);
let settingsWorkspace = $state<string | null>(null);
let showNewList = $state(false);
let showWorkspacePicker = $state(false);
let workspacePickerEl = $state<HTMLDivElement | null>(null);
@ -152,7 +153,7 @@
clone.style.position = "absolute";
clone.style.top = "-9999px";
clone.style.left = "-9999px";
if (app.darkMode) {
if (app.isDark) {
clone.classList.add("dark");
clone.style.backgroundColor = "var(--color-surface-dark)";
clone.style.color = "var(--color-text-dark)";
@ -192,12 +193,9 @@
showNewList = false;
}
function openSettings() {
showSettings = true;
}
function closeSettings() {
showSettings = false;
settingsWorkspace = null;
}
function handleHeaderMouseDown(e: MouseEvent) {
@ -256,7 +254,7 @@
{/if}
<div class="min-w-0 flex-1">
<p class="truncate text-sm">{name}</p>
<p class="truncate text-xs opacity-40">{ws?.path ?? ""}</p>
<p class="truncate text-xs opacity-40">{ws?.mode === "webdav" ? ws.webdav_url ?? "WebDAV" : ws?.path ?? ""}</p>
</div>
</button>
<div class="relative shrink-0" data-ws-menu>
@ -270,6 +268,15 @@
</button>
{#if wsMenuName === name}
<div class="absolute right-0 top-full z-40 mt-1 min-w-[140px] rounded-lg border border-border-light bg-surface-light py-1 shadow-lg dark:border-border-dark dark:bg-surface-dark">
<button
onclick={() => { wsMenuName = null; settingsWorkspace = name; showSettings = true; showWorkspacePicker = false; }}
class="flex w-full items-center gap-2 px-3 py-2 text-left text-sm hover:bg-black/5 dark:hover:bg-white/10"
>
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd" />
</svg>
Settings
</button>
<button
onclick={() => { wsMenuName = null; confirmRemoveWorkspace = name; }}
class="flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-danger hover:bg-black/5 dark:hover:bg-white/10"
@ -347,20 +354,6 @@
</div>
</div>
<!-- Settings -->
<button
onclick={openSettings}
class="flex shrink-0 items-center gap-2 border-t border-border-light px-5 py-3 text-sm opacity-50 hover:bg-black/5 hover:opacity-80 dark:border-border-dark dark:hover:bg-white/10"
>
<svg class="h-4.5 w-4.5" viewBox="0 0 20 20" fill="currentColor">
<path
fill-rule="evenodd"
d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"
clip-rule="evenodd"
/>
</svg>
Settings
</button>
</div>
<!-- Main content panel -->
@ -632,7 +625,7 @@
class="relative flex h-full w-full flex-col overflow-hidden rounded-2xl bg-surface-light transition-transform duration-200 dark:bg-surface-dark {showSettings ? 'scale-100' : 'scale-95'}"
style="border: 1px solid rgba(255,255,255,0.1); box-shadow: 0 25px 60px rgba(0,0,0,0.7), 0 10px 20px rgba(0,0,0,0.5)"
>
<SettingsScreen onclose={closeSettings} />
<SettingsScreen onclose={closeSettings} workspaceName={settingsWorkspace ?? app.config?.current_workspace ?? ""} />
</div>
</div>