summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorBrendan Allan <[email protected]>2026-03-23 16:58:20 +0800
committerGitHub <[email protected]>2026-03-23 08:58:20 +0000
commit0a7dfc03ee1dbc29d65605e8ca37ed9d137bd2ec (patch)
treed3ab35650170465dd4e892290ef908d1c7de7e34 /packages
parent4c27e7fc6499dee385e718d523c4f0612bd8a063 (diff)
downloadopencode-0a7dfc03ee1dbc29d65605e8ca37ed9d137bd2ec.tar.gz
opencode-0a7dfc03ee1dbc29d65605e8ca37ed9d137bd2ec.zip
fix(app): lift up project hover state to layout (#18732)
Diffstat (limited to 'packages')
-rw-r--r--packages/app/src/pages/layout.tsx4
-rw-r--r--packages/app/src/pages/layout/sidebar-project.tsx29
2 files changed, 12 insertions, 21 deletions
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index 88572503f..d8b073258 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -1989,6 +1989,10 @@ export default function Layout(props: ParentProps) {
onProjectMouseEnter: (worktree, event) => aim.enter(worktree, event),
onProjectMouseLeave: (worktree) => aim.leave(worktree),
onProjectFocus: (worktree) => aim.activate(worktree),
+ onHoverOpenChanged: (worktree, hoverOpen) => {
+ if (!hoverOpen && state.hoverProject && state.hoverProject !== worktree) return
+ setState("hoverProject", hoverOpen ? worktree : undefined)
+ },
navigateToProject,
openSidebar: () => layout.sidebar.open(),
closeProject,
diff --git a/packages/app/src/pages/layout/sidebar-project.tsx b/packages/app/src/pages/layout/sidebar-project.tsx
index 99f1edb74..aff0645dd 100644
--- a/packages/app/src/pages/layout/sidebar-project.tsx
+++ b/packages/app/src/pages/layout/sidebar-project.tsx
@@ -23,6 +23,7 @@ export type ProjectSidebarContext = {
onProjectMouseEnter: (worktree: string, event: MouseEvent) => void
onProjectMouseLeave: (worktree: string) => void
onProjectFocus: (worktree: string) => void
+ onHoverOpenChanged: (worktree: string, hovered: boolean) => void
navigateToProject: (directory: string) => void
openSidebar: () => void
closeProject: (directory: string) => void
@@ -197,7 +198,6 @@ const ProjectPreviewPanel = (props: {
projectChildren: Accessor<Map<string, string[]>>
workspaceSessions: (directory: string) => ReturnType<typeof sortedRootSessions>
workspaceChildren: (directory: string) => Map<string, string[]>
- setOpen: (value: boolean) => void
ctx: ProjectSidebarContext
language: ReturnType<typeof useLanguage>
}): JSX.Element => (
@@ -264,7 +264,7 @@ const ProjectPreviewPanel = (props: {
class="flex w-full text-left justify-start text-text-base px-2 hover:bg-transparent active:bg-transparent"
onClick={() => {
props.ctx.openSidebar()
- props.setOpen(false)
+ props.ctx.onHoverOpenChanged(props.project.worktree, false)
if (props.selected()) return
props.ctx.navigateToProject(props.project.worktree)
}}
@@ -289,28 +289,16 @@ export const SortableProject = (props: {
const workspaceEnabled = createMemo(() => props.ctx.workspacesEnabled(props.project))
const dirs = createMemo(() => props.ctx.workspaceIds(props.project))
const [state, setState] = createStore({
- open: false,
menu: false,
suppressHover: false,
})
+ const isHoverProject = () => props.ctx.hoverProject() === props.project.worktree
const preview = createMemo(() => !props.mobile && props.ctx.sidebarOpened())
const overlay = createMemo(() => !props.mobile && !props.ctx.sidebarOpened())
- const active = createMemo(
- () => state.menu || (preview() ? state.open : overlay() && props.ctx.hoverProject() === props.project.worktree),
- )
+ const active = createMemo(() => state.menu || (preview() ? isHoverProject() : overlay() && isHoverProject()))
- createEffect(() => {
- if (preview()) return
- if (!state.open) return
- setState("open", false)
- })
-
- createEffect(() => {
- if (!selected()) return
- if (!state.open) return
- setState("open", false)
- })
+ const hoverOpen = () => isHoverProject() && preview() && !selected() && !state.menu
const label = (directory: string) => {
const [data] = globalSync.child(directory, { bootstrap: false })
@@ -351,7 +339,7 @@ export const SortableProject = (props: {
workspacesEnabled={props.ctx.workspacesEnabled}
closeProject={props.ctx.closeProject}
setMenu={(value) => setState("menu", value)}
- setOpen={(value) => setState("open", value)}
+ setOpen={(value) => props.ctx.onHoverOpenChanged(props.project.worktree, value)}
setSuppressHover={(value) => setState("suppressHover", value)}
language={language}
/>
@@ -362,7 +350,7 @@ export const SortableProject = (props: {
<div use:sortable classList={{ "opacity-30": sortable.isActiveDraggable }}>
<Show when={preview() && !selected()} fallback={tile()}>
<HoverCard
- open={!state.suppressHover && state.open && !state.menu}
+ open={!state.suppressHover && hoverOpen() && !state.menu}
openDelay={0}
closeDelay={0}
placement="right-start"
@@ -371,7 +359,7 @@ export const SortableProject = (props: {
onOpenChange={(value) => {
if (state.menu) return
if (value && state.suppressHover) return
- setState("open", value)
+ props.ctx.onHoverOpenChanged(props.project.worktree, value)
if (value) props.ctx.setHoverSession(undefined)
}}
>
@@ -386,7 +374,6 @@ export const SortableProject = (props: {
projectChildren={projectChildren}
workspaceSessions={workspaceSessions}
workspaceChildren={workspaceChildren}
- setOpen={(value) => setState("open", value)}
ctx={props.ctx}
language={language}
/>