feat: implement support for duplicating profiles from the profile manager

This commit is contained in:
nekename committed 2026-04-14 16:14:10 +01:00
1 parent 3c68144ba6
commit 917855dea5
3 files changed
+47 -17

No files matched your search

+2 -2
View File
@@ -90,13 +90,13 @@ pub async fn delete_profile(device: String, profile: String) {
}
#[command]
pub async fn rename_profile(device: String, old_id: String, new_id: String) -> Result<(), Error> {
pub async fn rename_profile(device: String, old_id: String, new_id: String, retain: bool) -> Result<(), Error> {
let mut locks = acquire_locks_mut().await;
if !DEVICES.contains_key(&device) {
return Err(Error::new(format!("device {device} not found")));
}
locks.profile_stores.rename_profile(&DEVICES.get(&device).unwrap(), &old_id, &new_id).await?;
locks.profile_stores.rename_profile(&DEVICES.get(&device).unwrap(), &old_id, &new_id, retain).await?;
Ok(())
}
+25 -14
View File
@@ -1,6 +1,6 @@
use super::Store;
use crate::shared::{ActionInstance, DEVICES, DeviceInfo, Profile, config_dir};
use crate::shared::{ActionInstance, DEVICES, DeviceInfo, Profile, config_dir, copy_dir};
use std::collections::HashMap;
use std::fs;
@@ -86,9 +86,11 @@ impl ProfileStores {
let _ = fs::remove_dir_all(images_path);
}
pub async fn rename_profile(&mut self, device: &DeviceInfo, old_id: &str, new_id: &str) -> Result<(), anyhow::Error> {
// Remove from the store but don't delete the file
self.remove_profile(&device.id, old_id);
pub async fn rename_profile(&mut self, device: &DeviceInfo, old_id: &str, new_id: &str, retain: bool) -> Result<(), anyhow::Error> {
if !retain {
// Remove from the store but don't delete the file
self.remove_profile(&device.id, old_id);
}
let config_dir = config_dir();
@@ -112,12 +114,16 @@ impl ProfileStores {
}
// Rename the profile file
fs::rename(&old_path, &new_path)?;
if !retain {
fs::rename(&old_path, &new_path)?;
// Clean up empty old directory if profile was in a folder
if let Some(parent) = old_path.parent() {
// This is safe as `remove_dir` errors if the directory is not empty.
let _ = fs::remove_dir(parent);
// Clean up empty old directory if profile was in a folder
if let Some(parent) = old_path.parent() {
// This is safe as `remove_dir` errors if the directory is not empty.
let _ = fs::remove_dir(parent);
}
} else {
fs::copy(&old_path, &new_path)?;
}
// Rename images directory if it exists
@@ -128,12 +134,17 @@ impl ProfileStores {
if let Some(parent) = new_images_path.parent() {
fs::create_dir_all(parent)?;
}
fs::rename(&old_images_path, &new_images_path)?;
// Clean up empty old images directory
if let Some(parent) = old_images_path.parent() {
// This is safe as `remove_dir` errors if the directory is not empty.
let _ = fs::remove_dir(parent);
if !retain {
fs::rename(&old_images_path, &new_images_path)?;
// Clean up empty old images directory
if let Some(parent) = old_images_path.parent() {
// This is safe as `remove_dir` errors if the directory is not empty.
let _ = fs::remove_dir(parent);
}
} else {
copy_dir(&old_images_path, &new_images_path)?;
}
}
+20 -1
View File
@@ -3,6 +3,7 @@
import type { Profile } from "$lib/Profile";
import Browsers from "phosphor-svelte/lib/Browsers";
import Copy from "phosphor-svelte/lib/Copy";
import FloppyDisk from "phosphor-svelte/lib/FloppyDisk";
import Pencil from "phosphor-svelte/lib/Pencil";
import Trash from "phosphor-svelte/lib/Trash";
@@ -89,7 +90,7 @@
}
try {
await invoke("rename_profile", { device: device.id, oldId, newId });
await invoke("rename_profile", { device: device.id, oldId, newId, retain: false });
} catch (error: any) {
message(error, { title: "Failed to rename profile" });
console.error(error);
@@ -123,6 +124,21 @@
}
$: if (renameInput) renameInput.focus();
async function duplicateProfile(id: string) {
let newId = id + " Copy";
// Check if a profile with the new ID already exists
const allProfiles = Object.values(folders).flat();
let counter = 1;
while (allProfiles.includes(newId)) {
counter++;
newId = `${id} Copy ${counter}`;
}
await invoke("rename_profile", { device: device.id, oldId: id, newId, retain: true });
await getProfiles(device);
}
let oldValue: string;
$: {
if (value == "opendeck_edit_profiles") {
@@ -259,6 +275,9 @@
</button>
{:else}
<span class="grow text-neutral-400">{id ? profile.split("/")[1] : profile}</span>
<button on:click={() => duplicateProfile(profile)} title="Duplicate" aria-label="Duplicate">
<Copy size="20" class="text-neutral-400" />
</button>
{#if profile != value}
<button
on:click={() => renamingProfile = newId = profile}