From 0c6da69f39490f96e03759dc0b638718662abe70 Mon Sep 17 00:00:00 2001 From: Daniel Polito Date: Mon, 29 Dec 2025 11:54:49 -0300 Subject: Desktop: Edit Project (#6360) --- .../app/src/components/dialog-edit-project.tsx | 180 +++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 packages/app/src/components/dialog-edit-project.tsx (limited to 'packages/app/src/components') diff --git a/packages/app/src/components/dialog-edit-project.tsx b/packages/app/src/components/dialog-edit-project.tsx new file mode 100644 index 000000000..27ce3903c --- /dev/null +++ b/packages/app/src/components/dialog-edit-project.tsx @@ -0,0 +1,180 @@ +import { Button } from "@opencode-ai/ui/button" +import { useDialog } from "@opencode-ai/ui/context/dialog" +import { Dialog } from "@opencode-ai/ui/dialog" +import { TextField } from "@opencode-ai/ui/text-field" +import { Icon } from "@opencode-ai/ui/icon" +import { createMemo, createSignal, For, Show } from "solid-js" +import { createStore } from "solid-js/store" +import { useGlobalSDK } from "@/context/global-sdk" +import { type LocalProject, getAvatarColors } from "@/context/layout" +import { Avatar } from "@opencode-ai/ui/avatar" + +const AVATAR_COLOR_KEYS = ["pink", "mint", "orange", "purple", "cyan", "lime"] as const + +function getFilename(input: string) { + const parts = input.split("/") + return parts[parts.length - 1] || input +} + +export function DialogEditProject(props: { project: LocalProject }) { + const dialog = useDialog() + const globalSDK = useGlobalSDK() + + const folderName = createMemo(() => getFilename(props.project.worktree)) + const defaultName = createMemo(() => props.project.name || folderName()) + + const [store, setStore] = createStore({ + name: defaultName(), + color: props.project.icon?.color || "pink", + iconUrl: props.project.icon?.url || "", + saving: false, + }) + + const [dragOver, setDragOver] = createSignal(false) + + function handleFileSelect(file: File) { + if (!file.type.startsWith("image/")) return + const reader = new FileReader() + reader.onload = (e) => setStore("iconUrl", e.target?.result as string) + reader.readAsDataURL(file) + } + + function handleDrop(e: DragEvent) { + e.preventDefault() + setDragOver(false) + const file = e.dataTransfer?.files[0] + if (file) handleFileSelect(file) + } + + function handleDragOver(e: DragEvent) { + e.preventDefault() + setDragOver(true) + } + + function handleDragLeave() { + setDragOver(false) + } + + function handleInputChange(e: Event) { + const input = e.target as HTMLInputElement + const file = input.files?.[0] + if (file) handleFileSelect(file) + } + + function clearIcon() { + setStore("iconUrl", "") + } + + async function handleSubmit(e: SubmitEvent) { + e.preventDefault() + if (!props.project.id) return + + setStore("saving", true) + const name = store.name.trim() === folderName() ? "" : store.name.trim() + await globalSDK.client.project.update({ + projectID: props.project.id, + name, + icon: { color: store.color, url: store.iconUrl }, + }) + setStore("saving", false) + dialog.close() + } + + return ( + +
+
+ setStore("name", v)} + /> + +
+ +
+
+
document.getElementById("icon-upload")?.click()} + > + + +
+ } + > + Project icon + +
+ + + +
+ +
+ Click or drag an image + Recommended: 128x128px +
+
+
+ + +
+ +
+ + {(color) => ( + + )} + +
+
+
+ + +
+ + +
+
+
+ ) +} -- cgit v1.2.3