Skip to content

Commit

Permalink
feat: Improve Manoco editor implementation
Browse files Browse the repository at this point in the history
- Better styling for singleline editor
- Multiline singleline editor
- Improved hover

Ref: usebruno#1450
  • Loading branch information
Its-treason committed May 11, 2024
1 parent 1555fa7 commit 86f0e3f
Show file tree
Hide file tree
Showing 11 changed files with 323 additions and 161 deletions.
Original file line number Diff line number Diff line change
@@ -1,40 +1,61 @@
import { Editor, useMonaco } from '@monaco-editor/react';
/**
* This file is part of bruno-app.
* For license information, see the file LICENSE_GPL3 at the root directory of this distribution.
*/
import { Editor, Monaco, useMonaco } from '@monaco-editor/react';
import { useEffect, useRef } from 'react';
import { debounce } from 'lodash';
import { addMonacoCommands, addMonacoSingleLineActions, setMonacoVariables } from 'utils/monaco/monacoUtils';
import { BrunoEditorCallbacks, addMonacoCommands, setMonacoVariables } from 'utils/monaco/monacoUtils';
import { getAllVariables } from 'utils/collections';
import { useTheme } from 'providers/Theme';
import { editor } from 'monaco-editor';

const languages = {
text: 'text',
const languages: Record<string, string> = {
text: 'plaintext',
plaintext: 'plaintext',
graphql: 'graphql',
sparql: 'graphql',
sparql: 'sparql',
'graphql-query': 'graphql',
'application/sparql-query': 'sparql',
'application/ld+json': 'json',
'application/text': 'text',
'application/text': 'plaintext',
'application/xml': 'xml',
'application/javascript': 'typescript',
javascript: 'typescript'
};

export const MonacoEditor = ({
type MonacoProps = {
collection: {
collectionVariables: unknown;
activeEnvironmentUid: string | undefined;
};
fontSize: number;
readOnly: boolean;
value: string;
withVariables: boolean;
mode: string;
height: string | number;

onChange: (newValue: string) => void;
onRun: () => void;
onSave: () => void;
};

export const MonacoEditor: React.FC<MonacoProps> = ({
collection,
font,
fontSize,
mode = 'plaintext',
onChange,
onRun,
onSave,
readOnly,
value,
singleLine,
withVariables = false,
height = '60vh'
}) => {
const monaco = useMonaco();
const { displayedTheme } = useTheme();
const callbackRefs = useRef({});
const callbackRefs = useRef<BrunoEditorCallbacks>({});

useEffect(() => {
// Save the reference to the callback so the callbacks always update
Expand All @@ -47,51 +68,25 @@ export const MonacoEditor = ({
const debounceChanges = debounce((newValue) => {
onChange(newValue);
}, 300);
const handleEditorChange = (value, event) => {
const handleEditorChange = (value: string | undefined) => {
debounceChanges(value);
};

const onMount = (editor, monaco) => {
const onMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => {
addMonacoCommands(monaco, editor, callbackRefs.current);
if (singleLine) {
addMonacoSingleLineActions(editor);
}
};

const allVariables = getAllVariables(collection);
useEffect(() => {
if (allVariables && withVariables) {
const allVariables = getAllVariables(collection);
if (allVariables && withVariables && monaco) {
setMonacoVariables(monaco, allVariables, languages[mode] ?? 'plaintext');
}
}, [allVariables, withVariables, mode]);

const singleLineOptions = singleLine
? {
folding: false,
renderLineHighlight: 'none',
lineNumbers: 'off',
lineDecorationsWidth: 0,
lineNumbersMinChars: 0,
glyphMargin: false,
links: false,
wordWrap: 'off',
overviewRulerLanes: 0,
overviewRulerBorder: false,
hideCursorInOverviewRuler: true,
scrollBeyondLastColumn: 0,
showFoldingControls: 'never',
selectionHighlight: false,
occurrencesHighlight: 'off',
scrollbar: { horizontal: 'hidden', vertical: 'hidden' },
find: { addExtraSpaceOnTop: false, autoFindInSelection: 'never', seedSearchStringFromSelection: false },
minimap: { enabled: false }
}
: {};
}, [collection.collectionVariables, collection.activeEnvironmentUid, withVariables, mode]);

return (
<Editor
options={{
fontSize: font,
fontSize,
readOnly: readOnly,
wordWrap: 'off',
wrappingIndent: 'indent',
Expand All @@ -104,19 +99,15 @@ export const MonacoEditor = ({
vertical: 'hidden',
horizontal: 'hidden'
},
renderLineHighlight: 'none',
...singleLineOptions
renderLineHighlight: 'none'
}}
height={singleLine ? '22px' : height}
className={!singleLine ? 'rounded-md border border-zinc-200 dark:border-zinc-700' : undefined}
height={height}
className={'rounded-xs border border-zinc-200 dark:border-zinc-700'}
theme={displayedTheme === 'dark' ? 'bruno-dark' : 'bruno-light'}
loading={singleLine ? null : undefined}
language={languages[mode] ?? 'plaintext'}
value={value}
onMount={onMount}
onChange={!readOnly ? handleEditorChange : () => {}}
/>
);
};

export default MonacoEditor;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* This file is part of bruno-app.
* For license information, see the file LICENSE_GPL3 at the root directory of this distribution.
*/
.paper {
width: 100%;
height: fit-content; /* Monaco will auto set the height */

padding: var(--mantine-spacing-xs);
border: 1px solid var(--mantine-color-dark-4);
border-radius: var(--mantine-radius-xs);
}
.paperHidden {
width: 100%;
height: fit-content; /* Monaco will auto set the height */

background-color: transparent !important;
}

.editor {
min-height: 22px;

:global {
.monaco-editor {
.lines-content {
padding-left: 0;
}
.view-line {
font-family: var(--mantine-font-family-monospace);
line-height: var(--mantine-line-height-sm);
font-size: var(--mantine-font-size-sm);
font-weight: 400;
}
.scroll-decoration {
display: none;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* This file is part of bruno-app.
* For license information, see the file LICENSE_GPL3 at the root directory of this distribution.
*/
import { Editor, Monaco, useMonaco } from '@monaco-editor/react';
import { useEffect, useRef, useState } from 'react';
import { debounce } from 'lodash';
import {
BrunoEditorCallbacks,
addMonacoCommands,
addMonacoSingleLineActions,
setMonacoVariables
} from 'utils/monaco/monacoUtils';
import { getAllVariables } from 'utils/collections';
import { useTheme } from 'providers/Theme';
import { editor } from 'monaco-editor';
import { Paper } from '@mantine/core';
import classes from './MonacoSingleline.module.scss';

type MonacoSinglelineProps = {
collection: {
collectionVariables: unknown;
activeEnvironmentUid: string | undefined;
};
readOnly: boolean;
value: string;
withVariables: boolean;
allowLinebreaks: boolean;
asInput: boolean;

onChange: (newValue: string) => void;
onRun: () => void;
onSave: () => void;
};

export const MonacoSingleline: React.FC<MonacoSinglelineProps> = ({
collection,
onChange,
onRun,
onSave,
readOnly = false,
value,
withVariables = false,
allowLinebreaks = false,
asInput = false
}) => {
const monaco = useMonaco();
const { displayedTheme } = useTheme();
const callbackRefs = useRef<BrunoEditorCallbacks>({});
const [height, setHeight] = useState(22);

useEffect(() => {
// Save the reference to the callback so the callbacks always update
// OnMount is only executed once
callbackRefs.current.onRun = onRun;
callbackRefs.current.onSave = onSave;
callbackRefs.current.onChange = onChange;
}, [onRun, onSave, onChange]);

const debounceChanges = debounce((newValue) => {
onChange(newValue);
}, 300);
const handleEditorChange = (value: string | undefined) => {
debounceChanges(value);
};

const onMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => {
addMonacoCommands(monaco, editor, callbackRefs.current);
addMonacoSingleLineActions(editor, monaco, allowLinebreaks, setHeight);
};

useEffect(() => {
const allVariables = getAllVariables(collection);
if (allVariables && withVariables && monaco) {
setMonacoVariables(monaco, allVariables, 'plaintext');
}
}, [collection.collectionVariables, collection.activeEnvironmentUid, withVariables, monaco]);

return (
<Paper className={asInput ? classes.paper : classes.paperHidden}>
<Editor
options={{
readOnly: readOnly,
wordWrap: 'off',
wrappingIndent: 'indent',
autoIndent: 'keep',
formatOnType: true,
formatOnPaste: true,
scrollBeyondLastLine: false,
automaticLayout: true,
scrollbar: {
vertical: allowLinebreaks ? 'auto' : 'hidden',
horizontal: 'hidden'
},
folding: false,
renderLineHighlight: 'none',
lineNumbers: 'off',
lineDecorationsWidth: 0,
lineNumbersMinChars: 0,
glyphMargin: false,
links: false,
overviewRulerLanes: 0,
overviewRulerBorder: false,
hideCursorInOverviewRuler: true,
scrollBeyondLastColumn: 0,
showFoldingControls: 'never',
selectionHighlight: false,
occurrencesHighlight: 'off',
find: { addExtraSpaceOnTop: false, autoFindInSelection: 'never', seedSearchStringFromSelection: 'never' },
minimap: { enabled: false }
}}
className={classes.editor}
theme={displayedTheme === 'dark' ? 'bruno-dark' : 'bruno-light'}
loading={null} // Loading looks weird with singeline editor
language={'plaintext'}
value={value}
onMount={onMount}
onChange={!readOnly ? handleEditorChange : () => {}}
height={height}
/>
</Paper>
);
};
25 changes: 16 additions & 9 deletions packages/bruno-app/src/components/CodeEditor/index.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useSelector } from 'react-redux';
import MonacoEditor from './Monaco';
import Codemirror from './Codemirror';
import SingleLineEditor from './Codemirror/SingleLineEditor';
import { MonacoSingleline } from './Monaco/MonacoSingleline';
import { MonacoEditor } from './Monaco/Monaco';

const CodeEditor = ({
collection,
Expand All @@ -15,6 +16,7 @@ const CodeEditor = ({
value,
singleLine,
withVariables = false,
allowLinebreaks = false,
height = '60vh'
}) => {
const preferences = useSelector((state) => state.app.preferences);
Expand All @@ -30,16 +32,21 @@ const CodeEditor = ({
value,
singleLine,
withVariables,
allowLinebreaks,
height
};
// const [withVariables, height, ...rest] = forwardProps
return preferences?.editor?.monaco ? (
<MonacoEditor {...forwardProps} />
) : singleLine ? (
<SingleLineEditor {...forwardProps} />
) : (
<Codemirror {...forwardProps} />
);

if (preferences?.editor?.monaco) {
if (singleLine) {
return <MonacoSingleline {...forwardProps} />;
}
return <MonacoEditor {...forwardProps} />;
}

if (singleLine) {
return <SingleLineEditor {...forwardProps} />;
}
return <Codemirror {...forwardProps} />;
};

export default CodeEditor;
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ const FormUrlEncodedParams = ({ item, collection }) => {
)
}
allowNewlines={true}
allowLinebreaks={true}
onRun={handleRun}
collection={collection}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ const MultipartFormParams = ({ item, collection }) => {
}
onRun={handleRun}
allowNewlines={true}
allowLinebreaks={true}
collection={collection}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const RequestBody = ({ item, collection }) => {
onRun={onRun}
onSave={onSave}
mode={mode[bodyMode]}
withVariables
/>
</StyledWrapper>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ const RequestHeaders = ({ item, collection }) => {
}
onRun={handleRun}
allowNewlines={true}
allowLinebreaks={true}
collection={collection}
/>
</td>
Expand Down
Loading

0 comments on commit 86f0e3f

Please sign in to comment.