|
|
@@ -1,5 +1,5 @@
|
|
|
import type { Session } from "@opencode-ai/sdk/v2/client"
|
|
|
-import { createMemo, createSignal, For, Match, Show, Switch } from "solid-js"
|
|
|
+import { createMemo, For, Match, Show, Switch } from "solid-js"
|
|
|
import { createStore } from "solid-js/store"
|
|
|
import { useQuery } from "@tanstack/solid-query"
|
|
|
import { Button } from "@opencode-ai/ui/button"
|
|
|
@@ -18,7 +18,6 @@ import { DateTime } from "luxon"
|
|
|
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
|
|
import { DialogSelectDirectory } from "@/components/dialog-select-directory"
|
|
|
import { DialogSelectServer } from "@/components/dialog-select-server"
|
|
|
-import { DialogSelectModel } from "@/components/dialog-select-model"
|
|
|
import { useServer } from "@/context/server"
|
|
|
import { useGlobalSync } from "@/context/global-sync"
|
|
|
import { useLanguage } from "@/context/language"
|
|
|
@@ -467,11 +466,6 @@ function LegacyHome() {
|
|
|
const navigate = useNavigate()
|
|
|
const server = useServer()
|
|
|
const language = useLanguage()
|
|
|
-
|
|
|
- const [promptText, setPromptText] = createSignal("")
|
|
|
- const [selectedAgent, setSelectedAgent] = createSignal("frontend-specialist")
|
|
|
- const [showProjectsDropdown, setShowProjectsDropdown] = createSignal(false)
|
|
|
-
|
|
|
const homedir = createMemo(() => sync.data.path.home)
|
|
|
const recent = createMemo(() => {
|
|
|
return sync.data.project
|
|
|
@@ -480,8 +474,6 @@ function LegacyHome() {
|
|
|
.slice(0, 5)
|
|
|
})
|
|
|
|
|
|
- const currentProject = createMemo(() => recent()[0]?.worktree)
|
|
|
-
|
|
|
const serverDotClass = createMemo(() => {
|
|
|
const healthy = server.healthy()
|
|
|
if (healthy === true) return "bg-icon-success-base"
|
|
|
@@ -520,185 +512,69 @@ function LegacyHome() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- function handleModelSelect() {
|
|
|
- dialog.show(() => <DialogSelectModel />)
|
|
|
- }
|
|
|
-
|
|
|
- function toggleAgent() {
|
|
|
- const agents = ["frontend-specialist", "build", "general"]
|
|
|
- const nextIndex = (agents.indexOf(selectedAgent()) + 1) % agents.length
|
|
|
- setSelectedAgent(agents[nextIndex])
|
|
|
- }
|
|
|
-
|
|
|
- function handleSubmit() {
|
|
|
- const projectToOpen = currentProject()
|
|
|
- if (projectToOpen) {
|
|
|
- openProject(projectToOpen)
|
|
|
- } else {
|
|
|
- chooseProject()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const activeModelName = createMemo(() => {
|
|
|
- const model = sync.data.config.model
|
|
|
- if (!model) return "GPT-5.7 Pro"
|
|
|
- const parts = model.split("/")
|
|
|
- return parts[parts.length - 1]
|
|
|
- })
|
|
|
-
|
|
|
return (
|
|
|
- <div class="mx-auto mt-24 w-full max-w-2xl px-6 flex flex-col items-center">
|
|
|
- <div class="flex flex-col items-center gap-3 mb-10">
|
|
|
- <div onClick={chooseProject} class="cursor-pointer hover:opacity-25 transition-opacity duration-200">
|
|
|
- <Logo class="w-48 opacity-15" />
|
|
|
- </div>
|
|
|
- <Button
|
|
|
- size="normal"
|
|
|
- variant="ghost"
|
|
|
- class="text-12-regular text-text-weak px-3"
|
|
|
- onClick={() => dialog.show(() => <DialogSelectServer />)}
|
|
|
- >
|
|
|
- <div
|
|
|
- classList={{
|
|
|
- "size-1.5 rounded-full mr-2": true,
|
|
|
- [serverDotClass()]: true,
|
|
|
- }}
|
|
|
- />
|
|
|
- {server.name}
|
|
|
- </Button>
|
|
|
- </div>
|
|
|
-
|
|
|
+ <div class="mx-auto mt-55 w-full md:w-auto px-4">
|
|
|
+ <Logo class="md:w-xl opacity-12" />
|
|
|
+ <Button
|
|
|
+ size="large"
|
|
|
+ variant="ghost"
|
|
|
+ class="mt-4 mx-auto text-14-regular text-text-weak"
|
|
|
+ onClick={() => dialog.show(() => <DialogSelectServer />)}
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ classList={{
|
|
|
+ "size-2 rounded-full": true,
|
|
|
+ [serverDotClass()]: true,
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ {server.name}
|
|
|
+ </Button>
|
|
|
<Switch>
|
|
|
- <Match when={recent().length > 0}>
|
|
|
- <div class="w-full flex flex-col items-center gap-6">
|
|
|
- <div class="text-20-medium text-text-strong text-center">{language.t("session.new.title")}</div>
|
|
|
-
|
|
|
- <div class="w-full bg-surface-base border border-border-base rounded-xl p-4 flex flex-col gap-3 shadow-md relative">
|
|
|
- <textarea
|
|
|
- class="bg-transparent border-none outline-none text-14-regular text-text-base placeholder-text-weak w-full resize-none h-20 focus:outline-none"
|
|
|
- placeholder="Ask anything, / for commands, @ for context..."
|
|
|
- value={promptText()}
|
|
|
- onInput={(e) => setPromptText(e.currentTarget.value)}
|
|
|
- onKeyDown={(e) => {
|
|
|
- if (e.key === "Enter" && !e.shiftKey) {
|
|
|
- e.preventDefault()
|
|
|
- handleSubmit()
|
|
|
- }
|
|
|
- }}
|
|
|
- />
|
|
|
-
|
|
|
- <div class="flex flex-wrap items-center gap-2 pt-3 border-t border-border-weak-base">
|
|
|
- <Button
|
|
|
- size="small"
|
|
|
- variant="ghost"
|
|
|
- class="text-12-medium text-text-weak hover:text-text-strong flex items-center gap-1.5 px-2.5 py-1 bg-surface-raised-base hover:bg-surface-raised-base-hover border border-border-weak-base rounded-md"
|
|
|
- onClick={toggleAgent}
|
|
|
- >
|
|
|
- <Icon name="sliders" size="small" class="shrink-0" />
|
|
|
- <span>Agent: {selectedAgent()}</span>
|
|
|
- </Button>
|
|
|
-
|
|
|
- <Button
|
|
|
- size="small"
|
|
|
- variant="ghost"
|
|
|
- class="text-12-medium text-text-weak hover:text-text-strong flex items-center gap-1.5 px-2.5 py-1 bg-surface-raised-base hover:bg-surface-raised-base-hover border border-border-weak-base rounded-md"
|
|
|
- onClick={handleModelSelect}
|
|
|
- >
|
|
|
- <Icon name="brain" size="small" class="shrink-0" />
|
|
|
- <span>Model: {activeModelName()}</span>
|
|
|
- </Button>
|
|
|
-
|
|
|
- <div class="relative">
|
|
|
+ <Match when={sync.data.project.length > 0}>
|
|
|
+ <div class="mt-20 w-full flex flex-col gap-4">
|
|
|
+ <div class="flex gap-2 items-center justify-between pl-3">
|
|
|
+ <div class="text-14-medium text-text-strong">{language.t("home.recentProjects")}</div>
|
|
|
+ <Button icon="folder-add-left" size="normal" class="pl-2 pr-3" onClick={chooseProject}>
|
|
|
+ {language.t("command.project.open")}
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ <ul class="flex flex-col gap-2">
|
|
|
+ <For each={recent()}>
|
|
|
+ {(project) => (
|
|
|
<Button
|
|
|
- size="small"
|
|
|
+ size="large"
|
|
|
variant="ghost"
|
|
|
- class="text-12-medium text-text-weak hover:text-text-strong flex items-center gap-1.5 px-2.5 py-1 bg-surface-raised-base hover:bg-surface-raised-base-hover border border-border-weak-base rounded-md"
|
|
|
- onClick={() => setShowProjectsDropdown(!showProjectsDropdown())}
|
|
|
+ class="text-14-mono text-left justify-between px-3"
|
|
|
+ onClick={() => openProject(project.worktree)}
|
|
|
>
|
|
|
- <Icon name="folder" size="small" class="shrink-0" />
|
|
|
- <span>Project: {currentProject() ? getFilename(currentProject()) : "Select Project"}</span>
|
|
|
- </Button>
|
|
|
-
|
|
|
- <Show when={showProjectsDropdown()}>
|
|
|
- <div class="absolute left-0 mt-1 w-64 bg-surface-raised-base border border-border-base rounded-lg p-2 shadow-lg z-50 flex flex-col gap-1">
|
|
|
- <div class="text-10-semibold text-text-weak px-2 py-1 uppercase tracking-wider">
|
|
|
- {language.t("home.recentProjects")}
|
|
|
- </div>
|
|
|
- <For each={recent()}>
|
|
|
- {(project) => (
|
|
|
- <button
|
|
|
- class="text-12-mono text-left px-2 py-1.5 hover:bg-surface-raised-base-hover rounded flex items-center justify-between w-full"
|
|
|
- onClick={() => {
|
|
|
- openProject(project.worktree)
|
|
|
- setShowProjectsDropdown(false)
|
|
|
- }}
|
|
|
- >
|
|
|
- <span class="truncate">{getFilename(project.worktree)}</span>
|
|
|
- <span class="text-10-regular text-text-weak shrink-0 pl-2">
|
|
|
- {DateTime.fromMillis(project.time.updated ?? project.time.created).toRelative()}
|
|
|
- </span>
|
|
|
- </button>
|
|
|
- )}
|
|
|
- </For>
|
|
|
- <div class="border-t border-border-weak-base my-1" />
|
|
|
- <button
|
|
|
- class="text-12-medium text-text-strong text-left px-2 py-1.5 hover:bg-surface-raised-base-hover rounded flex items-center gap-2 w-full"
|
|
|
- onClick={() => {
|
|
|
- setShowProjectsDropdown(false)
|
|
|
- chooseProject()
|
|
|
- }}
|
|
|
- >
|
|
|
- <Icon name="folder-add-left" size="small" />
|
|
|
- {language.t("command.project.open")}
|
|
|
- </button>
|
|
|
+ {project.worktree.replace(homedir(), "~")}
|
|
|
+ <div class="text-14-regular text-text-weak">
|
|
|
+ {DateTime.fromMillis(project.time.updated ?? project.time.created).toRelative()}
|
|
|
</div>
|
|
|
- </Show>
|
|
|
- </div>
|
|
|
-
|
|
|
- <Button
|
|
|
- size="small"
|
|
|
- variant="ghost"
|
|
|
- class="text-12-medium text-text-weak flex items-center gap-1.5 px-2.5 py-1 bg-surface-raised-base border border-border-weak-base rounded-md cursor-default pointer-events-none"
|
|
|
- >
|
|
|
- <Icon name="branch" size="small" class="shrink-0" />
|
|
|
- <span>Branch: dev</span>
|
|
|
- </Button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ </Button>
|
|
|
+ )}
|
|
|
+ </For>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </Match>
|
|
|
+ <Match when={!sync.ready}>
|
|
|
+ <div class="mt-30 mx-auto flex flex-col items-center gap-3">
|
|
|
+ <div class="text-12-regular text-text-weak">{language.t("common.loading")}</div>
|
|
|
+ <Button class="px-3" onClick={chooseProject}>
|
|
|
+ {language.t("command.project.open")}
|
|
|
+ </Button>
|
|
|
</div>
|
|
|
</Match>
|
|
|
-
|
|
|
<Match when={true}>
|
|
|
- <div class="w-full flex flex-col items-center gap-6">
|
|
|
- <div class="text-20-medium text-text-strong text-center">{language.t("home.empty.title")}</div>
|
|
|
-
|
|
|
- <div class="w-full bg-surface-base border border-border-base rounded-xl p-4 flex flex-col gap-3 shadow-md">
|
|
|
- <div class="text-14-regular text-text-weak w-full min-h-[4rem] cursor-pointer" onClick={chooseProject}>
|
|
|
- Ask anything, / for commands, @ for context...
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="flex flex-wrap items-center gap-2 pt-3 border-t border-border-weak-base">
|
|
|
- <Button
|
|
|
- size="small"
|
|
|
- variant="ghost"
|
|
|
- class="text-12-medium text-text-weak hover:text-text-strong flex items-center gap-1.5 px-2.5 py-1 bg-surface-raised-base hover:bg-surface-raised-base-hover border border-border-weak-base rounded-md"
|
|
|
- onClick={chooseProject}
|
|
|
- >
|
|
|
- <Icon name="folder" size="small" class="shrink-0" />
|
|
|
- <span>Open project</span>
|
|
|
- </Button>
|
|
|
-
|
|
|
- <Button
|
|
|
- size="small"
|
|
|
- variant="ghost"
|
|
|
- class="text-12-medium text-text-weak hover:text-text-strong flex items-center gap-1.5 px-2.5 py-1 bg-surface-raised-base hover:bg-surface-raised-base-hover border border-border-weak-base rounded-md"
|
|
|
- onClick={handleModelSelect}
|
|
|
- >
|
|
|
- <Icon name="brain" size="small" class="shrink-0" />
|
|
|
- <span>Model: {activeModelName()}</span>
|
|
|
- </Button>
|
|
|
- </div>
|
|
|
+ <div class="mt-30 mx-auto flex flex-col items-center gap-3">
|
|
|
+ <Icon name="folder-add-left" size="large" />
|
|
|
+ <div class="flex flex-col gap-1 items-center justify-center">
|
|
|
+ <div class="text-14-medium text-text-strong">{language.t("home.empty.title")}</div>
|
|
|
+ <div class="text-12-regular text-text-weak">{language.t("home.empty.description")}</div>
|
|
|
</div>
|
|
|
+ <Button class="px-3 mt-1" onClick={chooseProject}>
|
|
|
+ {language.t("command.project.open")}
|
|
|
+ </Button>
|
|
|
</div>
|
|
|
</Match>
|
|
|
</Switch>
|