Drop Gate — every image asks where it should go
Image Gin now intercepts every image you drag or paste into a note and pops a single confirmation modal: vault, ImageKit, or Imgur? Built for writers who handle private client imagery and don't want a default-on third-party uploader silently shipping screenshots to a public CDN. Default is the vault. ImageKit is for less-public material that still wants a CDN. Imgur is there when something is genuinely shareable.
Why Care?
We use Obsidian to write about companies. The artifacts those companies share with us — growth charts, dashboard screenshots, photos of a whiteboard — are not always meant to leave a private context. The default failure mode used to be silent: drag a file in, and the bytes go where Obsidian (or some default-on third-party uploader plugin) decides without asking. That's fine for a recipe. It's a phone call we don't want to make for anything client-sensitive.
The Drop Gate puts a deliberate decision between the drop and the destination. One modal, three destinations:
Vault attachments — Obsidian's standard private behavior. Bytes stay in the vault.
ImageKit — your own private CDN. We use it when material isn't strictly secret but we don't want to host it forever ourselves. This is our default for less-public information.
Imgur — anonymous public upload. Free, fast, durable. Use when something is genuinely shareable.
Off by default. Toggle it on in Settings → Image Gin → Drag-Drop / Paste Confirmation Gate.
What's New?
A new feature inside Image Gin, layered alongside the existing Recraft / Magnific / Ideogram / ImageKit pipelines. No new plugin to install — same plugin, a new section in settings.
Intercepts every image drop and paste in a markdown view via Obsidian's
editor-dropandeditor-pasteworkspace events.The modal shows the dropped file(s) (name, size, type) and a radio group of all three destinations. Configured destinations are selectable; un-configured ones are visible-but-disabled with a hint pointing at the right setting. You can see what's possible even before you've turned anything on.
Three destinations behind a small
DropGateDestinationinterface so a fourth (S3, Bunny, Cloudinary, an internal SharePoint of doom) is a class plus a settings panel:VaultDestination — re-dispatches a synthetic event copy into Obsidian's internal
clipboardManagerso the default attachment-handling code runs cleanly. Falls back toVault.createBinary+FileManager.generateMarkdownLinkifclipboardManageris unavailable.ImageKitDestination — wraps Image Gin's existing
ImageKitService. Same multipart-upload pipeline that already powers "Convert Local Images to Remote." A drop-gate-specific folder setting (dropGate.imageKitFolder) overrides the main upload folder when set, so ad-hoc dropped images can land somewhere different from generated images.ImgurDestination — anonymous client-ID upload to
api.imgur.com/3/image. Free, no account.
Two policy modes:
always-confirm(modal on every image, the default) andexternal-only(vault drops fall through silently; modal only appears if at least one external destination is enabled).Per-session "remember choice" checkbox in the modal — skips the modal for subsequent drops in the current note. Resets on
active-leaf-change. Never persists across Obsidian restarts.Conflict detection. If the third-party
obsidian-imgur-pluginis also enabled, both plugins handle every drop and the user gets two modals back-to-back —preventDefault()blocks the browser default, not other plugins. We surface a persistent Notice on plugin load telling the user to disable the other one and configure Imgur from inside Image Gin.Reset command in the palette: "Drop Gate: Reset session-remembered destination."
How it feels
Drop a screenshot into a note. Modal opens, wide enough to read (~720px). Three destinations visible — the ones you've configured are selectable; the ones you haven't carry a one-line nudge to settings. Pick ImageKit. Modal closes. Image uploads. Markdown link lands at your cursor. Cursor moves to the next line. Editor refocuses. Console says [image-gin/drop-gate] ImageKit upload → https://ik.imagekit.io/.... You keep typing.
If you'd rather not be asked every time, set the policy to external-only and your vault drops happen silently — only ImageKit / Imgur destinations bring up the modal.
The arc
We started with the third-party obsidian-imgur-plugin by Kirill Gavrilov. Its drag-confirm pattern is the right idea but biased the wrong way for our workflow: it asks "should we upload to Imgur or paste locally?" The answer for client-sensitive imagery is "neither, until you tell me where." We wanted vault-first, third-party host second, and only with deliberate confirmation each time.
We sketched a standalone plugin, then folded it back into Image Gin once it was clear that:
Image Gin already owns the ImageKit upload pipeline (the most useful private destination).
The
DropGateDestinationinterface is small enough that it doesn't justify its own plugin; it sits naturally next to the existing Recraft / Magnific / Ideogram services.One settings tab is easier than two.
So this is a feature add inside Image Gin, not a new sibling plugin. The ImageKitDestination is a thin adapter over ImageKitService.uploadFile() — no duplicated upload logic.
Architecture
src/
├── handlers/DropGateHandlers.ts editor-drop + editor-paste; re-entry guard;
│ synchronous preventDefault; cursor capture
├── modals/DropGateModal.ts deferred-promise modal; modalEl-attached
│ for proper widening; renders all destinations
│ with disabled-state for un-configured ones
├── destinations/
│ ├── types.ts DropGateDestination + DropGateContext
│ │ (carries the captured insertPos)
│ ├── VaultDestination.ts clipboardManager re-dispatch + explicit-API fallback
│ ├── ImageKitDestination.ts wraps existing ImageKitService.uploadFile
│ └── ImgurDestination.ts anonymous client-ID upload
├── utils/dropGateEvents.ts DragEventCopy, PasteEventCopy (re-entry guards),
│ allFilesAreImages, fileNameFor
├── settings/settings.ts +dropGate +imgur blocks; settings-tab UI
└── styles/drop-gate.css modal styles, scoped to .image-gin-drop-gate-modal main.ts grew by ~40 lines: instantiate three destinations, wire two workspace.on(...) calls, register a reset command, run the conflict check.
Load-bearing details
Three implementation details we expect to forget and want findable when we do:
// 1. Re-entry guard. Without this, our own VaultDestination's
// re-dispatch loops back through the handler. Infinite loop.
if (e instanceof DragEventCopy) return
// 2. Synchronous preventDefault — must come BEFORE any await,
// otherwise the browser already gave up on cancelling the default.
e.preventDefault()
// 3. Capture cursor synchronously, before awaiting the modal.
// By the time the user picks a destination, editor.getCursor()
// is unreliable — focus may have drifted. We pass insertPos
// through DropGateContext and use editor.replaceRange(md, pos)
// instead of editor.replaceSelection(md).
const insertPos = editor.getCursor() The vault fall-through uses an undocumented internal: view.currentMode.clipboardManager.handleDrop(DragEventCopy.create(e, files)). It's not in obsidian.d.ts but stable in practice; we cast through unknown and fall back to Vault.createBinary + FileManager.generateMarkdownLink if the surface ever disappears.
The editor-drop callback signature in obsidian.d.ts is (evt: DragEvent, editor: Editor, info: MarkdownView | MarkdownFileInfo) => any. MarkdownFileInfo is the canvas / non-markdown variant; we narrow with info instanceof MarkdownView and bail on canvas drops. Canvas support is a follow-up.
Settings surface
Two new sections in the Image Gin settings tab, both default-off:
| Section | Setting | Default |
| Drop Gate | Enable drop gate | Off |
| Drop Gate | Policy mode | Always confirm |
| Drop Gate | Default destination | Vault attachments |
| Drop Gate | Show "remember for session" checkbox | On |
| Drop Gate | ImageKit folder for drop-gate uploads | empty (inherits main) |
| Imgur | Enable Imgur destination | Off |
| Imgur | Imgur client ID | empty |
ImageKit's main settings (private key, endpoint, main upload folder) live in their existing section above — they pre-date this feature. The drop-gate uses them whenever imageKit.enabled is on.
What to test
Toggle Drop Gate on. Drop a PNG into a markdown note → modal appears with all three destinations. Vault is pre-selected. Click Insert → image lands in vault attachments, markdown link inserted at cursor.
Configure ImageKit and drop again → ImageKit row becomes selectable. Pick it → image uploads, markdown link with the ImageKit URL inserted.
Configure Imgur (anonymous client ID) and drop again → Imgur row becomes selectable.
Tick "Remember for this session," drop a second image in the same note → no modal, goes straight to your remembered destination.
Switch to a different note → modal returns on next drop.
Drop a non-image file → falls through to Obsidian's default behavior; modal does not appear.
With the
obsidian-imgur-plugincommunity plugin also enabled → you'll see a persistent Notice on Image Gin's load telling you to disable it. Disable it, reload, drops go through Image Gin only.
Reference
Plan:
content-farm/context-v/plans/Image-Drop-Confirmation-Gate.md— design rationale, threat model, phasing.Inspiration / interception pattern:
gavvvr/obsidian-imgur-plugin(MIT). Disable it if you enable Image Gin's Drop Gate; both registereditor-dropand you'll see two modals otherwise.Modal widening mechanic:
perplexed/context-v/issues/Widen-Modals-in-Obsidian-using-CSS.md—modalElvs.contentEl. The 720px width and 88vh height only take effect because the class is onmodalEl.ImageKit upload pipeline: existing
src/services/imagekitService.ts, reused via theImageKitDestinationadapter.
Next
Canvas support —
MarkdownFileInforather thanMarkdownView; needs a parallel handler.Optional
imagery_policy:frontmatter gate ("always confirm for this folder/file"), once we see whether always-on is friction-light enough not to need it.Filename / preview thumbnail in the modal — currently shows name + size + type only.