Download packages/app/src/components/edit-project.ts from SaylorTwift/opencode: direct link, hf CLI and curl.
- Browser
- Download file 4.09 kB
-
https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/app/src/components/edit-project.ts
- Command line
-
hf download hf://SaylorTwift/opencode/packages/app/src/components/edit-project.ts
-
curl -L -o edit-project.ts https://huggingface.co/SaylorTwift/opencode/resolve/main/packages/app/src/components/edit-project.ts
4.09 kB
| import { getFilename } from "@opencode-ai/core/util/path" | |
| import { useDialog } from "@opencode-ai/ui/context/dialog" | |
| import { useMutation } from "@tanstack/solid-query" | |
| import { normalizeProjectInfo } from "@/context/global-sync/utils" | |
| import { createMemo } from "solid-js" | |
| import { createStore } from "solid-js/store" | |
| import { useGlobal } from "@/context/global" | |
| import { type LocalProject } from "@/context/layout" | |
| import { ServerConnection } from "@/context/server" | |
| export function createEditProjectModel(props: { project: LocalProject; server: ServerConnection.Any }) { | |
| const dialog = useDialog() | |
| const global = useGlobal() | |
| const serverCtx = createMemo(() => global.ensureServerCtx(props.server)) | |
| 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, | |
| iconOverride: props.project.icon?.override, | |
| startup: props.project.commands?.start ?? "", | |
| dragOver: false, | |
| iconHover: false, | |
| }) | |
| let iconInput: HTMLInputElement | undefined | |
| function selectFile(file: File) { | |
| if (!file.type.startsWith("image/")) return | |
| const reader = new FileReader() | |
| reader.onload = (event) => { | |
| const result = event.target?.result | |
| if (typeof result !== "string") return | |
| setStore("iconOverride", result) | |
| setStore("iconHover", false) | |
| } | |
| reader.readAsDataURL(file) | |
| } | |
| function drop(event: DragEvent) { | |
| event.preventDefault() | |
| setStore("dragOver", false) | |
| const file = event.dataTransfer?.files[0] | |
| if (file) selectFile(file) | |
| } | |
| function dragOver(event: DragEvent) { | |
| event.preventDefault() | |
| setStore("dragOver", true) | |
| } | |
| function dragLeave() { | |
| setStore("dragOver", false) | |
| } | |
| function inputChange(event: Event) { | |
| const file = (event.currentTarget as HTMLInputElement).files?.[0] | |
| if (file) selectFile(file) | |
| } | |
| function iconClick() { | |
| if (store.iconOverride && store.iconHover) { | |
| setStore("iconOverride", "") | |
| return | |
| } | |
| iconInput?.click() | |
| } | |
| const save = useMutation(() => ({ | |
| mutationFn: async () => { | |
| const name = store.name.trim() === folderName() ? "" : store.name.trim() | |
| const start = store.startup.trim() | |
| if (props.project.id && props.project.id !== "global") { | |
| if ((await serverCtx().sdk.protocol) !== "v1") return | |
| const project = await serverCtx() | |
| .sdk.client.project.update({ | |
| projectID: props.project.id, | |
| directory: props.project.worktree, | |
| name, | |
| icon: { color: store.color || "", override: store.iconOverride || "" }, | |
| commands: { start }, | |
| }) | |
| .then((result) => result.data) | |
| if (!project) return | |
| // const project = await serverCtx().sdk.api.project.update({ | |
| // projectID: props.project.id, | |
| // name, | |
| // icon: { color: store.color || "", override: store.iconOverride || "" }, | |
| // commands: { start }, | |
| // }) | |
| serverCtx().sync.set("project", (items) => | |
| items.map((item) => (item.id === project.id ? normalizeProjectInfo(project) : item)), | |
| ) | |
| serverCtx().sync.project.icon(props.project.worktree, store.iconOverride || undefined) | |
| dialog.close() | |
| return | |
| } | |
| serverCtx().sync.project.meta(props.project.worktree, { | |
| name, | |
| icon: { color: store.color || undefined, override: store.iconOverride || undefined }, | |
| commands: { start: start || undefined }, | |
| }) | |
| dialog.close() | |
| }, | |
| })) | |
| function submit(event: SubmitEvent) { | |
| event.preventDefault() | |
| if (save.isPending) return | |
| save.mutate() | |
| } | |
| return { | |
| store, | |
| setStore, | |
| folderName, | |
| defaultName, | |
| save, | |
| submit, | |
| drop, | |
| dragOver, | |
| dragLeave, | |
| inputChange, | |
| iconClick, | |
| close() { | |
| dialog.close() | |
| }, | |
| setIconInput(input: HTMLInputElement) { | |
| iconInput = input | |
| }, | |
| } | |
| } | |