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');