From af3d8c383e5fe2feba79ace5bb2e1082195a459d Mon Sep 17 00:00:00 2001 From: adamelmore <2363879+adamdottv@users.noreply.github.com> Date: Mon, 26 Jan 2026 06:28:25 -0600 Subject: [PATCH] wip(app): sidebar hover full --- packages/app/src/pages/layout.tsx | 776 ++++++++++++++++-------------- 1 file changed, 418 insertions(+), 358 deletions(-) diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index 2702f119b..a14b4a8e5 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -135,6 +135,21 @@ export default function Layout(props: ParentProps) { const editorRef = { current: undefined as HTMLInputElement | undefined } const [hoverSession, setHoverSession] = createSignal() + const [hoverProject, setHoverProject] = createSignal() + + const sidebarHovering = createMemo(() => !layout.sidebar.opened() && hoverProject() !== undefined) + const sidebarExpanded = createMemo(() => layout.sidebar.opened() || sidebarHovering()) + + const hoverProjectData = createMemo(() => { + const id = hoverProject() + if (!id) return + return layout.projects.list().find((project) => project.worktree === id) + }) + + createEffect(() => { + if (!layout.sidebar.opened()) return + setHoverProject(undefined) + }) const autoselecting = createMemo(() => { if (params.dir) return false @@ -1119,15 +1134,13 @@ export default function Layout(props: ParentProps) { return language.t("common.requestFailed") } - const deleteWorkspace = async (directory: string) => { - const current = currentProject() - if (!current) return - if (directory === current.worktree) return + const deleteWorkspace = async (root: string, directory: string) => { + if (directory === root) return setBusy(directory, true) const result = await globalSDK.client.worktree - .remove({ directory: current.worktree, worktreeRemoveInput: { directory } }) + .remove({ directory: root, worktreeRemoveInput: { directory } }) .then((x) => x.data) .catch((err) => { showToast({ @@ -1142,17 +1155,15 @@ export default function Layout(props: ParentProps) { if (!result) return layout.projects.close(directory) - layout.projects.open(current.worktree) + layout.projects.open(root) if (params.dir && base64Decode(params.dir) === directory) { - navigateToProject(current.worktree) + navigateToProject(root) } } - const resetWorkspace = async (directory: string) => { - const current = currentProject() - if (!current) return - if (directory === current.worktree) return + const resetWorkspace = async (root: string, directory: string) => { + if (directory === root) return setBusy(directory, true) const progress = showToast({ @@ -1168,7 +1179,7 @@ export default function Layout(props: ParentProps) { .catch(() => []) const result = await globalSDK.client.worktree - .reset({ directory: current.worktree, worktreeResetInput: { directory } }) + .reset({ directory: root, worktreeResetInput: { directory } }) .then((x) => x.data) .catch((err) => { showToast({ @@ -1251,7 +1262,7 @@ export default function Layout(props: ParentProps) { ) } - function DialogDeleteWorkspace(props: { directory: string }) { + function DialogDeleteWorkspace(props: { root: string; directory: string }) { const name = createMemo(() => getFilename(props.directory)) const [data, setData] = createStore({ status: "loading" as "loading" | "ready" | "error", @@ -1259,12 +1270,6 @@ export default function Layout(props: ParentProps) { }) onMount(() => { - const current = currentProject() - if (!current) { - setData({ status: "error", dirty: false }) - return - } - globalSDK.client.file .status({ directory: props.directory }) .then((x) => { @@ -1279,7 +1284,7 @@ export default function Layout(props: ParentProps) { const handleDelete = () => { dialog.close() - void deleteWorkspace(props.directory) + void deleteWorkspace(props.root, props.directory) } const description = () => { @@ -1311,7 +1316,7 @@ export default function Layout(props: ParentProps) { ) } - function DialogResetWorkspace(props: { directory: string }) { + function DialogResetWorkspace(props: { root: string; directory: string }) { const name = createMemo(() => getFilename(props.directory)) const [state, setState] = createStore({ status: "loading" as "loading" | "ready" | "error", @@ -1329,12 +1334,6 @@ export default function Layout(props: ParentProps) { } onMount(() => { - const current = currentProject() - if (!current) { - setState({ status: "error", dirty: false }) - return - } - globalSDK.client.file .status({ directory: props.directory }) .then((x) => { @@ -1350,7 +1349,7 @@ export default function Layout(props: ParentProps) { const handleReset = () => { dialog.close() - void resetWorkspace(props.directory) + void resetWorkspace(props.root, props.directory) } const archivedCount = () => state.sessions.length @@ -1444,6 +1443,7 @@ export default function Layout(props: ParentProps) { function handleDragStart(event: unknown) { const id = getDraggableId(event) if (!id) return + setHoverProject(undefined) setStore("activeProject", id) } @@ -1483,6 +1483,13 @@ export default function Layout(props: ParentProps) { return [...merged, extra] } + const sidebarProject = createMemo(() => { + if (layout.sidebar.opened()) return currentProject() + const hovered = hoverProjectData() + if (hovered) return hovered + return currentProject() + }) + function handleWorkspaceDragStart(event: unknown) { const id = getDraggableId(event) if (!id) return @@ -1493,7 +1500,7 @@ export default function Layout(props: ParentProps) { const { draggable, droppable } = event if (!draggable || !droppable) return - const project = currentProject() + const project = sidebarProject() if (!project) return const ids = workspaceIds(project) @@ -1593,7 +1600,7 @@ export default function Layout(props: ParentProps) { sessionStore.message[props.session.id]?.filter((message) => message.role === "user"), ) const hoverReady = createMemo(() => sessionStore.message[props.session.id] !== undefined) - const hoverAllowed = createMemo(() => !props.mobile && layout.sidebar.opened()) + const hoverAllowed = createMemo(() => !props.mobile && sidebarExpanded()) const hoverEnabled = createMemo(() => (props.popover ?? true) && hoverAllowed()) const isActive = createMemo(() => props.session.id === params.id) const [menuOpen, setMenuOpen] = createSignal(false) @@ -1611,7 +1618,11 @@ export default function Layout(props: ParentProps) { class={`flex items-center justify-between gap-3 min-w-0 text-left w-full focus:outline-none transition-[padding] ${menuOpen() ? "pr-7" : ""} group-hover/session:pr-7 group-focus-within/session:pr-7 group-active/session:pr-7 ${props.dense ? "py-0.5" : "py-1"}`} onMouseEnter={() => prefetchSession(props.session, "high")} onFocus={() => prefetchSession(props.session, "high")} - onClick={() => setHoverSession(undefined)} + onClick={() => { + setHoverSession(undefined) + if (layout.sidebar.opened()) return + queueMicrotask(() => setHoverProject(undefined)) + }} >
{ const label = language.t("command.session.new") - const tooltip = () => props.mobile || !layout.sidebar.opened() + const tooltip = () => props.mobile || !sidebarExpanded() const item = ( setHoverSession(undefined)} + onClick={() => { + setHoverSession(undefined) + if (layout.sidebar.opened()) return + queueMicrotask(() => setHoverProject(undefined)) + }} >
@@ -1814,7 +1829,7 @@ export default function Layout(props: ParentProps) { const WorkspaceDragOverlay = (): JSX.Element => { const label = createMemo(() => { - const project = currentProject() + const project = sidebarProject() if (!project) return const directory = store.activeWorkspace if (!directory) return @@ -1985,13 +2000,21 @@ export default function Layout(props: ParentProps) { dialog.show(() => )} + onSelect={() => + dialog.show(() => ( + + )) + } > {language.t("common.reset")} dialog.show(() => )} + onSelect={() => + dialog.show(() => ( + + )) + } > {language.t("common.delete")} @@ -2005,9 +2028,7 @@ export default function Layout(props: ParentProps) {