From 090aad6bc69e2d602d6e0e35860ddbf58bdd8513 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Thu, 5 Mar 2026 17:39:34 -0800 Subject: [PATCH] Fix project rename, remove confirmation, and auth mode change bugs - Add double-click-to-rename on project names in the sidebar - Replace window.confirm() with inline React confirmation for project removal (confirm dialog didn't block in Tauri webview) - Add serde(default) to skip_serializing fields in Rust models so deserialization doesn't fail when frontend omits secret fields Co-Authored-By: Claude Opus 4.6 --- app/src-tauri/src/models/project.rs | 10 +-- app/src/components/projects/ProjectCard.tsx | 77 ++++++++++++++++++--- 2 files changed, 71 insertions(+), 16 deletions(-) diff --git a/app/src-tauri/src/models/project.rs b/app/src-tauri/src/models/project.rs index 180e4ee..ae2076a 100644 --- a/app/src-tauri/src/models/project.rs +++ b/app/src-tauri/src/models/project.rs @@ -35,7 +35,7 @@ pub struct Project { pub bedrock_config: Option, pub allow_docker_access: bool, pub ssh_key_path: Option, - #[serde(skip_serializing)] + #[serde(skip_serializing, default)] pub git_token: Option, pub git_user_name: Option, pub git_user_email: Option, @@ -100,14 +100,14 @@ impl Default for BedrockAuthMethod { pub struct BedrockConfig { pub auth_method: BedrockAuthMethod, pub aws_region: String, - #[serde(skip_serializing)] + #[serde(skip_serializing, default)] pub aws_access_key_id: Option, - #[serde(skip_serializing)] + #[serde(skip_serializing, default)] pub aws_secret_access_key: Option, - #[serde(skip_serializing)] + #[serde(skip_serializing, default)] pub aws_session_token: Option, pub aws_profile: Option, - #[serde(skip_serializing)] + #[serde(skip_serializing, default)] pub aws_bearer_token: Option, pub model_id: Option, pub disable_prompt_caching: bool, diff --git a/app/src/components/projects/ProjectCard.tsx b/app/src/components/projects/ProjectCard.tsx index 34c0f27..df77c95 100644 --- a/app/src/components/projects/ProjectCard.tsx +++ b/app/src/components/projects/ProjectCard.tsx @@ -30,6 +30,9 @@ export default function ProjectCard({ project }: Props) { const [progressMsg, setProgressMsg] = useState(null); const [activeOperation, setActiveOperation] = useState<"starting" | "stopping" | "resetting" | null>(null); const [operationCompleted, setOperationCompleted] = useState(false); + const [showRemoveConfirm, setShowRemoveConfirm] = useState(false); + const [isEditingName, setIsEditingName] = useState(false); + const [editName, setEditName] = useState(project.name); const isSelected = selectedProjectId === project.id; const isStopped = project.status === "stopped" || project.status === "error"; @@ -54,6 +57,7 @@ export default function ProjectCard({ project }: Props) { // Sync local state when project prop changes (e.g., after save or external update) useEffect(() => { + setEditName(project.name); setPaths(project.paths ?? []); setSshKeyPath(project.ssh_key_path ?? ""); setGitName(project.git_user_name ?? ""); @@ -309,7 +313,40 @@ export default function ProjectCard({ project }: Props) { >
- {project.name} + {isEditingName ? ( + setEditName(e.target.value)} + onBlur={async () => { + setIsEditingName(false); + const trimmed = editName.trim(); + if (trimmed && trimmed !== project.name) { + try { + await update({ ...project, name: trimmed }); + } catch (err) { + console.error("Failed to rename project:", err); + setEditName(project.name); + } + } else { + setEditName(project.name); + } + }} + onKeyDown={(e) => { + if (e.key === "Enter") (e.target as HTMLInputElement).blur(); + if (e.key === "Escape") { setEditName(project.name); setIsEditingName(false); } + }} + onClick={(e) => e.stopPropagation()} + className="text-sm font-medium flex-1 min-w-0 px-1 py-0 bg-[var(--bg-primary)] border border-[var(--accent)] rounded text-[var(--text-primary)] focus:outline-none" + /> + ) : ( + { e.stopPropagation(); setIsEditingName(true); }} + > + {project.name} + + )}
{project.paths.map((pp, i) => ( @@ -385,16 +422,34 @@ export default function ProjectCard({ project }: Props) { disabled={false} label={showConfig ? "Hide" : "Config"} /> - { - if (confirm(`Remove project "${project.name}"?`)) { - await remove(project.id); - } - }} - disabled={loading} - label="Remove" - danger - /> + {showRemoveConfirm ? ( + + Remove? + + + + ) : ( + setShowRemoveConfirm(true)} + disabled={loading} + label="Remove" + danger + /> + )}
{/* Config panel */}