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:
parent
a1e97bc0fe
commit
12afc91110
|
|
@ -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;
|
||||
if (!ws?.webdav_url) return;
|
||||
webdavUrl = ws.webdav_url;
|
||||
try {
|
||||
const domain = new URL(cfg.webdav_url).hostname;
|
||||
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,7 +73,8 @@
|
|||
</header>
|
||||
|
||||
<main class="flex-1 overflow-y-auto p-4">
|
||||
<!-- WebDAV Sync -->
|
||||
<!-- 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
|
||||
|
|
@ -109,7 +108,7 @@
|
|||
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"}
|
||||
{testStatus === "testing" ? "Testing..." : testStatus === "ok" ? "Connected" : testStatus === "fail" ? "Failed -- Retry" : "Test Connection"}
|
||||
</button>
|
||||
<button
|
||||
onclick={saveWebdav}
|
||||
|
|
@ -121,7 +120,6 @@
|
|||
</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>
|
||||
{/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"
|
||||
<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"
|
||||
>
|
||||
<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="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>
|
||||
<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>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<p class="mt-8 text-center text-xs opacity-30">Tauri v2 + Svelte</p>
|
||||
|
|
|
|||
|
|
@ -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,6 +121,35 @@
|
|||
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>
|
||||
|
||||
{#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>
|
||||
|
||||
<button
|
||||
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"
|
||||
>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
{: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>
|
||||
|
|
@ -93,7 +171,7 @@
|
|||
type="text"
|
||||
bind:value={path}
|
||||
readonly
|
||||
placeholder="Select a folder…"
|
||||
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
|
||||
|
|
@ -120,10 +198,85 @@
|
|||
|
||||
<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"
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue