Skip to content

ctx.dnd stable

First-class drag-and-drop: be a drag source and a drop target, with typed payloads (DND_MIME) that interoperate across extensions — drag a file out of a tree and drop it into a terminal, an editor, or another extension's panel. The host owns the drag affordance (the floating chip + paste-mode overlay) and resolves the modifier mode, so extensions don't reimplement the platform-specific drag plumbing.

ts
ctx.dnd: DndService

Example

tsx
// --- drag SOURCE: start a drag from a dragstart handler ---
<div
  draggable
  onDragStart={(e) =>
    ctx.dnd.beginDrag(e, {
      items: [{ mime: DND_MIME.filePath, value: path }],
      label: name,
      effect: "move",
    })
  }
/>;

// --- drop TARGET: register a DOM element (e.g. via a ref) ---
useEffect(() => {
  const el = ref.current;
  if (!el) return;
  const reg = ctx.dnd.registerDropTarget(el, {
    accepts: [DND_MIME.filePath],
    onDragOver: () => "move", // sets the cursor; drives your hover styling
    onDrop: ({ items, mode }) => {
      const path = items.find((i) => i.mime === DND_MIME.filePath)?.value;
      if (!path) return false;
      mode === "paste" ? pasteInto(path) : open(path);
      return true; // handled — host stops it falling through to other targets
    },
  });
  return () => reg.dispose();
}, [ctx.dnd]);

Members

DndService (ctx.dnd):

MemberWhat it does
beginDrag(event, init)From a dragstart handler: write typed DragInit.items onto the drag + start the chip / paste affordance.
registerDropTarget(el, h)Make a DOM element a drop target; the host resolves the mode and delivers a DropContext. Returns a Disposable.

Types

DndService · DND_MIME · DndItem · DragInit · DropContext · DropTargetHandlers · DndMode.

Notes

Use the DND_MIME constants for payload keys rather than raw strings — that's what lets drags interoperate across extensions and built-ins (e.g. the file explorer's DND_MIME.filePath drag is understood by the editor and terminal).

onDrop returning true tells the host to preventDefault() + stopPropagation(), so a handled drop does not fall through to another target (such as the center dock opening the file in a new pane). Return false/nothing to let it pass.

mode is "paste" while Shift is held and "copy" otherwise; it's resolved robustly even mid-drag (some platforms suppress key events during a drag), so prefer it over reading nativeEvent.shiftKey. The items array is populated on drop; during onDragOver only MIME types are exposed (not values), so branch on accepts / mode there.