|
|
@@ -1,4 +1,5 @@
|
|
|
-import { createEffect, createMemo, createSignal, For, Show, type Accessor, type JSX } from "solid-js"
|
|
|
+import { createEffect, createMemo, For, Show, type Accessor, type JSX } from "solid-js"
|
|
|
+import { createStore } from "solid-js/store"
|
|
|
import { base64Encode } from "@opencode-ai/util/encode"
|
|
|
import { Button } from "@opencode-ai/ui/button"
|
|
|
import { ContextMenu } from "@opencode-ai/ui/context-menu"
|
|
|
@@ -7,7 +8,7 @@ import { Icon } from "@opencode-ai/ui/icon"
|
|
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
|
|
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
|
|
import { createSortable } from "@thisbeyond/solid-dnd"
|
|
|
-import { type LocalProject } from "@/context/layout"
|
|
|
+import { useLayout, type LocalProject } from "@/context/layout"
|
|
|
import { useGlobalSync } from "@/context/global-sync"
|
|
|
import { useLanguage } from "@/context/language"
|
|
|
import { useNotification } from "@/context/notification"
|
|
|
@@ -60,6 +61,7 @@ const ProjectTile = (props: {
|
|
|
selected: Accessor<boolean>
|
|
|
active: Accessor<boolean>
|
|
|
overlay: Accessor<boolean>
|
|
|
+ suppressHover: Accessor<boolean>
|
|
|
dirs: Accessor<string[]>
|
|
|
onProjectMouseEnter: (worktree: string, event: MouseEvent) => void
|
|
|
onProjectMouseLeave: (worktree: string) => void
|
|
|
@@ -71,9 +73,11 @@ const ProjectTile = (props: {
|
|
|
closeProject: (directory: string) => void
|
|
|
setMenu: (value: boolean) => void
|
|
|
setOpen: (value: boolean) => void
|
|
|
+ setSuppressHover: (value: boolean) => void
|
|
|
language: ReturnType<typeof useLanguage>
|
|
|
}): JSX.Element => {
|
|
|
const notification = useNotification()
|
|
|
+ const layout = useLayout()
|
|
|
const unseenCount = createMemo(() =>
|
|
|
props.dirs().reduce((total, directory) => total + notification.project.unseenCount(directory), 0),
|
|
|
)
|
|
|
@@ -107,17 +111,28 @@ const ProjectTile = (props: {
|
|
|
}}
|
|
|
onMouseEnter={(event: MouseEvent) => {
|
|
|
if (!props.overlay()) return
|
|
|
+ if (props.suppressHover()) return
|
|
|
props.onProjectMouseEnter(props.project.worktree, event)
|
|
|
}}
|
|
|
onMouseLeave={() => {
|
|
|
+ if (props.suppressHover()) props.setSuppressHover(false)
|
|
|
if (!props.overlay()) return
|
|
|
props.onProjectMouseLeave(props.project.worktree)
|
|
|
}}
|
|
|
onFocus={() => {
|
|
|
if (!props.overlay()) return
|
|
|
+ if (props.suppressHover()) return
|
|
|
props.onProjectFocus(props.project.worktree)
|
|
|
}}
|
|
|
- onClick={() => props.navigateToProject(props.project.worktree)}
|
|
|
+ onClick={() => {
|
|
|
+ if (props.selected()) {
|
|
|
+ props.setSuppressHover(true)
|
|
|
+ layout.sidebar.toggle()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ props.setSuppressHover(false)
|
|
|
+ props.navigateToProject(props.project.worktree)
|
|
|
+ }}
|
|
|
onBlur={() => props.setOpen(false)}
|
|
|
>
|
|
|
<ProjectIcon project={props.project} notify />
|
|
|
@@ -278,16 +293,19 @@ export const SortableProject = (props: {
|
|
|
const workspaces = createMemo(() => props.ctx.workspaceIds(props.project).slice(0, 2))
|
|
|
const workspaceEnabled = createMemo(() => props.ctx.workspacesEnabled(props.project))
|
|
|
const dirs = createMemo(() => props.ctx.workspaceIds(props.project))
|
|
|
- const [open, setOpen] = createSignal(false)
|
|
|
- const [menu, setMenu] = createSignal(false)
|
|
|
+ const [state, setState] = createStore({
|
|
|
+ open: false,
|
|
|
+ menu: false,
|
|
|
+ suppressHover: false,
|
|
|
+ })
|
|
|
|
|
|
const preview = createMemo(() => !props.mobile && props.ctx.sidebarOpened())
|
|
|
const overlay = createMemo(() => !props.mobile && !props.ctx.sidebarOpened())
|
|
|
const active = createMemo(() =>
|
|
|
projectTileActive({
|
|
|
- menu: menu(),
|
|
|
+ menu: state.menu,
|
|
|
preview: preview(),
|
|
|
- open: open(),
|
|
|
+ open: state.open,
|
|
|
overlay: overlay(),
|
|
|
hoverProject: props.ctx.hoverProject(),
|
|
|
worktree: props.project.worktree,
|
|
|
@@ -296,8 +314,14 @@ export const SortableProject = (props: {
|
|
|
|
|
|
createEffect(() => {
|
|
|
if (preview()) return
|
|
|
- if (!open()) return
|
|
|
- setOpen(false)
|
|
|
+ if (!state.open) return
|
|
|
+ setState("open", false)
|
|
|
+ })
|
|
|
+
|
|
|
+ createEffect(() => {
|
|
|
+ if (!selected()) return
|
|
|
+ if (!state.open) return
|
|
|
+ setState("open", false)
|
|
|
})
|
|
|
|
|
|
const label = (directory: string) => {
|
|
|
@@ -328,6 +352,7 @@ export const SortableProject = (props: {
|
|
|
selected={selected}
|
|
|
active={active}
|
|
|
overlay={overlay}
|
|
|
+ suppressHover={() => state.suppressHover}
|
|
|
dirs={dirs}
|
|
|
onProjectMouseEnter={props.ctx.onProjectMouseEnter}
|
|
|
onProjectMouseLeave={props.ctx.onProjectMouseLeave}
|
|
|
@@ -337,8 +362,9 @@ export const SortableProject = (props: {
|
|
|
toggleProjectWorkspaces={props.ctx.toggleProjectWorkspaces}
|
|
|
workspacesEnabled={props.ctx.workspacesEnabled}
|
|
|
closeProject={props.ctx.closeProject}
|
|
|
- setMenu={setMenu}
|
|
|
- setOpen={setOpen}
|
|
|
+ setMenu={(value) => setState("menu", value)}
|
|
|
+ setOpen={(value) => setState("open", value)}
|
|
|
+ setSuppressHover={(value) => setState("suppressHover", value)}
|
|
|
language={language}
|
|
|
/>
|
|
|
)
|
|
|
@@ -346,17 +372,18 @@ export const SortableProject = (props: {
|
|
|
return (
|
|
|
// @ts-ignore
|
|
|
<div use:sortable classList={{ "opacity-30": sortable.isActiveDraggable }}>
|
|
|
- <Show when={preview()} fallback={tile()}>
|
|
|
+ <Show when={preview() && !selected()} fallback={tile()}>
|
|
|
<HoverCard
|
|
|
- open={open() && !menu()}
|
|
|
+ open={!state.suppressHover && state.open && !state.menu}
|
|
|
openDelay={0}
|
|
|
closeDelay={0}
|
|
|
placement="right-start"
|
|
|
gutter={6}
|
|
|
trigger={tile()}
|
|
|
onOpenChange={(value) => {
|
|
|
- if (menu()) return
|
|
|
- setOpen(value)
|
|
|
+ if (state.menu) return
|
|
|
+ if (value && state.suppressHover) return
|
|
|
+ setState("open", value)
|
|
|
if (value) props.ctx.setHoverSession(undefined)
|
|
|
}}
|
|
|
>
|
|
|
@@ -371,7 +398,7 @@ export const SortableProject = (props: {
|
|
|
projectChildren={projectChildren}
|
|
|
workspaceSessions={workspaceSessions}
|
|
|
workspaceChildren={workspaceChildren}
|
|
|
- setOpen={setOpen}
|
|
|
+ setOpen={(value) => setState("open", value)}
|
|
|
ctx={props.ctx}
|
|
|
language={language}
|
|
|
/>
|