634386fe0f
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com> Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com> Co-authored-by: Brendan Allan <git@brendonovich.dev> Co-authored-by: Kit Langton <kit.langton@gmail.com> Co-authored-by: James Long <longster@gmail.com> Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Jay <53023+jayair@users.noreply.github.com> Co-authored-by: Simon Klee <hello@simonklee.dk> Co-authored-by: Jay <air@live.ca> Co-authored-by: Jack <jack@anoma.ly> Co-authored-by: David Hill <1879069+iamdavidhill@users.noreply.github.com> Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Co-authored-by: opencode <opencode@sst.dev> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Co-authored-by: James Long <jlongster@users.noreply.github.com> Co-authored-by: Dustin Deus <deusdustin@gmail.com> Co-authored-by: 冯基魁 <56265583+fengjikui@users.noreply.github.com> Co-authored-by: Frank <frank@anoma.ly> Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com> Co-authored-by: Victor Navarro <vn4varro@gmail.com> Co-authored-by: Dax Raad <d@ironbay.co> Co-authored-by: Dax <mail@thdxr.com> Co-authored-by: Nabs <nabil@instafork.com> Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com> Co-authored-by: AidenGeunGeun <eastlandwyvern@gmail.com> Co-authored-by: Mark <geraint0923@users.noreply.github.com>
120 lines
3.3 KiB
TypeScript
120 lines
3.3 KiB
TypeScript
import { getFilename } from "@opencode-ai/core/util/path"
|
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
|
import { useMutation } from "@tanstack/solid-query"
|
|
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") {
|
|
await serverCtx().sdk.client.project.update({
|
|
projectID: props.project.id,
|
|
directory: props.project.worktree,
|
|
name,
|
|
icon: { color: store.color || "", override: store.iconOverride || "" },
|
|
commands: { start },
|
|
})
|
|
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
|
|
},
|
|
}
|
|
}
|