diff --git a/packages/bruno-app/src/components/Preferences/General/StyledWrapper.js b/packages/bruno-app/src/components/Preferences/General/StyledWrapper.js
index 570d276c0..a71eb779f 100644
--- a/packages/bruno-app/src/components/Preferences/General/StyledWrapper.js
+++ b/packages/bruno-app/src/components/Preferences/General/StyledWrapper.js
@@ -24,7 +24,7 @@ const StyledWrapper = styled.div`
}
}
- .default-collection-location-input {
+ .default-location-input {
max-width: 28rem;
}
`;
diff --git a/packages/bruno-app/src/components/Preferences/General/index.js b/packages/bruno-app/src/components/Preferences/General/index.js
index ce072267c..f8b8f112c 100644
--- a/packages/bruno-app/src/components/Preferences/General/index.js
+++ b/packages/bruno-app/src/components/Preferences/General/index.js
@@ -60,7 +60,7 @@ const General = () => {
oauth2: Yup.object({
useSystemBrowser: Yup.boolean()
}),
- defaultCollectionLocation: Yup.string().max(1024)
+ defaultLocation: Yup.string().max(1024)
});
const formik = useFormik({
@@ -83,7 +83,7 @@ const General = () => {
oauth2: {
useSystemBrowser: get(preferences, 'request.oauth2.useSystemBrowser', false)
},
- defaultCollectionLocation: get(preferences, 'general.defaultCollectionLocation', '')
+ defaultLocation: get(preferences, 'general.defaultLocation', '')
},
validationSchema: preferencesSchema,
onSubmit: async (values) => {
@@ -121,7 +121,7 @@ const General = () => {
interval: newPreferences.autoSave.interval
},
general: {
- defaultCollectionLocation: newPreferences.defaultCollectionLocation
+ defaultLocation: newPreferences.defaultLocation
}
}))
.catch((err) => console.log(err) && toast.error('Failed to update preferences'));
@@ -163,11 +163,11 @@ const General = () => {
dispatch(browseDirectory())
.then((dirPath) => {
if (typeof dirPath === 'string') {
- formik.setFieldValue('defaultCollectionLocation', dirPath);
+ formik.setFieldValue('defaultLocation', dirPath);
}
})
.catch((error) => {
- formik.setFieldValue('defaultCollectionLocation', '');
+ formik.setFieldValue('defaultLocation', '');
console.error(error);
});
};
@@ -356,35 +356,38 @@ const General = () => {
{formik.errors.autoSave.interval}
)}
-
- {formik.touched.defaultCollectionLocation && formik.errors.defaultCollectionLocation ? (
- {formik.errors.defaultCollectionLocation}
+ {formik.touched.defaultLocation && formik.errors.defaultLocation ? (
+ {formik.errors.defaultLocation}
) : null}
diff --git a/packages/bruno-app/src/components/Sidebar/BulkImportCollectionLocation/index.js b/packages/bruno-app/src/components/Sidebar/BulkImportCollectionLocation/index.js
index e02783d18..906b0ab3a 100644
--- a/packages/bruno-app/src/components/Sidebar/BulkImportCollectionLocation/index.js
+++ b/packages/bruno-app/src/components/Sidebar/BulkImportCollectionLocation/index.js
@@ -133,7 +133,7 @@ export const BulkImportCollectionLocation = ({
const activeWorkspace = workspaces.find((w) => w.uid === activeWorkspaceUid);
const isDefaultWorkspace = !activeWorkspace || activeWorkspace.type === 'default';
const defaultLocation = isDefaultWorkspace
- ? get(preferences, 'general.defaultCollectionLocation', '')
+ ? get(preferences, 'general.defaultLocation', '')
: (activeWorkspace?.pathname ? `${activeWorkspace.pathname}/collections` : '');
const [status, setStatus] = useState({});
diff --git a/packages/bruno-app/src/components/Sidebar/CloneGitRespository/index.js b/packages/bruno-app/src/components/Sidebar/CloneGitRespository/index.js
index 0c0ece623..531330db2 100644
--- a/packages/bruno-app/src/components/Sidebar/CloneGitRespository/index.js
+++ b/packages/bruno-app/src/components/Sidebar/CloneGitRespository/index.js
@@ -33,7 +33,7 @@ const CloneGitRepository = ({ onClose, onFinish, collectionRepositoryUrl = null
const activeWorkspace = workspaces.find((w) => w.uid === activeWorkspaceUid);
const isDefaultWorkspace = !activeWorkspace || activeWorkspace.type === 'default';
const defaultLocation = isDefaultWorkspace
- ? get(preferences, 'general.defaultCollectionLocation', '')
+ ? get(preferences, 'general.defaultLocation', '')
: (activeWorkspace?.pathname ? `${activeWorkspace.pathname}/collections` : '');
const inputRef = useRef();
const dispatch = useDispatch();
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CloneCollection/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CloneCollection/index.js
index 6dbb69064..697adb839 100644
--- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CloneCollection/index.js
+++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CloneCollection/index.js
@@ -26,7 +26,7 @@ const CloneCollection = ({ onClose, collectionUid }) => {
const isDefaultWorkspace = activeWorkspace?.type === 'default';
const defaultLocation = isDefaultWorkspace
- ? get(preferences, 'general.defaultCollectionLocation', '')
+ ? get(preferences, 'general.defaultLocation', '')
: (activeWorkspace?.pathname ? `${activeWorkspace.pathname}/collections` : '');
const { name } = collection;
diff --git a/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js b/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js
index 4b0235f80..18495cbb4 100644
--- a/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js
+++ b/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js
@@ -32,7 +32,7 @@ const CreateCollection = ({ onClose, defaultLocation: propDefaultLocation }) =>
const activeWorkspace = workspaces.find((w) => w.uid === workspaceUid);
const isDefaultWorkspace = activeWorkspace?.type === 'default';
- const defaultLocation = isDefaultWorkspace ? get(preferences, 'general.defaultCollectionLocation', '') : (activeWorkspace?.pathname ? `${activeWorkspace.pathname}/collections` : '');
+ const defaultLocation = isDefaultWorkspace ? get(preferences, 'general.defaultLocation', '') : (activeWorkspace?.pathname ? `${activeWorkspace.pathname}/collections` : '');
const formik = useFormik({
enableReinitialize: true,
diff --git a/packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js b/packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js
index 4efd3c54c..52d7434c5 100644
--- a/packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js
+++ b/packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js
@@ -110,7 +110,7 @@ const ImportCollectionLocation = ({ onClose, handleSubmit, rawData, format }) =>
const isDefaultWorkspace = !activeWorkspace || activeWorkspace.type === 'default';
const defaultLocation = isDefaultWorkspace
- ? get(preferences, 'general.defaultCollectionLocation', '')
+ ? get(preferences, 'general.defaultLocation', '')
: (activeWorkspace?.pathname ? `${activeWorkspace.pathname}/collections` : '');
const collectionName = getCollectionName(format, rawData);
diff --git a/packages/bruno-app/src/components/WorkspaceSidebar/CreateWorkspace/index.js b/packages/bruno-app/src/components/WorkspaceSidebar/CreateWorkspace/index.js
index 2ed0caa11..396966f4f 100644
--- a/packages/bruno-app/src/components/WorkspaceSidebar/CreateWorkspace/index.js
+++ b/packages/bruno-app/src/components/WorkspaceSidebar/CreateWorkspace/index.js
@@ -12,20 +12,24 @@ import { browseDirectory } from 'providers/ReduxStore/slices/collections/actions
import { multiLineMsg } from 'utils/common/index';
import { formatIpcError } from 'utils/common/error';
import { sanitizeName, validateName, validateNameError } from 'utils/common/regex';
+import get from 'lodash/get';
const CreateWorkspace = ({ onClose }) => {
const inputRef = useRef();
const dispatch = useDispatch();
const workspaces = useSelector((state) => state.workspaces.workspaces);
+ const preferences = useSelector((state) => state.app.preferences);
const [isSubmitting, setIsSubmitting] = useState(false);
const [isEditing, setIsEditing] = useState(false);
+ const defaultLocation = get(preferences, 'general.defaultLocation', '');
+
const formik = useFormik({
enableReinitialize: true,
initialValues: {
workspaceName: '',
workspaceFolderName: '',
- workspaceLocation: ''
+ workspaceLocation: defaultLocation
},
validationSchema: Yup.object({
workspaceName: Yup.string()
diff --git a/packages/bruno-app/src/components/WorkspaceSidebar/ImportWorkspace/index.js b/packages/bruno-app/src/components/WorkspaceSidebar/ImportWorkspace/index.js
index 5aa759830..b7d006062 100644
--- a/packages/bruno-app/src/components/WorkspaceSidebar/ImportWorkspace/index.js
+++ b/packages/bruno-app/src/components/WorkspaceSidebar/ImportWorkspace/index.js
@@ -1,8 +1,9 @@
import React, { useState, useRef, useEffect } from 'react';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
import { useFormik } from 'formik';
import * as Yup from 'yup';
import toast from 'react-hot-toast';
+import get from 'lodash/get';
import { IconFileZip } from '@tabler/icons';
import Modal from 'components/Modal';
import { browseDirectory } from 'providers/ReduxStore/slices/collections/actions';
@@ -13,16 +14,19 @@ import Help from 'components/Help';
const ImportWorkspace = ({ onClose }) => {
const dispatch = useDispatch();
+ const preferences = useSelector((state) => state.app.preferences);
const [dragActive, setDragActive] = useState(false);
const [selectedFile, setSelectedFile] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const fileInputRef = useRef(null);
const locationInputRef = useRef(null);
+ const defaultLocation = get(preferences, 'general.defaultLocation', '');
+
const formik = useFormik({
enableReinitialize: true,
initialValues: {
- workspaceLocation: ''
+ workspaceLocation: defaultLocation
},
validationSchema: Yup.object({
workspaceLocation: Yup.string().min(1, 'location is required').required('location is required')
diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/app.js b/packages/bruno-app/src/providers/ReduxStore/slices/app.js
index ee6e9d3e0..a0e86da71 100644
--- a/packages/bruno-app/src/providers/ReduxStore/slices/app.js
+++ b/packages/bruno-app/src/providers/ReduxStore/slices/app.js
@@ -34,7 +34,7 @@ const initialState = {
codeFont: 'default'
},
general: {
- defaultCollectionLocation: ''
+ defaultLocation: ''
},
autoSave: {
enabled: false,
diff --git a/packages/bruno-electron/src/app/onboarding.js b/packages/bruno-electron/src/app/onboarding.js
index b2de55e77..f507cfb1d 100644
--- a/packages/bruno-electron/src/app/onboarding.js
+++ b/packages/bruno-electron/src/app/onboarding.js
@@ -3,26 +3,7 @@ const path = require('node:path');
const { app } = require('electron');
const { preferencesUtil } = require('../store/preferences');
const { importCollection, findUniqueFolderName } = require('../utils/collection-import');
-
-/**
- * Get the default location for collections
- * Tries documents first, then desktop, then userData as fallback
- */
-function getDefaultCollectionLocation() {
- const preferredPaths = ['documents', 'desktop', 'userData'];
-
- for (const pathType of preferredPaths) {
- try {
- return app.getPath(pathType);
- } catch (error) {
- console.warn(`Failed to get ${pathType} path:`, error.message);
- // Continue to next path
- }
- }
-
- // This should never happen since userData should always be available
- throw new Error('No valid collection location found');
-}
+const { resolveDefaultLocation } = require('../utils/default-location');
/**
* Import sample collection for new users
@@ -86,7 +67,7 @@ async function onboardUser(mainWindow, lastOpenedCollections) {
return;
}
- const collectionLocation = getDefaultCollectionLocation();
+ const collectionLocation = resolveDefaultLocation();
await importSampleCollection(collectionLocation, mainWindow);
}
diff --git a/packages/bruno-electron/src/ipc/preferences.js b/packages/bruno-electron/src/ipc/preferences.js
index 1a2deb28c..83adad57d 100644
--- a/packages/bruno-electron/src/ipc/preferences.js
+++ b/packages/bruno-electron/src/ipc/preferences.js
@@ -4,11 +4,20 @@ const { getGitVersion } = require('../utils/git');
const { globalEnvironmentsStore } = require('../store/global-environments');
const { parsedFileCacheStore } = require('../store/parsed-file-cache-idb');
const { getCachedSystemProxy, refreshSystemProxy } = require('../store/system-proxy');
+const { resolveDefaultLocation } = require('../utils/default-location');
const registerPreferencesIpc = (mainWindow) => {
ipcMain.handle('renderer:ready', async (event) => {
// load preferences
const preferences = getPreferences();
+
+ // Set the default location if it hasn't been set by the user
+ if (!preferences.general?.defaultLocation) {
+ preferences.general ??= {};
+ preferences.general.defaultLocation = resolveDefaultLocation();
+ await savePreferences(preferences);
+ }
+
mainWindow.webContents.send('main:load-preferences', preferences);
try {
diff --git a/packages/bruno-electron/src/store/preferences.js b/packages/bruno-electron/src/store/preferences.js
index 19b82dec5..fe0545d26 100644
--- a/packages/bruno-electron/src/store/preferences.js
+++ b/packages/bruno-electron/src/store/preferences.js
@@ -50,7 +50,7 @@ const defaultPreferences = {
hasLaunchedBefore: false
},
general: {
- defaultCollectionLocation: '',
+ defaultLocation: '',
defaultWorkspacePath: ''
},
autoSave: {
@@ -107,7 +107,7 @@ const preferencesSchema = Yup.object().shape({
hasLaunchedBefore: Yup.boolean()
}),
general: Yup.object({
- defaultCollectionLocation: Yup.string().max(1024).nullable(),
+ defaultLocation: Yup.string().max(1024).nullable(),
defaultWorkspacePath: Yup.string().max(1024).nullable()
}),
autoSave: Yup.object({
@@ -230,6 +230,14 @@ class PreferencesStore {
}
}
+ // Migrate from defaultCollectionLocation to defaultLocation
+ if (preferences.general?.defaultCollectionLocation !== undefined
+ && preferences.general?.defaultLocation === undefined) {
+ preferences.general.defaultLocation = preferences.general.defaultCollectionLocation;
+ delete preferences.general.defaultCollectionLocation;
+ this.store.set('preferences', preferences);
+ }
+
return merge({}, defaultPreferences, preferences);
}
diff --git a/packages/bruno-electron/src/utils/default-location.js b/packages/bruno-electron/src/utils/default-location.js
new file mode 100644
index 000000000..e84facf20
--- /dev/null
+++ b/packages/bruno-electron/src/utils/default-location.js
@@ -0,0 +1,29 @@
+const fs = require('node:fs');
+const path = require('node:path');
+const { app } = require('electron');
+
+const BRUNO_DIR_NAME = 'bruno';
+
+/**
+ * Returns the default location where new workspaces and collections are stored.
+ * Checks ~/Documents/bruno if available, otherwise falls back to the app's data directory
+ */
+function resolveDefaultLocation() {
+ const defaultPaths = [
+ path.join(app.getPath('documents'), BRUNO_DIR_NAME),
+ app.getPath('userData')
+ ];
+
+ for (const dirPath of defaultPaths) {
+ try {
+ fs.mkdirSync(dirPath, { recursive: true });
+ return dirPath;
+ } catch (error) {
+ console.warn(`Failed to create directory at ${dirPath}:`, error.message);
+ }
+ }
+
+ throw new Error('Failed to create default location');
+}
+
+module.exports = { resolveDefaultLocation };
diff --git a/packages/bruno-electron/tests/store/default-location-migration.spec.js b/packages/bruno-electron/tests/store/default-location-migration.spec.js
new file mode 100644
index 000000000..7912b9bb1
--- /dev/null
+++ b/packages/bruno-electron/tests/store/default-location-migration.spec.js
@@ -0,0 +1,60 @@
+let mockStoreData = {};
+
+jest.mock('electron-store', () => {
+ return jest.fn().mockImplementation((opts = {}) => {
+ return {
+ get: (key, fallback) => (key in mockStoreData ? mockStoreData[key] : fallback),
+ set: (key, value) => {
+ mockStoreData[key] = value;
+ }
+ };
+ });
+});
+
+const { getPreferences } = require('../../src/store/preferences');
+
+describe('Default Location Migration', () => {
+ beforeEach(() => {
+ // Reset mock store data before each test
+ mockStoreData = {};
+ });
+
+ it('should migrate defaultCollectionLocation to defaultLocation', () => {
+ mockStoreData['preferences'] = {
+ general: {
+ defaultCollectionLocation: '/home/user/collections'
+ }
+ };
+
+ const preferences = getPreferences();
+
+ expect(preferences.general.defaultLocation).toBe('/home/user/collections');
+ expect(mockStoreData['preferences'].general.defaultCollectionLocation).toBeUndefined();
+ expect(mockStoreData['preferences'].general.defaultLocation).toBe('/home/user/collections');
+ });
+
+ it('should not migrate if defaultLocation already exists', () => {
+ mockStoreData['preferences'] = {
+ general: {
+ defaultCollectionLocation: '/old/path',
+ defaultLocation: '/new/path'
+ }
+ };
+
+ const preferences = getPreferences();
+
+ expect(preferences.general.defaultLocation).toBe('/new/path');
+ // Old key is left untouched
+ expect(mockStoreData['preferences'].general.defaultCollectionLocation).toBe('/old/path');
+ });
+
+ it('should return default empty string when neither key exists', () => {
+ mockStoreData['preferences'] = {};
+
+ const preferences = getPreferences();
+
+ expect(preferences.general.defaultLocation).toBe('');
+ // No migration occurred — store unchanged
+ expect(mockStoreData['preferences'].general).toBeUndefined();
+ });
+});
diff --git a/tests/preferences/default-collection-location/default-collection-location.spec.js b/tests/preferences/default-collection-location/default-collection-location.spec.js
index 40b49a6f0..d6eec7305 100644
--- a/tests/preferences/default-collection-location/default-collection-location.spec.js
+++ b/tests/preferences/default-collection-location/default-collection-location.spec.js
@@ -1,6 +1,6 @@
import { test, expect } from '../../../playwright';
-test.describe('Default Collection Location Feature', () => {
+test.describe('Default Location Feature', () => {
test('Should hydrate the default location from preferences', async ({ pageWithUserData: page }) => {
// open preferences tab
await page.locator('.preferences-button').click();
@@ -12,7 +12,7 @@ test.describe('Default Collection Location Feature', () => {
await page.getByRole('tab', { name: 'General' }).click();
// verify the default location is pre-filled
- const defaultLocationInput = page.locator('.default-collection-location-input');
+ const defaultLocationInput = page.locator('.default-location-input');
await expect(defaultLocationInput).toHaveValue('/tmp/bruno-collections');
});
@@ -27,7 +27,7 @@ test.describe('Default Collection Location Feature', () => {
await page.getByRole('tab', { name: 'General' }).click();
// set a default location (readonly input, remove readonly then fill)
- const defaultLocationInput = page.locator('.default-collection-location-input');
+ const defaultLocationInput = page.locator('.default-location-input');
await defaultLocationInput.evaluate((el) => {
const input = el;
input.removeAttribute('readonly');
@@ -91,7 +91,7 @@ test.describe('Default Collection Location Feature', () => {
await page.getByRole('tab', { name: 'General' }).click();
// clear the default location field (readonly input, remove readonly then clear)
- const defaultLocationInput = page.locator('.default-collection-location-input');
+ const defaultLocationInput = page.locator('.default-location-input');
await defaultLocationInput.evaluate((el) => {
const input = el;
input.removeAttribute('readonly');
diff --git a/tests/preferences/default-collection-location/init-user-data/preferences.json b/tests/preferences/default-collection-location/init-user-data/preferences.json
index 9d0a76c50..6ba9f0b83 100644
--- a/tests/preferences/default-collection-location/init-user-data/preferences.json
+++ b/tests/preferences/default-collection-location/init-user-data/preferences.json
@@ -3,7 +3,7 @@
"lastOpenedCollections": ["{{projectRoot}}/tests/preferences/default-collection-location/collection"],
"preferences": {
"general": {
- "defaultCollectionLocation": "/tmp/bruno-collections"
+ "defaultLocation": "/tmp/bruno-collections"
}
}
}