diff --git a/packages/app-desktop/services/plugins/PlatformImplementation.ts b/packages/app-desktop/services/plugins/PlatformImplementation.ts
index 34a5bf9933..ef978e752f 100644
--- a/packages/app-desktop/services/plugins/PlatformImplementation.ts
+++ b/packages/app-desktop/services/plugins/PlatformImplementation.ts
@@ -57,9 +57,12 @@ export default class PlatformImplementation extends BasePlatformImplementation {
 		this.joplin_ = {
 			views: {
 				dialogs: {
-					showMessageBox: async function(message: string) {
+					showMessageBox: async (message: string) => {
 						return bridge().showMessageBox(message);
 					},
+					showOpenDialog: async (options) => {
+						return bridge().showOpenDialog(options);
+					},
 				},
 			},
 		};
diff --git a/packages/generator-joplin/generators/app/templates/api/JoplinViewsDialogs.d.ts b/packages/generator-joplin/generators/app/templates/api/JoplinViewsDialogs.d.ts
index 8bc988739f..493d76f9f9 100644
--- a/packages/generator-joplin/generators/app/templates/api/JoplinViewsDialogs.d.ts
+++ b/packages/generator-joplin/generators/app/templates/api/JoplinViewsDialogs.d.ts
@@ -43,6 +43,12 @@ export default class JoplinViewsDialogs {
      * Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel"
      */
     showMessageBox(message: string): Promise<number>;
+    /**
+     * Displays a dialog to select a file or a directory. Same options and
+     * output as
+     * https://www.electronjs.org/docs/latest/api/dialog#dialogshowopendialogbrowserwindow-options
+     */
+    showOpenDialog(options: any): Promise<any>;
     /**
      * Sets the dialog HTML content
      */
diff --git a/packages/generator-joplin/generators/app/templates/api/noteListType.d.ts b/packages/generator-joplin/generators/app/templates/api/noteListType.d.ts
index 07c94d6e7b..03c0bb3df0 100644
--- a/packages/generator-joplin/generators/app/templates/api/noteListType.d.ts
+++ b/packages/generator-joplin/generators/app/templates/api/noteListType.d.ts
@@ -1,5 +1,5 @@
 import { Size } from './types';
-type ListRendererDatabaseDependency = 'folder.created_time' | 'folder.encryption_applied' | 'folder.encryption_cipher_text' | 'folder.icon' | 'folder.id' | 'folder.is_shared' | 'folder.master_key_id' | 'folder.parent_id' | 'folder.share_id' | 'folder.title' | 'folder.updated_time' | 'folder.user_created_time' | 'folder.user_updated_time' | 'folder.type_' | 'note.altitude' | 'note.application_data' | 'note.author' | 'note.body' | 'note.conflict_original_id' | 'note.created_time' | 'note.encryption_applied' | 'note.encryption_cipher_text' | 'note.id' | 'note.is_conflict' | 'note.is_shared' | 'note.is_todo' | 'note.latitude' | 'note.longitude' | 'note.markup_language' | 'note.master_key_id' | 'note.order' | 'note.parent_id' | 'note.share_id' | 'note.source' | 'note.source_application' | 'note.source_url' | 'note.title' | 'note.todo_completed' | 'note.todo_due' | 'note.updated_time' | 'note.user_created_time' | 'note.user_data' | 'note.user_updated_time' | 'note.type_';
+type ListRendererDatabaseDependency = 'folder.created_time' | 'folder.encryption_applied' | 'folder.encryption_cipher_text' | 'folder.icon' | 'folder.id' | 'folder.is_shared' | 'folder.master_key_id' | 'folder.parent_id' | 'folder.share_id' | 'folder.title' | 'folder.updated_time' | 'folder.user_created_time' | 'folder.user_data' | 'folder.user_updated_time' | 'folder.type_' | 'note.altitude' | 'note.application_data' | 'note.author' | 'note.body' | 'note.conflict_original_id' | 'note.created_time' | 'note.encryption_applied' | 'note.encryption_cipher_text' | 'note.id' | 'note.is_conflict' | 'note.is_shared' | 'note.is_todo' | 'note.latitude' | 'note.longitude' | 'note.markup_language' | 'note.master_key_id' | 'note.order' | 'note.parent_id' | 'note.share_id' | 'note.source' | 'note.source_application' | 'note.source_url' | 'note.title' | 'note.todo_completed' | 'note.todo_due' | 'note.updated_time' | 'note.user_created_time' | 'note.user_data' | 'note.user_updated_time' | 'note.type_';
 export declare enum ItemFlow {
     TopToBottom = "topToBottom",
     LeftToRight = "leftToRight"
diff --git a/packages/generator-joplin/generators/app/templates/api/noteListType.ts b/packages/generator-joplin/generators/app/templates/api/noteListType.ts
index 0f9cbb577e..112999c9ab 100644
--- a/packages/generator-joplin/generators/app/templates/api/noteListType.ts
+++ b/packages/generator-joplin/generators/app/templates/api/noteListType.ts
@@ -3,7 +3,7 @@
 import { Size } from './types';
 
 // AUTO-GENERATED by generate-database-type
-type ListRendererDatabaseDependency = 'folder.created_time' | 'folder.encryption_applied' | 'folder.encryption_cipher_text' | 'folder.icon' | 'folder.id' | 'folder.is_shared' | 'folder.master_key_id' | 'folder.parent_id' | 'folder.share_id' | 'folder.title' | 'folder.updated_time' | 'folder.user_created_time' | 'folder.user_updated_time' | 'folder.type_' | 'note.altitude' | 'note.application_data' | 'note.author' | 'note.body' | 'note.conflict_original_id' | 'note.created_time' | 'note.encryption_applied' | 'note.encryption_cipher_text' | 'note.id' | 'note.is_conflict' | 'note.is_shared' | 'note.is_todo' | 'note.latitude' | 'note.longitude' | 'note.markup_language' | 'note.master_key_id' | 'note.order' | 'note.parent_id' | 'note.share_id' | 'note.source' | 'note.source_application' | 'note.source_url' | 'note.title' | 'note.todo_completed' | 'note.todo_due' | 'note.updated_time' | 'note.user_created_time' | 'note.user_data' | 'note.user_updated_time' | 'note.type_';
+type ListRendererDatabaseDependency = 'folder.created_time' | 'folder.encryption_applied' | 'folder.encryption_cipher_text' | 'folder.icon' | 'folder.id' | 'folder.is_shared' | 'folder.master_key_id' | 'folder.parent_id' | 'folder.share_id' | 'folder.title' | 'folder.updated_time' | 'folder.user_created_time' | 'folder.user_data' | 'folder.user_updated_time' | 'folder.type_' | 'note.altitude' | 'note.application_data' | 'note.author' | 'note.body' | 'note.conflict_original_id' | 'note.created_time' | 'note.encryption_applied' | 'note.encryption_cipher_text' | 'note.id' | 'note.is_conflict' | 'note.is_shared' | 'note.is_todo' | 'note.latitude' | 'note.longitude' | 'note.markup_language' | 'note.master_key_id' | 'note.order' | 'note.parent_id' | 'note.share_id' | 'note.source' | 'note.source_application' | 'note.source_url' | 'note.title' | 'note.todo_completed' | 'note.todo_due' | 'note.updated_time' | 'note.user_created_time' | 'note.user_data' | 'note.user_updated_time' | 'note.type_';
 // AUTO-GENERATED by generate-database-type
 
 export enum ItemFlow {
diff --git a/packages/lib/services/plugins/BasePlatformImplementation.ts b/packages/lib/services/plugins/BasePlatformImplementation.ts
index 8bc9cabc4a..c9d7e3334e 100644
--- a/packages/lib/services/plugins/BasePlatformImplementation.ts
+++ b/packages/lib/services/plugins/BasePlatformImplementation.ts
@@ -7,6 +7,7 @@ import { Implementation as ImagingImplementation } from './api/JoplinImaging';
 
 export interface JoplinViewsDialogs {
 	showMessageBox(message: string): Promise<number>;
+	showOpenDialog(options: any): Promise<any>;
 }
 
 export interface JoplinViews {
diff --git a/packages/lib/services/plugins/api/JoplinViewsDialogs.ts b/packages/lib/services/plugins/api/JoplinViewsDialogs.ts
index 6617b5d352..2130571378 100644
--- a/packages/lib/services/plugins/api/JoplinViewsDialogs.ts
+++ b/packages/lib/services/plugins/api/JoplinViewsDialogs.ts
@@ -5,6 +5,7 @@ import createViewHandle from '../utils/createViewHandle';
 import WebviewController, { ContainerType } from '../WebviewController';
 import { ButtonSpec, ViewHandle, DialogResult } from './types';
 import { _ } from '../../../locale';
+import { JoplinViewsDialogs as JoplinViewsDialogsImplementation } from '../BasePlatformImplementation';
 
 /**
  * Allows creating and managing dialogs. A dialog is modal window that
@@ -39,7 +40,7 @@ export default class JoplinViewsDialogs {
 
 	private store: any;
 	private plugin: Plugin;
-	private implementation_: any;
+	private implementation_: JoplinViewsDialogsImplementation;
 
 	public constructor(implementation: any, plugin: Plugin, store: any) {
 		this.store = store;
@@ -73,6 +74,15 @@ export default class JoplinViewsDialogs {
 		return this.implementation_.showMessageBox(`${_('(In plugin: %s)', this.plugin.manifest.name)}\n\n${message}`);
 	}
 
+	/**
+	 * Displays a dialog to select a file or a directory. Same options and
+	 * output as
+	 * https://www.electronjs.org/docs/latest/api/dialog#dialogshowopendialogbrowserwindow-options
+	 */
+	public async showOpenDialog(options: any): Promise<any> {
+		return this.implementation_.showOpenDialog(options);
+	}
+
 	/**
 	 * Sets the dialog HTML content
 	 */