From ca73e44f68220e744d907c80822febee21875ad9 Mon Sep 17 00:00:00 2001 From: Benjamin Shafii Date: Tue, 3 Feb 2026 12:15:37 -0800 Subject: [PATCH] feat: add sidebar session expanders and menu sizing --- .../src/app/components/session/sidebar.tsx | 59 ++++++++++++++++++- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/packages/app/src/app/components/session/sidebar.tsx b/packages/app/src/app/components/session/sidebar.tsx index f9766d85..91af2b6d 100644 --- a/packages/app/src/app/components/session/sidebar.tsx +++ b/packages/app/src/app/components/session/sidebar.tsx @@ -44,6 +44,7 @@ export type SidebarProps = { }; export default function SessionSidebar(props: SidebarProps) { + const MAX_SESSIONS_PREVIEW = 8; const realTodos = createMemo(() => props.todos.filter((todo) => todo.content.trim())); const WORKSPACE_COLLAPSE_KEY = "openwork.workspace-collapse.v1"; const readWorkspaceCollapse = () => { @@ -69,6 +70,9 @@ export default function SessionSidebar(props: SidebarProps) { const [collapsedById, setCollapsedById] = createSignal>(readWorkspaceCollapse()); const [draggingWorkspaceId, setDraggingWorkspaceId] = createSignal(null); const [dragOverWorkspaceId, setDragOverWorkspaceId] = createSignal(null); + const [showAllSessionsByWorkspaceId, setShowAllSessionsByWorkspaceId] = createSignal< + Record + >({}); const workspaceLabel = (workspace: WorkspaceInfo) => workspace.displayName?.trim() || @@ -106,6 +110,14 @@ export default function SessionSidebar(props: SidebarProps) { }; const isWorkspaceCollapsed = (workspaceId: string) => Boolean(collapsedById()[workspaceId]); + const isShowingAllSessions = (workspaceId: string) => + Boolean(showAllSessionsByWorkspaceId()[workspaceId]); + const toggleShowAllSessions = (workspaceId: string) => { + setShowAllSessionsByWorkspaceId((prev) => ({ + ...prev, + [workspaceId]: !prev[workspaceId], + })); + }; const handleDragStart = (event: DragEvent, workspaceId: string) => { event.dataTransfer?.setData("text/plain", workspaceId); @@ -162,6 +174,8 @@ export default function SessionSidebar(props: SidebarProps) { x: number; y: number; }>(null); + let contextMenuRef: HTMLDivElement | undefined; + const [contextMenuSize, setContextMenuSize] = createSignal({ width: 188, height: 96 }); const closeContextMenu = () => setContextMenu(null); @@ -174,8 +188,9 @@ export default function SessionSidebar(props: SidebarProps) { const contextMenuStyle = createMemo(() => { const menu = contextMenu(); if (!menu) return undefined; - const width = 188; - const height = 96; + const size = contextMenuSize(); + const width = size.width; + const height = size.height; if (typeof window === "undefined") { return { left: `${menu.x}px`, top: `${menu.y}px` }; } @@ -214,6 +229,28 @@ export default function SessionSidebar(props: SidebarProps) { writeWorkspaceCollapse(next); return next; }); + setShowAllSessionsByWorkspaceId((prev) => { + const next: Record = {}; + let changed = false; + for (const [id, value] of Object.entries(prev)) { + if (ids.has(id)) { + next[id] = value; + } else { + changed = true; + } + } + return changed ? next : prev; + }); + }); + + createEffect(() => { + if (!contextMenu()) return; + queueMicrotask(() => { + if (!contextMenuRef || typeof window === "undefined") return; + const rect = contextMenuRef.getBoundingClientRect(); + if (!rect.width || !rect.height) return; + setContextMenuSize({ width: rect.width, height: rect.height }); + }); }); return ( @@ -253,6 +290,10 @@ export default function SessionSidebar(props: SidebarProps) { const allowActions = () => !props.connectingWorkspaceId || isConnecting(); const collapsed = () => isWorkspaceCollapsed(group.workspace.id); const dragOver = () => dragOverWorkspaceId() === group.workspace.id; + const showingAll = () => isShowingAllSessions(group.workspace.id); + const visibleSessions = () => + showingAll() ? sessions() : sessions().slice(0, MAX_SESSIONS_PREVIEW); + const hasMoreSessions = () => sessions().length > MAX_SESSIONS_PREVIEW; return (
} > - + {(session) => ( +
@@ -469,6 +521,7 @@ export default function SessionSidebar(props: SidebarProps) { style={contextMenuStyle()} role="menu" onClick={(event) => event.stopPropagation()} + ref={(el) => (contextMenuRef = el)} >