diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js index 3c9f27115..b840885de 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js @@ -6,7 +6,7 @@ import { useDrag, useDrop } from 'react-dnd'; import { IconChevronRight, IconDots } from '@tabler/icons'; import { useSelector, useDispatch } from 'react-redux'; import { addTab, focusTab } from 'providers/ReduxStore/slices/tabs'; -import { moveItem, sendRequest } from 'providers/ReduxStore/slices/collections/actions'; +import { moveItem, revealInFinder, sendRequest } from 'providers/ReduxStore/slices/collections/actions'; import { collectionFolderClicked } from 'providers/ReduxStore/slices/collections'; import Dropdown from 'components/Dropdown'; import NewRequest from 'components/Sidebar/NewRequest'; @@ -220,6 +220,12 @@ const CollectionItem = ({ item, collection, searchText }) => { } }; + const handleReveal = () => { + dispatch(revealInFinder(item.pathname)).catch((error) => { + toast.error('Error revealing file:', error); + }); + }; + const requestItems = sortRequestItems(filter(item.items, (i) => isItemARequest(i))); const folderItems = sortFolderItems(filter(item.items, (i) => isItemAFolder(i))); @@ -371,6 +377,17 @@ const CollectionItem = ({ item, collection, searchText }) => { Generate Code )} + {!isFolder && ( +
{ + dropdownTippyRef.current.hide(); + handleReveal(); + }} + > + Reveal in Finder +
+ )}
{ diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index de9ad78e9..513c2f798 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -1219,3 +1219,12 @@ export const mountCollection = ({ collectionUid, collectionPathname, brunoConfig }); }); }; + + export const revealInFinder = (collectionPath) => () => { + return new Promise((resolve, reject) => { + const { ipcRenderer } = window; + + ipcRenderer.invoke('renderer:reveal-in-finder', collectionPath).then(resolve).catch(reject); + }); + }; + diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index b9061a227..f04b49619 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -3,6 +3,7 @@ const fs = require('fs'); const fsExtra = require('fs-extra'); const os = require('os'); const path = require('path'); +const { exec } = require('child_process'); const { ipcMain, shell, dialog, app } = require('electron'); const { envJsonToBru, bruToJson, jsonToBruViaWorker, jsonToCollectionBru, bruToJsonViaWorker } = require('../bru'); @@ -909,6 +910,41 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection watcher.addWatcher(mainWindow, collectionPathname, collectionUid, brunoConfig, false, shouldLoadCollectionAsync); }); + + ipcMain.handle('renderer:reveal-in-finder', async (event, filePath) => { + try { + if (!filePath) { + throw new Error('File path is required.'); + } + + const resolvedPath = path.resolve(filePath); + + if (!fs.existsSync(resolvedPath)) { + throw new Error('The specified file does not exist.'); + } + + console.log(process.platform, "process.platform") + + switch (process.platform) { + case 'darwin': // macOS + shell.showItemInFolder(resolvedPath); + break; + case 'win32': // Windows + shell.showItemInFolder(resolvedPath); + break; + case 'linux': // Linux + exec(`xdg-open "${resolvedPath}"`); + break; + default: + throw new Error('Unsupported platform.'); + } + + return { success: true }; + } catch (error) { + console.error('Error in reveal-in-finder:', error); + return { success: false, message: error.message }; + } + }); }; const registerMainEventHandlers = (mainWindow, watcher, lastOpenedCollections) => {