From cd2f5d52332c456b52282e889309b23f907d75e0 Mon Sep 17 00:00:00 2001 From: lohit Date: Mon, 17 Mar 2025 01:36:39 +0530 Subject: [PATCH 01/10] filename support ui updates, regex update for generating validation error for the last char, getEncoding function headers props optional chaining validation (#4243) --- .../components/PathDisplay/StyledWrapper.js | 1 + .../src/components/PathDisplay/index.js | 71 +++++---------- .../Collection/CloneCollection/index.js | 87 +++++++++++-------- .../CloneCollectionItem/index.js | 51 ++++++----- .../CollectionItemInfo/index.js | 2 +- .../RenameCollectionItem/index.js | 44 ++++++---- .../Collection/CollectionItem/index.js | 1 - .../Sidebar/CreateCollection/index.js | 84 ++++++++++-------- .../src/components/Sidebar/NewFolder/index.js | 44 ++++++---- .../components/Sidebar/NewRequest/index.js | 37 +++++--- packages/bruno-app/src/utils/common/index.js | 2 +- packages/bruno-app/src/utils/common/regex.js | 3 +- .../bruno-app/src/utils/curl/curl-to-json.js | 1 - 13 files changed, 232 insertions(+), 196 deletions(-) diff --git a/packages/bruno-app/src/components/PathDisplay/StyledWrapper.js b/packages/bruno-app/src/components/PathDisplay/StyledWrapper.js index bdaca8bbf..1b6ace094 100644 --- a/packages/bruno-app/src/components/PathDisplay/StyledWrapper.js +++ b/packages/bruno-app/src/components/PathDisplay/StyledWrapper.js @@ -1,6 +1,7 @@ import styled from 'styled-components'; const StyledWrapper = styled.div` + width: 100%; .path-display { background: ${(props) => props.theme.requestTabPanel.url.bg}; border-radius: 4px; diff --git a/packages/bruno-app/src/components/PathDisplay/index.js b/packages/bruno-app/src/components/PathDisplay/index.js index b7ffa087f..b867abb7b 100644 --- a/packages/bruno-app/src/components/PathDisplay/index.js +++ b/packages/bruno-app/src/components/PathDisplay/index.js @@ -1,63 +1,34 @@ import React from 'react'; -import { IconEdit, IconFolder, IconFile } from '@tabler/icons'; +import { IconFolder, IconFile } from '@tabler/icons'; import path from 'utils/common/path'; import StyledWrapper from './StyledWrapper'; const PathDisplay = ({ - collection, - item, - filename, - extension = '.bru', - showExtension = true, - toggleEditingFilename, - showDirectory = false + dirName = '', + baseName = '' }) => { - const relativePath = item?.pathname && path.relative(collection?.pathname, showDirectory ? path.dirname(item?.pathname) : item?.pathname); - const pathSegments = relativePath?.split(path.sep).filter(Boolean); + const extName = path.extname(baseName) + const hasExtension = Boolean(extName); + const pathSegments = dirName?.split(path.sep).filter(Boolean); return ( -
-
- - toggleEditingFilename(true)} - /> -
-
-
-
- {showExtension ? : } -
-
-
- {collection?.name} -
- - {pathSegments?.length > 0 && pathSegments?.map((segment, index) => ( - - / -
- {segment} -
-
- ))} - - {collection && ( - / - )} - - - {filename} - {showExtension && filename?.length ? ( - {extension} - ) : null} - -
+
+
+
+ {hasExtension ? : }
+ {pathSegments?.map((segment, index) => ( + +
+ {segment} +
+ / +
+ ))} + + {baseName} +
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 30337e3cb..35235df94 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 @@ -9,18 +9,19 @@ import Modal from 'components/Modal'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import PathDisplay from 'components/PathDisplay/index'; import { useState } from 'react'; -import { IconArrowBackUp } from "@tabler/icons"; +import { IconArrowBackUp, IconEdit } from "@tabler/icons"; const CloneCollection = ({ onClose, collection }) => { const inputRef = useRef(); const dispatch = useDispatch(); - const [isEditingFilename, toggleEditingFilename] = useState(false); + const [isEditing, toggleEditing] = useState(false); + const { name } = collection; const formik = useFormik({ enableReinitialize: true, initialValues: { - collectionName: '', - collectionFolderName: '', + collectionName: `${name} copy`, + collectionFolderName: `${sanitizeName(name)} copy`, collectionLocation: '' }, validationSchema: Yup.object({ @@ -31,7 +32,7 @@ const CloneCollection = ({ onClose, collection }) => { collectionFolderName: Yup.string() .min(1, 'must be at least 1 character') .max(255, 'must be 255 characters or less') - .test('is-valid-dir-name', function(value) { + .test('is-valid-collection-name', function(value) { const isValid = validateName(value); return isValid ? true : this.createError({ message: validateNameError(value) }); }) @@ -92,7 +93,7 @@ const CloneCollection = ({ onClose, collection }) => { className="block textbox mt-2 w-full" onChange={(e) => { formik.handleChange(e); - !isEditingFilename && formik.setFieldValue('collectionFolderName', sanitizeName(e.target.value)); + !isEditing && formik.setFieldValue('collectionFolderName', sanitizeName(e.target.value)); }} autoComplete="off" autoCorrect="off" @@ -128,41 +129,51 @@ const CloneCollection = ({ onClose, collection }) => { Browse
- {isEditingFilename ? - <> -
-
- - toggleEditingFilename(false)} - /> -
- +
+ + toggleEditing(false)} />
- + +
: - +
+
+ + toggleEditing(true)} + /> +
+
+ +
+
} {formik.touched.collectionFolderName && formik.errors.collectionFolderName ? (
{formik.errors.collectionFolderName}
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js index 37d144735..db9b0e895 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js @@ -6,24 +6,23 @@ import Modal from 'components/Modal'; import { useDispatch } from 'react-redux'; import { isItemAFolder } from 'utils/tabs'; import { cloneItem } from 'providers/ReduxStore/slices/collections/actions'; -import { IconArrowBackUp } from '@tabler/icons'; -import * as path from 'path'; +import { IconArrowBackUp, IconEdit } from "@tabler/icons"; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import PathDisplay from 'components/PathDisplay/index'; +import path from 'utils/common/path'; const CloneCollectionItem = ({ collection, item, onClose }) => { const dispatch = useDispatch(); const isFolder = isItemAFolder(item); const inputRef = useRef(); - const [isEditingFilename, toggleEditingFilename] = useState(false); + const [isEditing, toggleEditing] = useState(false); const itemName = item?.name; const itemType = item?.type; - const itemFilename = item?.filename ? path.parse(item?.filename).name : ''; const formik = useFormik({ enableReinitialize: true, initialValues: { - name: itemName, - filename: sanitizeName(itemFilename) + name: `${itemName} copy`, + filename: `${sanitizeName(itemName)} copy` }, validationSchema: Yup.object({ name: Yup.string() @@ -34,7 +33,7 @@ const CloneCollectionItem = ({ collection, item, onClose }) => { .min(1, 'must be at least 1 character') .max(255, 'must be 255 characters or less') .required('name is required') - .test('is-valid-filename', function(value) { + .test('is-valid-name', function(value) { const isValid = validateName(value); return isValid ? true : this.createError({ message: validateNameError(value) }); }) @@ -71,7 +70,7 @@ const CloneCollectionItem = ({ collection, item, onClose }) => {
e.preventDefault()}>
{ spellCheck="false" onChange={e => { formik.setFieldValue('name', e.target.value); - !isEditingFilename && formik.setFieldValue('filename', sanitizeName(e.target.value)); + !isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value)); }} value={formik.values.name || ''} /> {formik.touched.name && formik.errors.name ?
{formik.errors.name}
: null}
- {isEditingFilename ? ( + {isEditing ? (
toggleEditingFilename(false)} + onClick={() => toggleEditing(false)} />
@@ -123,15 +122,25 @@ const CloneCollectionItem = ({ collection, item, onClose }) => {
) : ( - +
+
+ + toggleEditing(true)} + /> +
+
+ +
+
)} {formik.touched.filename && formik.errors.filename ? (
{formik.errors.filename}
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js index 1b20e666f..22d546326 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js @@ -1,6 +1,6 @@ import React from 'react'; import Modal from 'components/Modal'; -import * as path from 'path'; +import path from 'utils/common/path'; const CollectionItemInfo = ({ collection, item, onClose }) => { const { pathname: collectionPathname } = collection; diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js index 9d174560d..50c3dee22 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js @@ -6,7 +6,7 @@ import { useDispatch } from 'react-redux'; import { isItemAFolder } from 'utils/tabs'; import { renameItem, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import path from 'utils/common/path'; -import { IconArrowBackUp } from '@tabler/icons'; +import { IconArrowBackUp, IconEdit } from '@tabler/icons'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import toast from 'react-hot-toast'; import { closeTabs } from 'providers/ReduxStore/slices/tabs'; @@ -16,7 +16,7 @@ const RenameCollectionItem = ({ collection, item, onClose }) => { const dispatch = useDispatch(); const isFolder = isItemAFolder(item); const inputRef = useRef(); - const [isEditingFilename, toggleEditingFilename] = useState(false); + const [isEditing, toggleEditing] = useState(false); const itemName = item?.name; const itemType = item?.type; const itemFilename = item?.filename ? path.parse(item?.filename).name : ''; @@ -35,7 +35,7 @@ const RenameCollectionItem = ({ collection, item, onClose }) => { .min(1, 'must be at least 1 character') .max(255, 'must be 255 characters or less') .required('name is required') - .test('is-valid-filename', function(value) { + .test('is-valid-name', function(value) { const isValid = validateName(value); return isValid ? true : this.createError({ message: validateNameError(value) }); }) @@ -90,7 +90,7 @@ const RenameCollectionItem = ({ collection, item, onClose }) => { {e.preventDefault()}}>
{ spellCheck="false" onChange={e => { formik.setFieldValue('name', e.target.value); - !isEditingFilename && formik.setFieldValue('filename', sanitizeName(e.target.value)); + !isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value)); }} value={formik.values.name || ''} /> {formik.touched.name && formik.errors.name ?
{formik.errors.name}
: null}
- {isEditingFilename ? ( + {isEditing ? (
toggleEditingFilename(false)} + onClick={() => toggleEditing(false)} />
@@ -142,15 +142,25 @@ const RenameCollectionItem = ({ collection, item, onClose }) => {
) : ( - +
+
+ + toggleEditing(true)} + /> +
+
+ +
+
)} {formik.touched.filename && formik.errors.filename ? (
{formik.errors.filename}
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 41a8a9d32..6201dc7c0 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 @@ -24,7 +24,6 @@ import toast from 'react-hot-toast'; import StyledWrapper from './StyledWrapper'; import NetworkError from 'components/ResponsePane/NetworkError/index'; import CollectionItemInfo from './CollectionItemInfo/index'; -import { findItemInCollection } from 'utils/collections'; import CollectionItemIcon from './CollectionItemIcon'; import { scrollToTheActiveTab } from 'utils/tabs'; diff --git a/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js b/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js index a1b48843f..a8787a8fb 100644 --- a/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js +++ b/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js @@ -9,13 +9,13 @@ import Modal from 'components/Modal'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import PathDisplay from 'components/PathDisplay/index'; import { useState } from 'react'; -import { IconArrowBackUp } from '@tabler/icons'; +import { IconArrowBackUp, IconEdit } from '@tabler/icons'; import Help from 'components/Help'; const CreateCollection = ({ onClose }) => { const inputRef = useRef(); const dispatch = useDispatch(); - const [isEditingFilename, toggleEditingFilename] = useState(false); + const [isEditing, toggleEditing] = useState(false); const formik = useFormik({ enableReinitialize: true, @@ -32,7 +32,7 @@ const CreateCollection = ({ onClose }) => { collectionFolderName: Yup.string() .min(1, 'must be at least 1 character') .max(255, 'must be 255 characters or less') - .test('is-valid-dir-name', function(value) { + .test('is-valid-collection-name', function(value) { const isValid = validateName(value); return isValid ? true : this.createError({ message: validateNameError(value) }); }) @@ -86,7 +86,7 @@ const CreateCollection = ({ onClose }) => { className="block textbox mt-2 w-full" onChange={(e) => { formik.handleChange(e); - !isEditingFilename && formik.setFieldValue('collectionFolderName', sanitizeName(e.target.value)); + !isEditing && formik.setFieldValue('collectionFolderName', sanitizeName(e.target.value)); }} autoComplete="off" autoCorrect="off" @@ -130,41 +130,51 @@ const CreateCollection = ({ onClose }) => { Browse
- {isEditingFilename ? - <> -
-
- - toggleEditingFilename(false)} - /> -
- +
+ + toggleEditing(false)} />
- - : - + +
+ : +
+
+ + toggleEditing(true)} + /> +
+
+ +
+
} {formik.touched.collectionFolderName && formik.errors.collectionFolderName ? (
{formik.errors.collectionFolderName}
diff --git a/packages/bruno-app/src/components/Sidebar/NewFolder/index.js b/packages/bruno-app/src/components/Sidebar/NewFolder/index.js index 3f6c4ccf4..b703beba0 100644 --- a/packages/bruno-app/src/components/Sidebar/NewFolder/index.js +++ b/packages/bruno-app/src/components/Sidebar/NewFolder/index.js @@ -5,14 +5,15 @@ import * as Yup from 'yup'; import Modal from 'components/Modal'; import { useDispatch } from 'react-redux'; import { newFolder } from 'providers/ReduxStore/slices/collections/actions'; -import { IconArrowBackUp } from '@tabler/icons'; +import { IconArrowBackUp, IconEdit} from '@tabler/icons'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; -import PathDisplay from 'components/PathDisplay'; +import PathDisplay from 'components/PathDisplay/index'; +import path from "utils/common/path"; const NewFolder = ({ collection, item, onClose }) => { const dispatch = useDispatch(); const inputRef = useRef(); - const [isEditingFilename, toggleEditingFilename] = useState(false); + const [isEditing, toggleEditing] = useState(false); const formik = useFormik({ enableReinitialize: true, @@ -65,7 +66,7 @@ const NewFolder = ({ collection, item, onClose }) => {
{ spellCheck="false" onChange={e => { formik.setFieldValue('folderName', e.target.value); - !isEditingFilename && formik.setFieldValue('directoryName', sanitizeName(e.target.value)); + !isEditing && formik.setFieldValue('directoryName', sanitizeName(e.target.value)); }} value={formik.values.folderName || ''} /> @@ -88,17 +89,17 @@ const NewFolder = ({ collection, item, onClose }) => { ) : null}
- {isEditingFilename ? ( + {isEditing ? (
toggleEditingFilename(false)} + onClick={() => toggleEditing(false)} />
@@ -118,14 +119,25 @@ const NewFolder = ({ collection, item, onClose }) => {
) : ( - +
+
+ + toggleEditing(true)} + /> +
+
+ +
+
)} {formik.touched.directoryName && formik.errors.directoryName ? (
{formik.errors.directoryName}
diff --git a/packages/bruno-app/src/components/Sidebar/NewRequest/index.js b/packages/bruno-app/src/components/Sidebar/NewRequest/index.js index 2f201f469..6484f21ff 100644 --- a/packages/bruno-app/src/components/Sidebar/NewRequest/index.js +++ b/packages/bruno-app/src/components/Sidebar/NewRequest/index.js @@ -2,6 +2,7 @@ import React, { useRef, useEffect, useCallback, forwardRef, useState } from 'rea import { useFormik } from 'formik'; import * as Yup from 'yup'; import toast from 'react-hot-toast'; +import path from 'utils/common/path'; import { uuid } from 'utils/common'; import Modal from 'components/Modal'; import { useDispatch } from 'react-redux'; @@ -11,7 +12,7 @@ import { addTab } from 'providers/ReduxStore/slices/tabs'; import HttpMethodSelector from 'components/RequestPane/QueryUrl/HttpMethodSelector'; import { getDefaultRequestPaneTab } from 'utils/collections'; import { getRequestFromCurlCommand } from 'utils/curl'; -import { IconArrowBackUp, IconCaretDown } from '@tabler/icons'; +import { IconArrowBackUp, IconCaretDown, IconEdit } from '@tabler/icons'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import Dropdown from 'components/Dropdown'; import PathDisplay from 'components/PathDisplay'; @@ -57,7 +58,7 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => { setCurlRequestTypeDetected(type); }; - const [isEditingFilename, toggleEditingFilename] = useState(false); + const [isEditing, toggleEditing] = useState(false); const getRequestType = (collectionPresets) => { if (!collectionPresets || !collectionPresets.requestType) { @@ -309,7 +310,7 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => { spellCheck="false" onChange={e => { formik.setFieldValue('requestName', e.target.value); - !isEditingFilename && formik.setFieldValue('filename', sanitizeName(e.target.value)); + !isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value)); }} value={formik.values.requestName || ''} /> @@ -317,7 +318,7 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
{formik.errors.requestName}
) : null}
- {isEditingFilename ? ( + {isEditing ? (
@@ -348,13 +349,25 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
) : ( - +
+
+ + toggleEditing(true)} + /> +
+
+ +
+
)} {formik.touched.filename && formik.errors.filename ? (
{formik.errors.filename}
diff --git a/packages/bruno-app/src/utils/common/index.js b/packages/bruno-app/src/utils/common/index.js index 518b1908b..14fd8805a 100644 --- a/packages/bruno-app/src/utils/common/index.js +++ b/packages/bruno-app/src/utils/common/index.js @@ -187,6 +187,6 @@ export const stringifyIfNot = v => typeof v === 'string' ? v : String(v); export const getEncoding = (headers) => { // Parse the charset from content type: https://stackoverflow.com/a/33192813 - const charsetMatch = /charset=([^()<>@,;:"/[\]?.=\s]*)/i.exec(headers['content-type'] || ''); + const charsetMatch = /charset=([^()<>@,;:"/[\]?.=\s]*)/i.exec(headers?.['content-type'] || ''); return charsetMatch?.[1]; } diff --git a/packages/bruno-app/src/utils/common/regex.js b/packages/bruno-app/src/utils/common/regex.js index 9338288f0..6bf5d7b4d 100644 --- a/packages/bruno-app/src/utils/common/regex.js +++ b/packages/bruno-app/src/utils/common/regex.js @@ -2,7 +2,7 @@ const invalidCharacters = /[<>:"/\\|?*\x00-\x1F]/g; // replace invalid character const reservedDeviceNames = /^(CON|PRN|AUX|NUL|COM[0-9]|LPT[0-9])$/i; const firstCharacter = /^[^.\s\-\<>:"/\\|?*\x00-\x1F]/; // no dot, space, or hyphen at start const middleCharacters = /^[^<>:"/\\|?*\x00-\x1F]*$/; // no invalid characters -const lastCharacter = /[^.\s]$/; // no dot or space at end, hyphen allowed +const lastCharacter = /^[^.\s\-\<>:"/\\|?*\x00-\x1F]/; // no dot or space at end, hyphen allowed export const variableNameRegex = /^[\w-.]*$/; @@ -29,6 +29,7 @@ export const validateName = (name) => { export const validateNameError = (name) => { if (!name) return "Name cannot be empty."; + if (name.length > 255) { return "Name cannot exceed 255 characters."; } diff --git a/packages/bruno-app/src/utils/curl/curl-to-json.js b/packages/bruno-app/src/utils/curl/curl-to-json.js index 7683b5cda..ea0ec2a05 100644 --- a/packages/bruno-app/src/utils/curl/curl-to-json.js +++ b/packages/bruno-app/src/utils/curl/curl-to-json.js @@ -9,7 +9,6 @@ import parseCurlCommand from './parse-curl'; import * as querystring from 'query-string'; import * as jsesc from 'jsesc'; -import * as path from 'path'; function getContentType(headers = {}) { const contentType = Object.keys(headers).find((key) => key.toLowerCase() === 'content-type'); From 3808089e608c479ce121c2f2e4ea480bbf15a8d1 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Mon, 17 Mar 2025 02:52:03 +0530 Subject: [PATCH 02/10] feat: added helptips for file and folder custom names --- .../components/PathDisplay/StyledWrapper.js | 2 +- .../src/components/PathDisplay/index.js | 9 +- .../Collection/CloneCollection/index.js | 68 +++++++----- .../CloneCollectionItem/index.js | 105 ++++++++++-------- .../RenameCollectionItem/index.js | 73 +++++++----- .../Sidebar/CreateCollection/index.js | 101 +++++++++-------- .../src/components/Sidebar/NewFolder/index.js | 61 +++++----- .../components/Sidebar/NewRequest/index.js | 58 +++++----- 8 files changed, 276 insertions(+), 201 deletions(-) diff --git a/packages/bruno-app/src/components/PathDisplay/StyledWrapper.js b/packages/bruno-app/src/components/PathDisplay/StyledWrapper.js index 1b6ace094..f5e174b78 100644 --- a/packages/bruno-app/src/components/PathDisplay/StyledWrapper.js +++ b/packages/bruno-app/src/components/PathDisplay/StyledWrapper.js @@ -31,7 +31,7 @@ const StyledWrapper = styled.div` .separator { color: ${(props) => props.theme.text}; opacity: 0.6; - margin: 0 1px; + margin: 0 2px; } } `; diff --git a/packages/bruno-app/src/components/PathDisplay/index.js b/packages/bruno-app/src/components/PathDisplay/index.js index b867abb7b..5e4add37d 100644 --- a/packages/bruno-app/src/components/PathDisplay/index.js +++ b/packages/bruno-app/src/components/PathDisplay/index.js @@ -4,6 +4,7 @@ import path from 'utils/common/path'; import StyledWrapper from './StyledWrapper'; const PathDisplay = ({ + collection, dirName = '', baseName = '' }) => { @@ -14,10 +15,16 @@ const PathDisplay = ({ return (
-
+
{hasExtension ? : }
+ {collection?.name && ( +
+ {collection.name} + / +
+ )} {pathSegments?.map((segment, index) => (
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 35235df94..ab9fc1a7f 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 @@ -7,7 +7,8 @@ import { cloneCollection } from 'providers/ReduxStore/slices/collections/actions import toast from 'react-hot-toast'; import Modal from 'components/Modal'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; -import PathDisplay from 'components/PathDisplay/index'; +import Help from 'components/Help'; +import PathDisplay from 'components/PathDisplay'; import { useState } from 'react'; import { IconArrowBackUp, IconEdit } from "@tabler/icons"; @@ -125,23 +126,46 @@ const CloneCollection = ({ onClose, collection }) => {
{formik.errors.collectionLocation}
) : null}
- + Browse
- {isEditing ? -
-
- + +
+
+ + {isEditing ? ( toggleEditing(false)} /> -
+ ) : ( + toggleEditing(true)} + /> + )} +
+ {isEditing ? ( { spellCheck="false" value={formik.values.collectionFolderName || ''} /> -
- : -
-
- - toggleEditing(true)} - /> -
+ ) : (
-
- } - {formik.touched.collectionFolderName && formik.errors.collectionFolderName ? ( -
{formik.errors.collectionFolderName}
- ) : null} + )} + + {formik.touched.collectionFolderName && formik.errors.collectionFolderName ? ( +
{formik.errors.collectionFolderName}
+ ) : null} +
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js index db9b0e895..296051a97 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js @@ -8,6 +8,7 @@ import { isItemAFolder } from 'utils/tabs'; import { cloneItem } from 'providers/ReduxStore/slices/collections/actions'; import { IconArrowBackUp, IconEdit } from "@tabler/icons"; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; +import Help from 'components/Help'; import PathDisplay from 'components/PathDisplay/index'; import path from 'utils/common/path'; @@ -91,60 +92,76 @@ const CloneCollectionItem = ({ collection, item, onClose }) => { /> {formik.touched.name && formik.errors.name ?
{formik.errors.name}
: null}
- {isEditing ? ( -
-
- - toggleEditing(false)} - /> -
-
- - {itemType !== 'folder' && .bru} -
-
- ) : ( -
-
- - +
+ + {isEditing ? ( + toggleEditing(false)} + /> + ) : ( + toggleEditing(true)} /> -
-
- -
+ )} +
+ {isEditing ? ( +
+ + {itemType !== 'folder' && .bru} +
+ ) : ( +
+
)} {formik.touched.filename && formik.errors.filename ? (
{formik.errors.filename}
) : null} +
); diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js index 50c3dee22..619778755 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js @@ -10,6 +10,7 @@ import { IconArrowBackUp, IconEdit } from '@tabler/icons'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import toast from 'react-hot-toast'; import { closeTabs } from 'providers/ReduxStore/slices/tabs'; +import Help from 'components/Help'; import PathDisplay from 'components/PathDisplay'; const RenameCollectionItem = ({ collection, item, onClose }) => { @@ -110,26 +111,54 @@ const RenameCollectionItem = ({ collection, item, onClose }) => { /> {formik.touched.name && formik.errors.name ?
{formik.errors.name}
: null}
- - {isEditing ? ( -
-
- + +
+
+ + {isEditing ? ( toggleEditing(false)} /> -
+ ) : ( + toggleEditing(true)} + /> + )} +
+ {isEditing ? (
{ /> {itemType !== 'folder' && .bru}
-
- ) : ( -
-
- - toggleEditing(true)} - /> -
+ ) : (
-
- )} - {formik.touched.filename && formik.errors.filename ? ( -
{formik.errors.filename}
- ) : null} + )} + {formik.touched.filename && formik.errors.filename ? ( +
{formik.errors.filename}
+ ) : null} +
); diff --git a/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js b/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js index a8787a8fb..07f390901 100644 --- a/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js +++ b/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js @@ -105,7 +105,7 @@ const CreateCollection = ({ onClose }) => { Bruno stores your collections on your computer's filesystem.

- Choose where you want to store this collection. + Choose the location where you want to store this collection.

@@ -126,59 +126,70 @@ const CreateCollection = ({ onClose }) => {
{formik.errors.collectionLocation}
) : null}
- + Browse
- {isEditing ? + {formik.values.collectionName?.trim()?.length > 0 && (
-
- + {isEditing ? ( + + ) : ( +
+ +
+ )} + {formik.touched.collectionFolderName && formik.errors.collectionFolderName ? ( +
{formik.errors.collectionFolderName}
+ ) : null}
- : -
-
- - toggleEditing(true)} - /> -
-
- -
-
- } - {formik.touched.collectionFolderName && formik.errors.collectionFolderName ? ( -
{formik.errors.collectionFolderName}
- ) : null} + )}
diff --git a/packages/bruno-app/src/components/Sidebar/NewFolder/index.js b/packages/bruno-app/src/components/Sidebar/NewFolder/index.js index b703beba0..7989f806f 100644 --- a/packages/bruno-app/src/components/Sidebar/NewFolder/index.js +++ b/packages/bruno-app/src/components/Sidebar/NewFolder/index.js @@ -9,7 +9,7 @@ import { IconArrowBackUp, IconEdit} from '@tabler/icons'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import PathDisplay from 'components/PathDisplay/index'; import path from "utils/common/path"; - +import Help from 'components/Help'; const NewFolder = ({ collection, item, onClose }) => { const dispatch = useDispatch(); const inputRef = useRef(); @@ -88,20 +88,37 @@ const NewFolder = ({ collection, item, onClose }) => {
{formik.errors.folderName}
) : null} - - {isEditing ? ( -
-
- + +
+
+ + {isEditing ? ( toggleEditing(false)} /> -
+ ): ( + toggleEditing(true)} + /> + )} +
+ {isEditing ? (
{ value={formik.values.directoryName || ''} />
-
- ) : ( -
-
- - toggleEditing(true)} - /> -
+ ) : (
-
- )} - {formik.touched.directoryName && formik.errors.directoryName ? ( -
{formik.errors.directoryName}
- ) : null} + )} + {formik.touched.directoryName && formik.errors.directoryName ? ( +
{formik.errors.directoryName}
+ ) : null} +
); diff --git a/packages/bruno-app/src/components/Sidebar/NewRequest/index.js b/packages/bruno-app/src/components/Sidebar/NewRequest/index.js index 6484f21ff..9e8f32beb 100644 --- a/packages/bruno-app/src/components/Sidebar/NewRequest/index.js +++ b/packages/bruno-app/src/components/Sidebar/NewRequest/index.js @@ -16,6 +16,7 @@ import { IconArrowBackUp, IconCaretDown, IconEdit } from '@tabler/icons'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import Dropdown from 'components/Dropdown'; import PathDisplay from 'components/PathDisplay'; +import Help from 'components/Help'; import StyledWrapper from './StyledWrapper'; const NewRequest = ({ collection, item, isEphemeral, onClose }) => { @@ -318,19 +319,36 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
{formik.errors.requestName}
) : null} - {isEditing ? ( -
-
- +
+
+ + {isEditing ? ( toggleEditing(false)} /> -
+ ) : ( + toggleEditing(true)} + /> + )} +
+ {isEditing ? (
{ /> .bru
-
- ) : ( -
-
- - toggleEditing(true)} - /> -
+ ) : (
-
- )} - {formik.touched.filename && formik.errors.filename ? ( -
{formik.errors.filename}
- ) : null} + )} + {formik.touched.filename && formik.errors.filename ? ( +
{formik.errors.filename}
+ ) : null} +
{formik.values.requestType !== 'from-curl' ? ( <>
From 74d9b0aafe94513197cec9078955129f36accb2d Mon Sep 17 00:00:00 2001 From: lohxt1 Date: Mon, 17 Mar 2025 16:55:56 +0530 Subject: [PATCH 03/10] fix browse_files function --- packages/bruno-electron/src/utils/filesystem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bruno-electron/src/utils/filesystem.js b/packages/bruno-electron/src/utils/filesystem.js index aaff867b1..a95c116eb 100644 --- a/packages/bruno-electron/src/utils/filesystem.js +++ b/packages/bruno-electron/src/utils/filesystem.js @@ -131,7 +131,7 @@ const browseFiles = async (win, filters = [], properties = []) => { return []; } - return filePaths.map((path) => path.resolve(path)).filter((path) => isFile(path)); + return filePaths.map((filePath) => path.resolve(filePath)).filter((filePath) => isFile(filePath)); }; const chooseFileToSave = async (win, preferredFileName = '') => { From ab9befd7731b88c16eaa201a8f4e16d4cf1ed7a3 Mon Sep 17 00:00:00 2001 From: Ed Brannin Date: Fri, 1 Nov 2024 09:45:44 -0400 Subject: [PATCH 04/10] UI: Change the default request auth mode from "none" to "inherit" Fix #2315 --- .../src/providers/ReduxStore/slices/collections/actions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a693604fa..aef423e04 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -799,7 +799,7 @@ export const newHttpRequest = (params) => (dispatch, getState) => { file: null }, auth: auth ?? { - mode: 'none' + mode: 'inherit' } } }; From 1be0e8d31c051076c1e530148c22d1969eed99df Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Tue, 18 Mar 2025 00:42:43 +0530 Subject: [PATCH 05/10] feat: custom filename can be shown by clicking advanced options --- .../components/PathDisplay/StyledWrapper.js | 2 +- .../src/components/PathDisplay/index.js | 27 +- .../CloneCollectionItem/StyledWrapper.js | 12 + .../CloneCollectionItem/index.js | 249 ++++++---- .../CollectionItemInfo/index.js | 2 +- .../RenameCollectionItem/StyledWrapper.js | 12 + .../RenameCollectionItem/index.js | 247 ++++++---- .../Sidebar/NewFolder/StyledWrapper.js | 12 + .../src/components/Sidebar/NewFolder/index.js | 223 +++++---- .../components/Sidebar/NewRequest/index.js | 446 ++++++++++-------- 10 files changed, 731 insertions(+), 501 deletions(-) create mode 100644 packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/StyledWrapper.js create mode 100644 packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/StyledWrapper.js create mode 100644 packages/bruno-app/src/components/Sidebar/NewFolder/StyledWrapper.js diff --git a/packages/bruno-app/src/components/PathDisplay/StyledWrapper.js b/packages/bruno-app/src/components/PathDisplay/StyledWrapper.js index f5e174b78..326e9f8da 100644 --- a/packages/bruno-app/src/components/PathDisplay/StyledWrapper.js +++ b/packages/bruno-app/src/components/PathDisplay/StyledWrapper.js @@ -24,7 +24,7 @@ const StyledWrapper = styled.div` } - .filename, .file-extension { + .name-container, .file-extension { color: ${(props) => props.theme.colors.text.yellow}; } diff --git a/packages/bruno-app/src/components/PathDisplay/index.js b/packages/bruno-app/src/components/PathDisplay/index.js index 5e4add37d..7413d5748 100644 --- a/packages/bruno-app/src/components/PathDisplay/index.js +++ b/packages/bruno-app/src/components/PathDisplay/index.js @@ -4,36 +4,17 @@ import path from 'utils/common/path'; import StyledWrapper from './StyledWrapper'; const PathDisplay = ({ - collection, - dirName = '', - baseName = '' + baseName = '', + iconType = 'file' }) => { - const extName = path.extname(baseName) - const hasExtension = Boolean(extName); - const pathSegments = dirName?.split(path.sep).filter(Boolean); - return (
- {hasExtension ? : } + {iconType === 'file' ? : }
- {collection?.name && ( -
- {collection.name} - / -
- )} - {pathSegments?.map((segment, index) => ( - -
- {segment} -
- / -
- ))} - + {baseName}
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/StyledWrapper.js new file mode 100644 index 000000000..d46e186d2 --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/StyledWrapper.js @@ -0,0 +1,12 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + .advanced-options { + .caret { + color: ${(props) => props.theme.textLink}; + fill: ${(props) => props.theme.textLink}; + } + } +`; + +export default StyledWrapper; \ No newline at end of file diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js index 296051a97..6edaf33b9 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js @@ -1,4 +1,4 @@ -import React, { useState, useRef, useEffect } from 'react'; +import React, { useState, useRef, useEffect, forwardRef } from 'react'; import toast from 'react-hot-toast'; import { useFormik } from 'formik'; import * as Yup from 'yup'; @@ -6,11 +6,14 @@ import Modal from 'components/Modal'; import { useDispatch } from 'react-redux'; import { isItemAFolder } from 'utils/tabs'; import { cloneItem } from 'providers/ReduxStore/slices/collections/actions'; -import { IconArrowBackUp, IconEdit } from "@tabler/icons"; +import { IconArrowBackUp, IconEdit, IconCaretDown } from "@tabler/icons"; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import Help from 'components/Help'; import PathDisplay from 'components/PathDisplay/index'; import path from 'utils/common/path'; +import Portal from 'components/Portal'; +import Dropdown from 'components/Dropdown'; +import StyledWrapper from './StyledWrapper'; const CloneCollectionItem = ({ collection, item, onClose }) => { const dispatch = useDispatch(); @@ -19,6 +22,11 @@ const CloneCollectionItem = ({ collection, item, onClose }) => { const [isEditing, toggleEditing] = useState(false); const itemName = item?.name; const itemType = item?.type; + const [showFilesystemName, toggleShowFilesystemName] = useState(false); + + const dropdownTippyRef = useRef(); + const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); + const formik = useFormik({ enableReinitialize: true, initialValues: { @@ -58,112 +66,159 @@ const CloneCollectionItem = ({ collection, item, onClose }) => { } }, [inputRef]); - const onSubmit = () => formik.handleSubmit(); + const AdvancedOptions = forwardRef((props, ref) => { + return ( +
+ + +
+ ); + }); return ( - -
e.preventDefault()}> -
- - { - formik.setFieldValue('name', e.target.value); - !isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value)); - }} - value={formik.values.name || ''} - /> - {formik.touched.name && formik.errors.name ?
{formik.errors.name}
: null} -
-
-
- - {isEditing ? ( - toggleEditing(false)} - /> - ) : ( - toggleEditing(true)} - /> - )} -
- {isEditing ? ( -
+ + + + +
+ { + formik.setFieldValue('name', e.target.value); + !isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value)); + }} + value={formik.values.name || ''} /> - {itemType !== 'folder' && .bru} + {formik.touched.name && formik.errors.name ?
{formik.errors.name}
: null}
- ) : ( -
- + + {showFilesystemName && ( +
+
+ + {isEditing ? ( + toggleEditing(false)} + /> + ) : ( + toggleEditing(true)} + /> + )} +
+ {isEditing ? ( +
+ + {itemType !== 'folder' && .bru} +
+ ) : ( +
+ +
+ )} + {formik.touched.filename && formik.errors.filename ? ( +
{formik.errors.filename}
+ ) : null} +
+ )} + +
+
+ } placement="bottom-start"> +
{ + dropdownTippyRef.current.hide(); + toggleShowFilesystemName(!showFilesystemName); + }} + > + {showFilesystemName ? 'Hide Filesystem Name' : 'Show Filesystem Name'} +
+
+
+
+ + + + + + +
- )} - {formik.touched.filename && formik.errors.filename ? ( -
{formik.errors.filename}
- ) : null} -
- -
+ + +
+
); }; diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js index 22d546326..d2aef1e21 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js @@ -22,7 +22,7 @@ const CollectionItemInfo = ({ collection, item, onClose }) => { {name} - {type=='folder' ? 'Directory Name' : 'File Name'} : + {type=='folder' ? 'Folder Name' : 'File Name'} : {filename} diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/StyledWrapper.js new file mode 100644 index 000000000..d46e186d2 --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/StyledWrapper.js @@ -0,0 +1,12 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + .advanced-options { + .caret { + color: ${(props) => props.theme.textLink}; + fill: ${(props) => props.theme.textLink}; + } + } +`; + +export default StyledWrapper; \ No newline at end of file diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js index 619778755..0b0350017 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js @@ -1,4 +1,4 @@ -import React, { useRef, useEffect, useState } from 'react'; +import React, { useRef, useEffect, useState, forwardRef } from 'react'; import { useFormik } from 'formik'; import * as Yup from 'yup'; import Modal from 'components/Modal'; @@ -6,12 +6,15 @@ import { useDispatch } from 'react-redux'; import { isItemAFolder } from 'utils/tabs'; import { renameItem, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import path from 'utils/common/path'; -import { IconArrowBackUp, IconEdit } from '@tabler/icons'; +import { IconArrowBackUp, IconEdit, IconCaretDown } from '@tabler/icons'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import toast from 'react-hot-toast'; import { closeTabs } from 'providers/ReduxStore/slices/tabs'; import Help from 'components/Help'; import PathDisplay from 'components/PathDisplay'; +import Portal from 'components/Portal'; +import Dropdown from 'components/Dropdown'; +import StyledWrapper from './StyledWrapper'; const RenameCollectionItem = ({ collection, item, onClose }) => { const dispatch = useDispatch(); @@ -21,6 +24,11 @@ const RenameCollectionItem = ({ collection, item, onClose }) => { const itemName = item?.name; const itemType = item?.type; const itemFilename = item?.filename ? path.parse(item?.filename).name : ''; + const [showFilesystemName, toggleShowFilesystemName] = useState(false); + + const dropdownTippyRef = useRef(); + const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); + const formik = useFormik({ enableReinitialize: true, initialValues: { @@ -78,112 +86,157 @@ const RenameCollectionItem = ({ collection, item, onClose }) => { } }, [inputRef]); - const onSubmit = () => formik.handleSubmit(); + const AdvancedOptions = forwardRef((props, ref) => { + return ( +
+ + +
+ ); + }); return ( - -
{e.preventDefault()}}> -
- - { - formik.setFieldValue('name', e.target.value); - !isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value)); - }} - value={formik.values.name || ''} - /> - {formik.touched.name && formik.errors.name ?
{formik.errors.name}
: null} -
- -
-
- - {isEditing ? ( - toggleEditing(false)} - /> - ) : ( - toggleEditing(true)} - /> - )} -
- {isEditing ? ( -
+ + + + +
+ { + formik.setFieldValue('name', e.target.value); + !isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value)); + }} + value={formik.values.name || ''} /> - {itemType !== 'folder' && .bru} + {formik.touched.name && formik.errors.name ?
{formik.errors.name}
: null}
- ) : ( -
- + + {showFilesystemName && ( +
+
+ + {isEditing ? ( + toggleEditing(false)} + /> + ) : ( + toggleEditing(true)} + /> + )} +
+ {isEditing ? ( +
+ + {itemType !== 'folder' && .bru} +
+ ) : ( +
+ +
+ )} + {formik.touched.filename && formik.errors.filename ? ( +
{formik.errors.filename}
+ ) : null} +
+ )} +
+
+ } placement="bottom-start"> +
{ + dropdownTippyRef.current.hide(); + toggleShowFilesystemName(!showFilesystemName); + }} + > + {showFilesystemName ? 'Hide Filesystem Name' : 'Show Filesystem Name'} +
+
+
+
+ + + + + + +
- )} - {formik.touched.filename && formik.errors.filename ? ( -
{formik.errors.filename}
- ) : null} -
- -
+ + +
+
); }; diff --git a/packages/bruno-app/src/components/Sidebar/NewFolder/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/NewFolder/StyledWrapper.js new file mode 100644 index 000000000..d46e186d2 --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/NewFolder/StyledWrapper.js @@ -0,0 +1,12 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + .advanced-options { + .caret { + color: ${(props) => props.theme.textLink}; + fill: ${(props) => props.theme.textLink}; + } + } +`; + +export default StyledWrapper; \ No newline at end of file diff --git a/packages/bruno-app/src/components/Sidebar/NewFolder/index.js b/packages/bruno-app/src/components/Sidebar/NewFolder/index.js index 7989f806f..4ff681a86 100644 --- a/packages/bruno-app/src/components/Sidebar/NewFolder/index.js +++ b/packages/bruno-app/src/components/Sidebar/NewFolder/index.js @@ -1,19 +1,27 @@ -import React, { useRef, useEffect, useState } from 'react'; +import React, { useRef, useEffect, useState, forwardRef } from 'react'; import { useFormik } from 'formik'; import toast from 'react-hot-toast'; import * as Yup from 'yup'; +import Portal from 'components/Portal'; import Modal from 'components/Modal'; import { useDispatch } from 'react-redux'; import { newFolder } from 'providers/ReduxStore/slices/collections/actions'; import { IconArrowBackUp, IconEdit} from '@tabler/icons'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import PathDisplay from 'components/PathDisplay/index'; -import path from "utils/common/path"; import Help from 'components/Help'; +import Dropdown from "components/Dropdown"; +import { IconCaretDown } from "@tabler/icons"; +import StyledWrapper from './StyledWrapper'; + const NewFolder = ({ collection, item, onClose }) => { const dispatch = useDispatch(); const inputRef = useRef(); const [isEditing, toggleEditing] = useState(false); + const [showFilesystemName, toggleShowFilesystemName] = useState(false); + + const dropdownTippyRef = useRef(); + const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); const formik = useFormik({ enableReinitialize: true, @@ -59,96 +67,139 @@ const NewFolder = ({ collection, item, onClose }) => { } }, [inputRef]); - const onSubmit = () => formik.handleSubmit(); + const AdvancedOptions = forwardRef((props, ref) => { + return ( +
+ + +
+ ); + }); return ( - -
-
- - { - formik.setFieldValue('folderName', e.target.value); - !isEditing && formik.setFieldValue('directoryName', sanitizeName(e.target.value)); - }} - value={formik.values.folderName || ''} - /> - {formik.touched.folderName && formik.errors.folderName ? ( -
{formik.errors.folderName}
- ) : null} -
- -
-
-
- {isEditing ? ( -
- +
+
+ } placement="bottom-start"> +
{ + dropdownTippyRef.current.hide(); + toggleShowFilesystemName(!showFilesystemName); + }} + > + {showFilesystemName ? 'Hide Filesystem Name' : 'Show Filesystem Name'} +
+
+
+
+ + + + + + +
- ) : ( -
- -
- )} - {formik.touched.directoryName && formik.errors.directoryName ? ( -
{formik.errors.directoryName}
- ) : null} -
- - + + + + ); }; diff --git a/packages/bruno-app/src/components/Sidebar/NewRequest/index.js b/packages/bruno-app/src/components/Sidebar/NewRequest/index.js index 9e8f32beb..f3ef32cba 100644 --- a/packages/bruno-app/src/components/Sidebar/NewRequest/index.js +++ b/packages/bruno-app/src/components/Sidebar/NewRequest/index.js @@ -16,6 +16,7 @@ import { IconArrowBackUp, IconCaretDown, IconEdit } from '@tabler/icons'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import Dropdown from 'components/Dropdown'; import PathDisplay from 'components/PathDisplay'; +import Portal from 'components/Portal'; import Help from 'components/Help'; import StyledWrapper from './StyledWrapper'; @@ -26,10 +27,14 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => { brunoConfig: { presets: collectionPresets = {} } } = collection; const [curlRequestTypeDetected, setCurlRequestTypeDetected] = useState(null); + const [showFilesystemName, toggleShowFilesystemName] = useState(false); const dropdownTippyRef = useRef(); const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); + const advancedDropdownTippyRef = useRef(); + const onAdvancedDropdownCreate = (ref) => (advancedDropdownTippyRef.current = ref); + const Icon = forwardRef((props, ref) => { return (
@@ -231,230 +236,279 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => { } }; - return ( - - -
{ - if (e.key === 'Enter') { - e.preventDefault(); - formik.handleSubmit(); - } - }} + const AdvancedOptions = forwardRef((props, ref) => { + return ( +
+ + +
+ ); + }); -
- -
-
- - { - formik.setFieldValue('requestName', e.target.value); - !isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value)); - }} - value={formik.values.requestName || ''} - /> - {formik.touched.requestName && formik.errors.requestName ? ( -
{formik.errors.requestName}
- ) : null} -
-
-
- - {isEditing ? ( - toggleEditing(false)} - /> - ) : ( - toggleEditing(true)} - /> - )} -
- {isEditing ? ( -
+
- .bru -
- ) : ( -
- -
- )} - {formik.touched.filename && formik.errors.filename ? ( -
{formik.errors.filename}
- ) : null} -
- {formik.values.requestType !== 'from-curl' ? ( - <> -
-
- - ) : ( -
-
- - } placement="bottom-end"> -
{ - dropdownTippyRef.current.hide(); - curlRequestTypeChange('http-request'); - }} - > - HTTP + )} + {formik.values.requestType !== 'from-curl' ? ( + <> +
+ +
+
+ formik.setFieldValue('requestMethod', val)} + /> +
+
+ +
-
{formik.errors.requestUrl}
+ ) : null} +
+ + ) : ( +
+
+ + } placement="bottom-end"> +
{ + dropdownTippyRef.current.hide(); + curlRequestTypeChange('http-request'); + }} + > + HTTP +
+
{ + dropdownTippyRef.current.hide(); + curlRequestTypeChange('graphql-request'); + }} + > + GraphQL +
+
+
+ + {formik.touched.curlCommand && formik.errors.curlCommand ? ( +
{formik.errors.curlCommand}
+ ) : null} +
+ )} +
+
+ } placement="bottom-start"> +
{ - dropdownTippyRef.current.hide(); - curlRequestTypeChange('graphql-request'); + key="show-filesystem-name" + onClick={(e) => { + advancedDropdownTippyRef.current.hide(); + toggleShowFilesystemName(!showFilesystemName); }} > - GraphQL + {showFilesystemName ? 'Hide Filesystem Name' : 'Show Filesystem Name'}
- - {formik.touched.curlCommand && formik.errors.curlCommand ? ( -
{formik.errors.curlCommand}
- ) : null} +
+ + + + + + +
- )} - - - + + + + ); }; From 1009d42f92d301b5eedd22135dbeca7bdc25b72f Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Tue, 18 Mar 2025 00:48:51 +0530 Subject: [PATCH 06/10] chore: fix caret color --- .../Sidebar/NewRequest/StyledWrapper.js | 92 ++++++++++--------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/packages/bruno-app/src/components/Sidebar/NewRequest/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/NewRequest/StyledWrapper.js index 872ba2877..338fb2e60 100644 --- a/packages/bruno-app/src/components/Sidebar/NewRequest/StyledWrapper.js +++ b/packages/bruno-app/src/components/Sidebar/NewRequest/StyledWrapper.js @@ -1,45 +1,53 @@ import styled from 'styled-components'; - const StyledWrapper = styled.div` - div.method-selector-container { - border: solid 1px ${(props) => props.theme.modal.input.border}; - border-right: none; - background-color: ${(props) => props.theme.modal.input.bg}; - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; - .method-selector { - min-width: 80px; - } - } - div.method-selector-container, - div.input-container { - background-color: ${(props) => props.theme.modal.input.bg}; - height: 2.3rem; - } - div.input-container { - border: solid 1px ${(props) => props.theme.modal.input.border}; - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - input { - background-color: ${(props) => props.theme.modal.input.bg}; - outline: none; - box-shadow: none; - &:focus { - outline: none !important; - box-shadow: none !important; - } - } - } - textarea.curl-command { - min-height: 150px; - } - .dropdown { - width: fit-content; - - .dropdown-item { - padding: 0.2rem 0.6rem !important; - } - } - `; +const StyledWrapper = styled.div` + div.method-selector-container { + border: solid 1px ${(props) => props.theme.modal.input.border}; + border-right: none; + background-color: ${(props) => props.theme.modal.input.bg}; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + .method-selector { + min-width: 80px; + } + } + div.method-selector-container, + div.input-container { + background-color: ${(props) => props.theme.modal.input.bg}; + height: 2.3rem; + } + div.input-container { + border: solid 1px ${(props) => props.theme.modal.input.border}; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + input { + background-color: ${(props) => props.theme.modal.input.bg}; + outline: none; + box-shadow: none; + &:focus { + outline: none !important; + box-shadow: none !important; + } + } + } - export default StyledWrapper; \ No newline at end of file + textarea.curl-command { + min-height: 150px; + } + .dropdown { + width: fit-content; + + .dropdown-item { + padding: 0.2rem 0.6rem !important; + } + } + + .advanced-options { + .caret { + color: ${(props) => props.theme.textLink}; + fill: ${(props) => props.theme.textLink}; + } + } +`; + +export default StyledWrapper; \ No newline at end of file From 039c157f33c18038d1fc6ad6fac5c4aa0e3d20cf Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Tue, 18 Mar 2025 19:46:31 +0530 Subject: [PATCH 07/10] feat: improved item info ux --- .../CloneCollectionItem/index.js | 2 +- .../CollectionItemInfo/index.js | 64 ++++++++++++------- .../RenameCollectionItem/index.js | 2 +- .../src/components/Sidebar/NewFolder/index.js | 2 +- .../components/Sidebar/NewRequest/index.js | 2 +- 5 files changed, 45 insertions(+), 27 deletions(-) diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js index 6edaf33b9..9194e8a64 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CloneCollectionItem/index.js @@ -73,7 +73,7 @@ const CloneCollectionItem = ({ collection, item, onClose }) => { className="btn-advanced" type="button" > - Advanced + Options
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js index d2aef1e21..ca46d0d79 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/CollectionItemInfo/index.js @@ -1,11 +1,10 @@ import React from 'react'; import Modal from 'components/Modal'; -import path from 'utils/common/path'; +import Help from 'components/Help'; + +const CollectionItemInfo = ({ item, onClose }) => { + const { name, filename, type } = item; -const CollectionItemInfo = ({ collection, item, onClose }) => { - const { pathname: collectionPathname } = collection; - const { name, filename, pathname, type } = item; - const relativePathname = path.relative(collectionPathname, pathname); return ( { hideCancel={true} hideFooter={true} > -
- - - - - - - - - - - - - - - -
Name :{name}
{type=='folder' ? 'Folder Name' : 'File Name'} :{filename}
Pathname :{relativePathname}
-
+
+ + + + + + + + + + + +
+ {type=='folder' ? 'Folder Name' : 'Request Name'} + + :{name} +
+ {type == 'folder' ? 'Folder Name' : 'File Name'} + (on filesystem) + {type == 'folder' ? ( + +

+ The name of the folder on your filesystem. +

+
+ ) : ( + +

+ Bruno saves each request as a file in your collection's folder. +

+
+ )} +
+ : + {filename} +
+
); }; diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js index 0b0350017..705c45c79 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/RenameCollectionItem/index.js @@ -93,7 +93,7 @@ const RenameCollectionItem = ({ collection, item, onClose }) => { className="btn-advanced" type="button" > - Advanced + Options
diff --git a/packages/bruno-app/src/components/Sidebar/NewFolder/index.js b/packages/bruno-app/src/components/Sidebar/NewFolder/index.js index 4ff681a86..c0b39b727 100644 --- a/packages/bruno-app/src/components/Sidebar/NewFolder/index.js +++ b/packages/bruno-app/src/components/Sidebar/NewFolder/index.js @@ -74,7 +74,7 @@ const NewFolder = ({ collection, item, onClose }) => { className="btn-advanced" type="button" > - Advanced + Options
diff --git a/packages/bruno-app/src/components/Sidebar/NewRequest/index.js b/packages/bruno-app/src/components/Sidebar/NewRequest/index.js index f3ef32cba..61fdcd22a 100644 --- a/packages/bruno-app/src/components/Sidebar/NewRequest/index.js +++ b/packages/bruno-app/src/components/Sidebar/NewRequest/index.js @@ -243,7 +243,7 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => { className="btn-advanced" type="button" > - Advanced + Options
From a7cf24278eed5f39487adb7b292e908797e9995b Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Tue, 18 Mar 2025 21:15:13 +0530 Subject: [PATCH 08/10] style: update font-family for improved typography consistency --- packages/bruno-app/src/styles/globals.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bruno-app/src/styles/globals.css b/packages/bruno-app/src/styles/globals.css index f81023479..ef2de6bbe 100644 --- a/packages/bruno-app/src/styles/globals.css +++ b/packages/bruno-app/src/styles/globals.css @@ -145,7 +145,7 @@ body { font-kerning: none; text-rendering: optimizeSpeed; letter-spacing: normal; - font-family: Inter, sans-serif !important; + font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important; overflow-x: hidden; } From 98bd99766561217987aa2f250d8bab31d6ec1836 Mon Sep 17 00:00:00 2001 From: lohxt1 Date: Tue, 18 Mar 2025 21:18:41 +0530 Subject: [PATCH 09/10] chore: fix font not loading issue, fix about menu item, fix padding for preferences modal --- .../src/components/Preferences/index.js | 2 +- .../bruno-electron/src/app/menu-template.js | 201 +++++++++++++++++- packages/bruno-electron/src/index.js | 2 +- scripts/build-electron.sh | 2 + 4 files changed, 196 insertions(+), 11 deletions(-) diff --git a/packages/bruno-app/src/components/Preferences/index.js b/packages/bruno-app/src/components/Preferences/index.js index 3635ca5a9..2319d4c78 100644 --- a/packages/bruno-app/src/components/Preferences/index.js +++ b/packages/bruno-app/src/components/Preferences/index.js @@ -46,7 +46,7 @@ const Preferences = ({ onClose }) => { return ( -
+
setTab('general')}> General diff --git a/packages/bruno-electron/src/app/menu-template.js b/packages/bruno-electron/src/app/menu-template.js index e662336ae..a25feaf57 100644 --- a/packages/bruno-electron/src/app/menu-template.js +++ b/packages/bruno-electron/src/app/menu-template.js @@ -1,7 +1,188 @@ const { ipcMain } = require('electron'); const os = require('os'); -const openAboutWindow = require('about-window').default; const { join } = require('path'); +const { BrowserWindow } = require('electron'); +const { version } = require('../../package.json'); + +const htmlContent = ` + + + + + + About Bruno + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bruno ${version}

+

Opensource API Client for Exploring and Testing APIs

+ +
+ All rights reserved ©2025 Bruno Software Inc +
+ + +`; + const template = [ { @@ -77,14 +258,16 @@ const template = [ submenu: [ { label: 'About Bruno', - click: () => - openAboutWindow({ - product_name: 'Bruno', - icon_path: join(__dirname, '../about/256x256.png'), - css_path: join(__dirname, '../about/about.css'), - homepage: 'https://www.usebruno.com/', - package_json_dir: join(__dirname, '../..') - }) + click: () => { + const aboutWindow = new BrowserWindow({ + width: 500, + height: 400, + webPreferences: { + nodeIntegration: true, + }, + }); + aboutWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`); + } }, { label: 'Documentation', click: () => ipcMain.emit('main:open-docs') } ] diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js index 522df6c68..6ef906ec2 100644 --- a/packages/bruno-electron/src/index.js +++ b/packages/bruno-electron/src/index.js @@ -31,7 +31,7 @@ const lastOpenedCollections = new LastOpenedCollections(); const contentSecurityPolicy = [ "default-src 'self'", "connect-src 'self' https://*.posthog.com", - "font-src 'self' https:", + "font-src 'self' https: data:;", "frame-src data:", // this has been commented out to make oauth2 work // "form-action 'none'", diff --git a/scripts/build-electron.sh b/scripts/build-electron.sh index 42c19a2d8..8ca74608c 100755 --- a/scripts/build-electron.sh +++ b/scripts/build-electron.sh @@ -16,6 +16,8 @@ cp -r packages/bruno-app/dist/* packages/bruno-electron/web # Change paths in next sed -i'' -e 's@/static/@static/@g' packages/bruno-electron/web/**.html +sed -i'' -e 's@/static/font@../../static/font@g' packages/bruno-electron/web/static/css/**.**.css + # Remove sourcemaps find packages/bruno-electron/web -name '*.map' -type f -delete From ccd4a14da60f8e4d215ae4057fb9537608d539ab Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Tue, 18 Mar 2025 21:43:28 +0530 Subject: [PATCH 10/10] feat: refactored about menu + added static path updates for win build --- .../bruno-electron/src/app/about-bruno.js | 176 ++++++++++++++++ .../bruno-electron/src/app/menu-template.js | 188 +----------------- scripts/build-electron.js | 19 +- scripts/build-electron.sh | 3 +- 4 files changed, 199 insertions(+), 187 deletions(-) create mode 100644 packages/bruno-electron/src/app/about-bruno.js diff --git a/packages/bruno-electron/src/app/about-bruno.js b/packages/bruno-electron/src/app/about-bruno.js new file mode 100644 index 000000000..484a062be --- /dev/null +++ b/packages/bruno-electron/src/app/about-bruno.js @@ -0,0 +1,176 @@ +module.exports = function aboutBruno({version}) { + return ` + + + + + + About Bruno + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bruno ${version}

+
+ ©2025 Bruno Software Inc +
+ + + `; +}; \ No newline at end of file diff --git a/packages/bruno-electron/src/app/menu-template.js b/packages/bruno-electron/src/app/menu-template.js index a25feaf57..f2b4c82aa 100644 --- a/packages/bruno-electron/src/app/menu-template.js +++ b/packages/bruno-electron/src/app/menu-template.js @@ -1,188 +1,8 @@ const { ipcMain } = require('electron'); const os = require('os'); -const { join } = require('path'); const { BrowserWindow } = require('electron'); const { version } = require('../../package.json'); - -const htmlContent = ` - - - - - - About Bruno - - - - - - - - - - - - - - - - - - - - - - - - - - -

Bruno ${version}

-

Opensource API Client for Exploring and Testing APIs

- - - - -`; - +const aboutBruno = require('./about-bruno'); const template = [ { @@ -260,13 +80,13 @@ const template = [ label: 'About Bruno', click: () => { const aboutWindow = new BrowserWindow({ - width: 500, - height: 400, + width: 350, + height: 250, webPreferences: { nodeIntegration: true, }, }); - aboutWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(htmlContent)}`); + aboutWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(aboutBruno({version}))}`); } }, { label: 'Documentation', click: () => ipcMain.emit('main:open-docs') } diff --git a/scripts/build-electron.js b/scripts/build-electron.js index ab44dcbdf..6d778ffdf 100644 --- a/scripts/build-electron.js +++ b/scripts/build-electron.js @@ -2,6 +2,7 @@ const os = require('os'); const fs = require('fs-extra'); const util = require('util'); const spawn = util.promisify(require('child_process').spawn); +const path = require('path'); async function deleteFileIfExists(filePath) { try { @@ -80,7 +81,7 @@ async function main() { // Copy build await copyFolderIfExists('packages/bruno-app/dist', 'packages/bruno-electron/web'); - // Change paths in next + // Update static paths const files = await fs.readdir('packages/bruno-electron/web'); for (const file of files) { if (file.endsWith('.html')) { @@ -90,6 +91,22 @@ async function main() { } } + // update font load paths + const cssDir = path.join('packages/bruno-electron/web/static/css'); + try { + const cssFiles = await fs.readdir(cssDir); + for (const file of cssFiles) { + if (file.endsWith('.css')) { + const filePath = path.join(cssDir, file); + let content = await fs.readFile(filePath, 'utf8'); + content = content.replace(/\/static\/font/g, '../../static/font'); + await fs.writeFile(filePath, content); + } + } + } catch (error) { + console.error(`Error updating font paths: ${error}`); + } + // Remove sourcemaps await removeSourceMapFiles('packages/bruno-electron/web'); diff --git a/scripts/build-electron.sh b/scripts/build-electron.sh index 8ca74608c..7f3887c70 100755 --- a/scripts/build-electron.sh +++ b/scripts/build-electron.sh @@ -13,9 +13,8 @@ mkdir packages/bruno-electron/web cp -r packages/bruno-app/dist/* packages/bruno-electron/web -# Change paths in next +# Update static paths sed -i'' -e 's@/static/@static/@g' packages/bruno-electron/web/**.html - sed -i'' -e 's@/static/font@../../static/font@g' packages/bruno-electron/web/static/css/**.**.css # Remove sourcemaps