fix: separate sidebar drag ids by type

This commit is contained in:
LukeParkerDev
2026-04-16 17:53:01 +10:00
parent 0dae445f4f
commit fcc9cab760
4 changed files with 49 additions and 13 deletions

View File

@@ -83,9 +83,16 @@ import {
LocalWorkspace,
SortableWorkspace,
WorkspaceDragOverlay,
workspaceSortableDirectory,
workspaceSortableId,
type WorkspaceSidebarContext,
} from "./layout/sidebar-workspace"
import { ProjectDragOverlay, SortableProject, type ProjectSidebarContext } from "./layout/sidebar-project"
import {
ProjectDragOverlay,
SortableProject,
projectSortableWorktree,
type ProjectSidebarContext,
} from "./layout/sidebar-project"
import { SidebarContent } from "./layout/sidebar-shell"
export default function Layout(props: ParentProps) {
@@ -1841,7 +1848,7 @@ export default function Layout(props: ParentProps) {
)
function handleDragStart(event: unknown) {
const id = getDraggableId(event)
const id = projectSortableWorktree(getDraggableId(event))
if (!id) return
setHoverProject(undefined)
setStore("activeProject", id)
@@ -1850,11 +1857,14 @@ export default function Layout(props: ParentProps) {
function handleDragOver(event: DragEvent) {
const { draggable, droppable } = event
if (draggable && droppable) {
const from = projectSortableWorktree(draggable.id?.toString())
const to = projectSortableWorktree(droppable.id?.toString())
if (!from || !to) return
const projects = layout.projects.list()
const fromIndex = projects.findIndex((p) => p.worktree === draggable.id.toString())
const toIndex = projects.findIndex((p) => p.worktree === droppable.id.toString())
const fromIndex = projects.findIndex((p) => p.worktree === from)
const toIndex = projects.findIndex((p) => p.worktree === to)
if (fromIndex !== toIndex && toIndex !== -1) {
layout.projects.move(draggable.id.toString(), toIndex)
layout.projects.move(from, toIndex)
}
}
}
@@ -1892,7 +1902,7 @@ export default function Layout(props: ParentProps) {
})
function handleWorkspaceDragStart(event: unknown) {
const id = getDraggableId(event)
const id = workspaceSortableDirectory(getDraggableId(event))
if (!id) return
setStore("activeWorkspace", id)
}
@@ -1900,13 +1910,16 @@ export default function Layout(props: ParentProps) {
function handleWorkspaceDragOver(event: DragEvent) {
const { draggable, droppable } = event
if (!draggable || !droppable) return
const from = workspaceSortableDirectory(draggable.id?.toString())
const to = workspaceSortableDirectory(droppable.id?.toString())
if (!from || !to) return
const project = sidebarProject()
if (!project) return
const ids = workspaceIds(project)
const fromIndex = ids.findIndex((dir) => dir === draggable.id.toString())
const toIndex = ids.findIndex((dir) => dir === droppable.id.toString())
const fromIndex = ids.findIndex((dir) => dir === from)
const toIndex = ids.findIndex((dir) => dir === to)
if (fromIndex === -1 || toIndex === -1) return
if (fromIndex === toIndex) return
@@ -2267,13 +2280,13 @@ export default function Layout(props: ParentProps) {
}}
class="size-full flex flex-col py-2 gap-4 overflow-y-auto no-scrollbar [overflow-anchor:none]"
>
<SortableProvider ids={workspaces()}>
<SortableProvider ids={workspaces().map(workspaceSortableId)}>
<For each={workspaces()}>
{(directory) => (
<SortableWorkspace
ctx={workspaceSidebarCtx}
directory={directory}
project={project()}
project={project()!}
sortNow={sortNow}
mobile={panelProps.mobile}
/>

View File

@@ -34,6 +34,17 @@ export type ProjectSidebarContext = {
sessionProps: Omit<SessionItemProps, "session" | "list" | "slug" | "mobile" | "dense">
}
const PROJECT_SORTABLE_PREFIX = "project:"
export function projectSortableId(worktree: string) {
return `${PROJECT_SORTABLE_PREFIX}${worktree}`
}
export function projectSortableWorktree(id: string | undefined) {
if (!id?.startsWith(PROJECT_SORTABLE_PREFIX)) return
return id.slice(PROJECT_SORTABLE_PREFIX.length)
}
export const ProjectDragOverlay = (props: {
projects: Accessor<LocalProject[]>
activeProject: Accessor<string | undefined>
@@ -275,7 +286,7 @@ export const SortableProject = (props: {
}): JSX.Element => {
const globalSync = useGlobalSync()
const language = useLanguage()
const sortable = createSortable(props.project.worktree)
const sortable = createSortable(projectSortableId(props.project.worktree))
const selected = createMemo(() => props.ctx.currentProject()?.worktree === props.project.worktree)
const workspaces = createMemo(() => props.ctx.workspaceIds(props.project).slice(0, 2))
const workspaceEnabled = createMemo(() => props.ctx.workspacesEnabled(props.project))

View File

@@ -11,6 +11,7 @@ import { ConstrainDragXAxis } from "@/utils/solid-dnd"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { type LocalProject } from "@/context/layout"
import { projectSortableId } from "./sidebar-project"
export const SidebarContent = (props: {
mobile?: boolean
@@ -63,7 +64,7 @@ export const SidebarContent = (props: {
<DragDropSensors />
<ConstrainDragXAxis />
<div class="h-full w-full flex flex-col items-center gap-3 px-3 py-3 overflow-y-auto no-scrollbar">
<SortableProvider ids={props.projects().map((p) => p.worktree)}>
<SortableProvider ids={props.projects().map((p) => projectSortableId(p.worktree))}>
<For each={props.projects()}>{(project) => props.renderProject(project)}</For>
</SortableProvider>
<Tooltip

View File

@@ -54,6 +54,17 @@ export type WorkspaceSidebarContext = {
setScrollContainerRef: (el: HTMLDivElement | undefined, mobile?: boolean) => void
}
const WORKSPACE_SORTABLE_PREFIX = "workspace:"
export function workspaceSortableId(directory: string) {
return `${WORKSPACE_SORTABLE_PREFIX}${directory}`
}
export function workspaceSortableDirectory(id: string | undefined) {
if (!id?.startsWith(WORKSPACE_SORTABLE_PREFIX)) return
return id.slice(WORKSPACE_SORTABLE_PREFIX.length)
}
export const WorkspaceDragOverlay = (props: {
sidebarProject: Accessor<LocalProject | undefined>
activeWorkspace: Accessor<string | undefined>
@@ -300,7 +311,7 @@ export const SortableWorkspace = (props: {
const params = useParams()
const globalSync = useGlobalSync()
const language = useLanguage()
const sortable = createSortable(props.directory)
const sortable = createSortable(workspaceSortableId(props.directory))
const [workspaceStore, setWorkspaceStore] = globalSync.child(props.directory, { bootstrap: false })
const [menu, setMenu] = createStore({
open: false,