diff --git a/apps/desktop/src/components/dnd/dnd-provider.tsx b/apps/desktop/src/components/dnd/dnd-provider.tsx
index 86d02c1e..9b4ec9c7 100644
--- a/apps/desktop/src/components/dnd/dnd-provider.tsx
+++ b/apps/desktop/src/components/dnd/dnd-provider.tsx
@@ -13,7 +13,10 @@ import { handleEditorDrop } from "./editor-drop-handler"
import { EditorDropLine } from "./editor-drop-indicator"
import { isPoint } from "./editor-drop-indicator.helpers"
import { EditorDropOwnershipProvider } from "./editor-drop-ownership"
-import { ExplorerDragOverlay } from "./explorer-drag-overlay"
+import {
+ ExplorerDragOverlay,
+ getExplorerDragOverlayName,
+} from "./explorer-drag-overlay"
import {
EMPTY_DRAGGED_EXPLORER_PATHS,
ExplorerDragPathsProvider,
@@ -61,7 +64,7 @@ function renderDragOverlay(source: DragOverlaySource) {
if (isFileEntryDragData(source.data) && source.data.name) {
return (
)
diff --git a/apps/desktop/src/components/dnd/dnd-types.ts b/apps/desktop/src/components/dnd/dnd-types.ts
index 7b756cc9..58093a63 100644
--- a/apps/desktop/src/components/dnd/dnd-types.ts
+++ b/apps/desktop/src/components/dnd/dnd-types.ts
@@ -12,6 +12,7 @@ export type FileEntryDragData = {
path?: string
isDirectory?: boolean
name?: string
+ displayName?: string
}
export type EditorDragData = {
@@ -80,15 +81,15 @@ export function isFileEntryDragData(data: unknown): data is FileEntryDragData {
}
const hasKnownKey = "path" in data || "isDirectory" in data || "name" in data
- if (!hasKnownKey) {
+ if (!hasKnownKey && !("displayName" in data)) {
return false
}
-
- const { path, isDirectory, name } = data
+ const { path, isDirectory, name, displayName } = data
return (
(path === undefined || typeof path === "string") &&
(isDirectory === undefined || typeof isDirectory === "boolean") &&
- (name === undefined || typeof name === "string")
+ (name === undefined || typeof name === "string") &&
+ (displayName === undefined || typeof displayName === "string")
)
}
diff --git a/apps/desktop/src/components/dnd/explorer-drag-overlay.test.tsx b/apps/desktop/src/components/dnd/explorer-drag-overlay.test.tsx
new file mode 100644
index 00000000..6cdf6700
--- /dev/null
+++ b/apps/desktop/src/components/dnd/explorer-drag-overlay.test.tsx
@@ -0,0 +1,23 @@
+import { describe, expect, it } from "vitest"
+import { getExplorerDragOverlayName } from "./explorer-drag-overlay"
+
+describe("getExplorerDragOverlayName", () => {
+ it("prefers the display name when present", () => {
+ expect(
+ getExplorerDragOverlayName({
+ name: "note.md",
+ displayName: "note",
+ isDirectory: false,
+ }),
+ ).toBe("note")
+ })
+
+ it("falls back to the source name when no display name is provided", () => {
+ expect(
+ getExplorerDragOverlayName({
+ name: "folder",
+ isDirectory: true,
+ }),
+ ).toBe("folder")
+ })
+})
diff --git a/apps/desktop/src/components/dnd/explorer-drag-overlay.tsx b/apps/desktop/src/components/dnd/explorer-drag-overlay.tsx
index d4f5665d..04b52ec9 100644
--- a/apps/desktop/src/components/dnd/explorer-drag-overlay.tsx
+++ b/apps/desktop/src/components/dnd/explorer-drag-overlay.tsx
@@ -1,11 +1,16 @@
import { cn } from "@mdit/ui/lib/utils"
import { ChevronRight } from "lucide-react"
+import type { FileEntryDragData } from "./dnd-types"
type ExplorerDragOverlayProps = {
name: string
isDirectory: boolean
}
+export function getExplorerDragOverlayName(data: FileEntryDragData): string {
+ return data.displayName ?? data.name ?? ""
+}
+
function getExplorerDragOverlayClassName(isDirectory: boolean) {
return cn(
"pointer-events-none flex min-w-0 max-w-80 items-center gap-1 rounded-sm bg-transparent px-0 py-0 text-sm text-accent-foreground/95 shadow-none ring-0 outline-none border-0",
diff --git a/apps/desktop/src/components/file-explorer/tree/file-tree-node.tsx b/apps/desktop/src/components/file-explorer/tree/file-tree-node.tsx
index 83f70a49..a32969ee 100644
--- a/apps/desktop/src/components/file-explorer/tree/file-tree-node.tsx
+++ b/apps/desktop/src/components/file-explorer/tree/file-tree-node.tsx
@@ -45,12 +45,12 @@ export function FileTreeNode({
}, [entry.isDirectory, entry.name])
const baseName = useMemo(() => {
- if (entry.isDirectory || !extension) {
+ if (!extension) {
return entry.name
}
- return entry.name.slice(0, entry.name.length - extension.length)
- }, [entry.isDirectory, entry.name, extension])
+ return entry.name.slice(0, -extension.length)
+ }, [entry.name, extension])
const isMarkdown = useMemo(
() => !entry.isDirectory && extension.toLowerCase() === ".md",
diff --git a/apps/desktop/src/components/file-explorer/tree/use-tree-node-interactions.test.ts b/apps/desktop/src/components/file-explorer/tree/use-tree-node-interactions.test.ts
new file mode 100644
index 00000000..ab4e64a8
--- /dev/null
+++ b/apps/desktop/src/components/file-explorer/tree/use-tree-node-interactions.test.ts
@@ -0,0 +1,19 @@
+import { describe, expect, it } from "vitest"
+import { getExplorerDragData } from "./use-tree-node-interactions"
+
+describe("getExplorerDragData", () => {
+ it("includes an overlay display name without the file extension", () => {
+ expect(
+ getExplorerDragData({
+ path: "/notes/archive.tar.gz",
+ name: "archive.tar.gz",
+ isDirectory: false,
+ }),
+ ).toEqual({
+ path: "/notes/archive.tar.gz",
+ name: "archive.tar.gz",
+ isDirectory: false,
+ displayName: "archive.tar",
+ })
+ })
+})
diff --git a/apps/desktop/src/components/file-explorer/tree/use-tree-node-interactions.ts b/apps/desktop/src/components/file-explorer/tree/use-tree-node-interactions.ts
index 61183455..c339fa58 100644
--- a/apps/desktop/src/components/file-explorer/tree/use-tree-node-interactions.ts
+++ b/apps/desktop/src/components/file-explorer/tree/use-tree-node-interactions.ts
@@ -1,8 +1,10 @@
import { useDraggable } from "@dnd-kit/react"
import type { FileTreeRenderNode } from "@mdit/file-tree"
import { useCallback } from "react"
+import type { FileEntryDragData } from "@/components/dnd/dnd-types"
import { useDraggedExplorerPaths } from "@/components/dnd/explorer-drag-state"
import type { WorkspaceEntry } from "@/store"
+import { getExplorerEntryDisplayName } from "../utils/display-name"
type UseTreeNodeInteractionsParams = {
node: FileTreeRenderNode
@@ -13,6 +15,15 @@ type UseTreeNodeInteractionsParams = {
onEntryContextMenu: (entry: WorkspaceEntry) => void | Promise
}
+export function getExplorerDragData(entry: WorkspaceEntry): FileEntryDragData {
+ return {
+ path: entry.path,
+ isDirectory: entry.isDirectory,
+ name: entry.name,
+ displayName: getExplorerEntryDisplayName(entry.name, entry.isDirectory),
+ }
+}
+
export function useTreeNodeInteractions({
node,
onEntryPrimaryAction,
@@ -27,11 +38,7 @@ export function useTreeNodeInteractions({
const { ref: draggableRef, isDragging: isSourceDragging } = useDraggable({
id: entry.path,
- data: {
- path: entry.path,
- isDirectory: entry.isDirectory,
- name: entry.name,
- },
+ data: getExplorerDragData(entry),
disabled: isBusy,
})
const isDragging = isSourceDragging || draggedExplorerPaths.has(entry.path)
diff --git a/apps/desktop/src/components/file-explorer/utils/display-name.test.ts b/apps/desktop/src/components/file-explorer/utils/display-name.test.ts
new file mode 100644
index 00000000..1981c104
--- /dev/null
+++ b/apps/desktop/src/components/file-explorer/utils/display-name.test.ts
@@ -0,0 +1,21 @@
+import { describe, expect, it } from "vitest"
+import { getExplorerEntryDisplayName } from "./display-name"
+
+describe("getExplorerEntryDisplayName", () => {
+ it("strips the last file extension for files", () => {
+ expect(getExplorerEntryDisplayName("note.md", false)).toBe("note")
+ expect(getExplorerEntryDisplayName("archive.tar.gz", false)).toBe(
+ "archive.tar",
+ )
+ })
+
+ it("preserves dotfiles", () => {
+ expect(getExplorerEntryDisplayName(".env", false)).toBe(".env")
+ })
+
+ it("preserves directory names", () => {
+ expect(getExplorerEntryDisplayName("folder.with.dot", true)).toBe(
+ "folder.with.dot",
+ )
+ })
+})
diff --git a/apps/desktop/src/components/file-explorer/utils/display-name.ts b/apps/desktop/src/components/file-explorer/utils/display-name.ts
new file mode 100644
index 00000000..0c0fd1af
--- /dev/null
+++ b/apps/desktop/src/components/file-explorer/utils/display-name.ts
@@ -0,0 +1,12 @@
+import { getFileNameWithoutExtension } from "@mdit/utils/path-utils"
+
+export function getExplorerEntryDisplayName(
+ name: string,
+ isDirectory: boolean,
+): string {
+ if (isDirectory) {
+ return name
+ }
+
+ return getFileNameWithoutExtension(name)
+}