summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/pages/layout.tsx
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-04 13:25:55 -0600
committerGitHub <[email protected]>2026-02-04 19:25:55 +0000
commit9436cb575bf64ba5867511c998d1e1c51782173c (patch)
tree4856b18f71241f0f559470e5be0a6566485a0da1 /packages/app/src/pages/layout.tsx
parentd1686661c0d42edb8e9e6576fb12cfdfc4ee2142 (diff)
downloadopencode-9436cb575bf64ba5867511c998d1e1c51782173c.tar.gz
opencode-9436cb575bf64ba5867511c998d1e1c51782173c.zip
fix(app): safety triangle for sidebar hover (#12179)
Diffstat (limited to 'packages/app/src/pages/layout.tsx')
-rw-r--r--packages/app/src/pages/layout.tsx42
1 files changed, 31 insertions, 11 deletions
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index 1c5edbf2b..4ee54d306 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -58,6 +58,7 @@ import { usePermission } from "@/context/permission"
import { Binary } from "@opencode-ai/util/binary"
import { retry } from "@opencode-ai/util/retry"
import { playSound, soundSrc } from "@/utils/sound"
+import { createAim } from "@/utils/aim"
import { Worktree as WorktreeState } from "@/utils/worktree"
import { agentColor } from "@/utils/agent"
@@ -146,9 +147,20 @@ export default function Layout(props: ParentProps) {
const navLeave = { current: undefined as number | undefined }
+ const aim = createAim({
+ enabled: () => !layout.sidebar.opened(),
+ active: () => state.hoverProject,
+ el: () => state.nav,
+ onActivate: (directory) => {
+ globalSync.child(directory)
+ setState("hoverProject", directory)
+ setState("hoverSession", undefined)
+ },
+ })
+
onCleanup(() => {
- if (navLeave.current === undefined) return
- clearTimeout(navLeave.current)
+ if (navLeave.current !== undefined) clearTimeout(navLeave.current)
+ aim.reset()
})
const sidebarHovering = createMemo(() => !layout.sidebar.opened() && state.hoverProject !== undefined)
@@ -162,15 +174,22 @@ export default function Layout(props: ParentProps) {
createEffect(() => {
if (!layout.sidebar.opened()) return
+ aim.reset()
setState("hoverProject", undefined)
})
+ createEffect(() => {
+ if (state.hoverProject !== undefined) return
+ aim.reset()
+ })
+
createEffect(
on(
() => ({ dir: params.dir, id: params.id }),
() => {
if (layout.sidebar.opened()) return
if (!state.hoverProject) return
+ aim.reset()
setState("hoverSession", undefined)
setState("hoverProject", undefined)
},
@@ -2311,17 +2330,17 @@ export default function Layout(props: ParentProps) {
!selected() && !active(),
"bg-surface-base-hover border border-border-weak-base": !selected() && active(),
}}
- onMouseEnter={() => {
+ onMouseEnter={(event: MouseEvent) => {
+ if (!overlay()) return
+ aim.enter(props.project.worktree, event)
+ }}
+ onMouseLeave={() => {
if (!overlay()) return
- globalSync.child(props.project.worktree)
- setState("hoverProject", props.project.worktree)
- setState("hoverSession", undefined)
+ aim.leave(props.project.worktree)
}}
onFocus={() => {
if (!overlay()) return
- globalSync.child(props.project.worktree)
- setState("hoverProject", props.project.worktree)
- setState("hoverSession", undefined)
+ aim.activate(props.project.worktree)
}}
onClick={() => navigateToProject(props.project.worktree)}
onBlur={() => setOpen(false)}
@@ -2806,7 +2825,7 @@ export default function Layout(props: ParentProps) {
return (
<div class="flex h-full w-full overflow-hidden">
- <div class="w-16 shrink-0 bg-background-base flex flex-col items-center overflow-hidden">
+ <div class="w-16 shrink-0 bg-background-base flex flex-col items-center overflow-hidden" onMouseMove={aim.move}>
<div class="flex-1 min-h-0 w-full">
<DragDropProvider
onDragStart={handleDragStart}
@@ -2901,6 +2920,7 @@ export default function Layout(props: ParentProps) {
navLeave.current = undefined
}}
onMouseLeave={() => {
+ aim.reset()
if (!sidebarHovering()) return
if (navLeave.current !== undefined) clearTimeout(navLeave.current)
@@ -2916,7 +2936,7 @@ export default function Layout(props: ParentProps) {
</div>
<Show when={!layout.sidebar.opened() ? hoverProjectData() : undefined} keyed>
{(project) => (
- <div class="absolute inset-y-0 left-16 z-50 flex">
+ <div class="absolute inset-y-0 left-16 z-50 flex" onMouseEnter={aim.reset}>
<SidebarPanel project={project} />
</div>
)}