feat(dashboard): clone profiles from any source

This commit is contained in:
WompaJango
2026-06-13 07:33:58 -07:00
committed by Teknium
parent 3380563d94
commit 28bf8fb47d
31 changed files with 182 additions and 105 deletions
+1 -1
View File
@@ -220,7 +220,7 @@ export default function ProfileBuilderPage() {
try {
const res = await api.createProfile({
name: n,
clone_from_default: false,
clone_from: null,
description: description.trim() || undefined,
provider: pickedModel?.provider,
model: pickedModel?.model,
+32 -26
View File
@@ -35,11 +35,11 @@ import { Badge } from "@nous-research/ui/ui/components/badge";
import { Button } from "@nous-research/ui/ui/components/button";
import { Input } from "@nous-research/ui/ui/components/input";
import { Label } from "@nous-research/ui/ui/components/label";
import { Checkbox } from "@nous-research/ui/ui/components/checkbox";
import {
Select,
SelectOption,
} from "@nous-research/ui/ui/components/select";
import { Checkbox } from "@nous-research/ui/ui/components/checkbox";
import { useI18n } from "@/i18n";
import { usePageHeader } from "@/contexts/usePageHeader";
import { cn, themedBody } from "@/lib/utils";
@@ -312,7 +312,7 @@ export default function ProfilesPage() {
// Create modal
const [createModalOpen, setCreateModalOpen] = useState(false);
const [newName, setNewName] = useState("");
const [cloneFromDefault, setCloneFromDefault] = useState(true);
const [cloneFrom, setCloneFrom] = useState<string | null>("default");
const [cloneAll, setCloneAll] = useState(false);
const [noSkills, setNoSkills] = useState(false);
const [newDescription, setNewDescription] = useState("");
@@ -429,7 +429,7 @@ export default function ProfilesPage() {
}
setCreating(true);
try {
const cloning = cloneAll || cloneFromDefault;
const cloning = cloneFrom !== null;
const picked = modelChoice
? modelChoices?.find(
(c) => `${c.provider}\u0000${c.model}` === modelChoice,
@@ -437,8 +437,8 @@ export default function ProfilesPage() {
: undefined;
const res = await api.createProfile({
name,
clone_from_default: cloneAll ? false : cloneFromDefault,
clone_all: cloneAll,
clone_from: cloneFrom,
clone_all: cloning && cloneAll,
no_skills: cloning ? false : noSkills,
description: newDescription.trim() || undefined,
provider: picked?.provider,
@@ -455,7 +455,7 @@ export default function ProfilesPage() {
setNewDescription("");
setNoSkills(false);
setCloneAll(false);
setCloneFromDefault(true);
setCloneFrom("default");
setModelChoice("");
setCreateModalOpen(false);
load();
@@ -772,7 +772,7 @@ export default function ProfilesPage() {
};
}, [setEnd, t.common.create, loading, navigate]);
const cloning = cloneAll || cloneFromDefault;
const cloning = cloneFrom !== null;
if (loading) {
return (
@@ -862,6 +862,26 @@ export default function ProfilesPage() {
</p>
</div>
<div className="grid gap-2">
<Label htmlFor="clone-from">{t.profiles.cloneFrom}</Label>
<Select
id="clone-from"
value={cloneFrom ?? ""}
onValueChange={(v) => {
const next = v || null;
setCloneFrom(next);
if (next === null) setCloneAll(false);
}}
>
<SelectOption value="">{t.profiles.cloneFromNone}</SelectOption>
{profiles.map((profile) => (
<SelectOption key={profile.name} value={profile.name}>
{profile.name}
</SelectOption>
))}
</Select>
</div>
<div className="grid gap-2">
<Label htmlFor="profile-description">
{L.descriptionOptional}
@@ -909,33 +929,19 @@ export default function ProfilesPage() {
{L.advancedOptions}
</legend>
<div className="flex items-center gap-2.5">
<Checkbox
checked={cloneFromDefault}
id="clone-from-default"
disabled={cloneAll}
onCheckedChange={(checked) =>
setCloneFromDefault(checked === true)
}
/>
<Label
className="font-mondwest normal-case tracking-normal text-sm cursor-pointer"
htmlFor="clone-from-default"
>
{t.profiles.cloneFromDefault}
</Label>
</div>
<div className="flex items-center gap-2.5">
<Checkbox
checked={cloneAll}
disabled={!cloning}
id="clone-all"
onCheckedChange={(checked) => setCloneAll(checked === true)}
/>
<Label
className="font-mondwest normal-case tracking-normal text-sm cursor-pointer"
className={cn(
"font-mondwest normal-case tracking-normal text-sm cursor-pointer",
!cloning && "opacity-50",
)}
htmlFor="clone-all"
>
{L.cloneAll}