Prevent workspace name collisions

Adding explicit duplicate-name checks to the Tauri commands that create
workspaces prevents a new workspace from silently overwriting an
existing one. The change returns an error if a workspace with the given
name already exists.

Also set sane defaults in the setup UI: default workspace name to "Onyx"
for both local and WebDAV flows, default the local folder to the user's
Documents directory via documentDir(), and ensure the create flow resets
the name to the "Onyx" default when starting creation.
This commit is contained in:
Tristan Michael 2026-04-05 14:49:55 -07:00
parent 095ac8fa97
commit a709df609f
2 changed files with 12 additions and 3 deletions

View file

@ -120,6 +120,9 @@ fn add_workspace(
state: State<'_, Mutex<AppState>>, state: State<'_, Mutex<AppState>>,
) -> Result<(), String> { ) -> Result<(), String> {
let mut s = lock_state(&state)?; let mut s = lock_state(&state)?;
if s.config.workspaces.contains_key(&name) {
return Err(format!("A workspace named '{}' already exists", name));
}
let ws = WorkspaceConfig::new(PathBuf::from(&path)); let ws = WorkspaceConfig::new(PathBuf::from(&path));
s.config.add_workspace(name.clone(), ws); s.config.add_workspace(name.clone(), ws);
s.config s.config
@ -569,6 +572,9 @@ fn add_webdav_workspace(
state: State<'_, Mutex<AppState>>, state: State<'_, Mutex<AppState>>,
) -> Result<(), String> { ) -> Result<(), String> {
let mut s = lock_state(&state)?; let mut s = lock_state(&state)?;
if s.config.workspaces.contains_key(&name) {
return Err(format!("A workspace named '{}' already exists", name));
}
let managed_dir = s.app_data_dir.join("workspaces").join(&name); let managed_dir = s.app_data_dir.join("workspaces").join(&name);
std::fs::create_dir_all(&managed_dir).map_err(|e| e.to_string())?; std::fs::create_dir_all(&managed_dir).map_err(|e| e.to_string())?;
TaskRepository::init(managed_dir.clone()).map(|_| ()).map_err(|e| e.to_string())?; TaskRepository::init(managed_dir.clone()).map(|_| ()).map_err(|e| e.to_string())?;

View file

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { documentDir } from "@tauri-apps/api/path";
import { open } from "@tauri-apps/plugin-dialog"; import { open } from "@tauri-apps/plugin-dialog";
import { app } from "../stores/app.svelte"; import { app } from "../stores/app.svelte";
import { getCurrentWindow } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
@ -15,9 +16,11 @@
// ── Shared state ────────────────────────────────────────────────── // ── Shared state ──────────────────────────────────────────────────
let mode = $state<"local" | "webdav" | null>(isMobile ? "webdav" : null); let mode = $state<"local" | "webdav" | null>(isMobile ? "webdav" : null);
let name = $state(""); let name = $state("Onyx");
let path = $state(""); let path = $state("");
documentDir().then((d) => { path = d; }).catch(() => {});
// ── WebDAV state ────────────────────────────────────────────────── // ── WebDAV state ──────────────────────────────────────────────────
let webdavUrl = $state(""); let webdavUrl = $state("");
let webdavUser = $state(""); let webdavUser = $state("");
@ -37,7 +40,7 @@
let previewLoading = $state(false); let previewLoading = $state(false);
// Create workspace state // Create workspace state
let createName = $state(""); let createName = $state("Onyx");
let creating = $state(false); let creating = $state(false);
// ── Derived ─────────────────────────────────────────────────────── // ── Derived ───────────────────────────────────────────────────────
@ -157,7 +160,7 @@
} }
function startCreate() { function startCreate() {
createName = ""; createName = "Onyx";
webdavStep = "create"; webdavStep = "create";
} }