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, For, Show } from "solid-js" import { createStore } from "solid-js/store" import { useGlobalSDK } from "@/context/global-sdk" import { useGlobalSync } from "@/context/global-sync" import { type LocalProject, getAvatarColors } from "@/context/layout" import { getFilename } from "@opencode-ai/util/path" import { Avatar } from "@opencode-ai/ui/avatar" import { useLanguage } from "@/context/language" const AVATAR_COLOR_KEYS = ["pink", "mint", "orange", "purple", "cyan", "lime"] as const export function DialogEditProject(props: { project: LocalProject }) { const dialog = useDialog() const globalSDK = useGlobalSDK() const globalSync = useGlobalSync() const language = useLanguage() 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?.override || "", startup: props.project.commands?.start ?? "", saving: false, dragOver: false, iconHover: false, }) let iconInput: HTMLInputElement | undefined function handleFileSelect(file: File) { if (!file.type.startsWith("image/")) return const reader = new FileReader() reader.onload = (e) => { setStore("iconUrl", e.target?.result as string) setStore("iconHover", false) } reader.readAsDataURL(file) } function handleDrop(e: DragEvent) { e.preventDefault() setStore("dragOver", false) const file = e.dataTransfer?.files[0] if (file) handleFileSelect(file) } function handleDragOver(e: DragEvent) { e.preventDefault() setStore("dragOver", true) } function handleDragLeave() { setStore("dragOver", 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() await Promise.resolve() .then(async () => { setStore("saving", true) const name = store.name.trim() === folderName() ? "" : store.name.trim() const start = store.startup.trim() if (props.project.id && props.project.id !== "global") { await globalSDK.client.project.update({ projectID: props.project.id, directory: props.project.worktree, name, icon: { color: store.color, override: store.iconUrl }, commands: { start }, }) globalSync.project.icon(props.project.worktree, store.iconUrl || undefined) dialog.close() return } globalSync.project.meta(props.project.worktree, { name, icon: { color: store.color, override: store.iconUrl || undefined }, commands: { start: start || undefined }, }) dialog.close() }) .finally(() => { setStore("saving", false) }) } return (
setStore("name", v)} />
setStore("iconHover", true)} onMouseLeave={() => setStore("iconHover", false)} >
{ if (store.iconUrl && store.iconHover) { clearIcon() } else { iconInput?.click() } }} >
} > {language.t("dialog.project.edit.icon.alt")}
{ iconInput = el }} type="file" accept="image/*" class="hidden" onChange={handleInputChange} />
{language.t("dialog.project.edit.icon.hint")} {language.t("dialog.project.edit.icon.recommended")}
{(color) => ( )}
setStore("startup", v)} spellcheck={false} class="max-h-14 w-full overflow-y-auto font-mono text-xs" />
) }