mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-24 01:38:26 +02:00
Support multi-line params and env vars
This commit is contained in:
@@ -180,6 +180,7 @@ const EnvironmentEditor = function ({
|
|||||||
</HStack>
|
</HStack>
|
||||||
<div className="h-full pr-2 pb-2">
|
<div className="h-full pr-2 pb-2">
|
||||||
<PairOrBulkEditor
|
<PairOrBulkEditor
|
||||||
|
allowMultilineValues
|
||||||
preferenceName="environment"
|
preferenceName="environment"
|
||||||
nameAutocomplete={nameAutocomplete}
|
nameAutocomplete={nameAutocomplete}
|
||||||
nameAutocompleteVariables={false}
|
nameAutocompleteVariables={false}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export function FormMultipartEditor({ request, forceUpdateKey, onChange }: Props
|
|||||||
valueAutocompleteVariables
|
valueAutocompleteVariables
|
||||||
nameAutocompleteVariables
|
nameAutocompleteVariables
|
||||||
allowFileValues
|
allowFileValues
|
||||||
|
allowMultilineValues
|
||||||
pairs={pairs}
|
pairs={pairs}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
forceUpdateKey={forceUpdateKey}
|
forceUpdateKey={forceUpdateKey}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export function FormUrlencodedEditor({ request, forceUpdateKey, onChange }: Prop
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PairOrBulkEditor
|
<PairOrBulkEditor
|
||||||
|
allowMultilineValues
|
||||||
preferenceName="form_urlencoded"
|
preferenceName="form_urlencoded"
|
||||||
valueAutocompleteVariables
|
valueAutocompleteVariables
|
||||||
nameAutocompleteVariables
|
nameAutocompleteVariables
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export function UrlParametersEditor({ pairs, forceUpdateKey, onChange, stateKey
|
|||||||
<VStack className="h-full">
|
<VStack className="h-full">
|
||||||
<PairOrBulkEditor
|
<PairOrBulkEditor
|
||||||
ref={pairEditor}
|
ref={pairEditor}
|
||||||
|
allowMultilineValues
|
||||||
forceUpdateKey={forceUpdateKey + urlParametersKey}
|
forceUpdateKey={forceUpdateKey + urlParametersKey}
|
||||||
nameAutocompleteVariables
|
nameAutocompleteVariables
|
||||||
namePlaceholder="param_name"
|
namePlaceholder="param_name"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { generateId } from '../../lib/generateId';
|
import { generateId } from '../../lib/generateId';
|
||||||
import { Editor } from './Editor/Editor';
|
import { Editor } from './Editor/Editor';
|
||||||
import type { PairEditorProps, PairWithId } from './PairEditor';
|
import type { Pair, PairEditorProps, PairWithId } from './PairEditor';
|
||||||
|
|
||||||
type Props = PairEditorProps;
|
type Props = PairEditorProps;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ export function BulkPairEditor({
|
|||||||
const pairsText = useMemo(() => {
|
const pairsText = useMemo(() => {
|
||||||
return pairs
|
return pairs
|
||||||
.filter((p) => !(p.name.trim() === '' && p.value.trim() === ''))
|
.filter((p) => !(p.name.trim() === '' && p.value.trim() === ''))
|
||||||
.map((p) => `${p.name}: ${p.value}`)
|
.map(pairToLine)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
}, [pairs]);
|
}, [pairs]);
|
||||||
|
|
||||||
@@ -45,12 +45,17 @@ export function BulkPairEditor({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pairToLine(pair: Pair) {
|
||||||
|
const value = pair.value.replaceAll('\n', '\\n');
|
||||||
|
return `${pair.name}: ${value}`;
|
||||||
|
}
|
||||||
|
|
||||||
function lineToPair(line: string): PairWithId {
|
function lineToPair(line: string): PairWithId {
|
||||||
const [, name, value] = line.match(/^(:?[^:]+):\s+(.*)$/) ?? [];
|
const [, name, value] = line.match(/^(:?[^:]+):\s+(.*)$/) ?? [];
|
||||||
return {
|
return {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
name: (name ?? '').trim(),
|
name: (name ?? '').trim(),
|
||||||
value: (value ?? '').trim(),
|
value: (value ?? '').replaceAll('\\n', '\n').trim(),
|
||||||
id: generateId(),
|
id: generateId(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { formatSize } from '@yaakapp-internal/lib/formatSize';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { EditorView } from 'codemirror';
|
import type { EditorView } from 'codemirror';
|
||||||
import {
|
import {
|
||||||
@@ -24,7 +23,6 @@ import { Button } from './Button';
|
|||||||
import { Checkbox } from './Checkbox';
|
import { Checkbox } from './Checkbox';
|
||||||
import type { DropdownItem } from './Dropdown';
|
import type { DropdownItem } from './Dropdown';
|
||||||
import { Dropdown } from './Dropdown';
|
import { Dropdown } from './Dropdown';
|
||||||
import type { EditorProps } from './Editor/Editor';
|
|
||||||
import { Editor } from './Editor/Editor';
|
import { Editor } from './Editor/Editor';
|
||||||
import type { GenericCompletionConfig } from './Editor/genericCompletion';
|
import type { GenericCompletionConfig } from './Editor/genericCompletion';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
@@ -41,6 +39,7 @@ export interface PairEditorRef {
|
|||||||
|
|
||||||
export type PairEditorProps = {
|
export type PairEditorProps = {
|
||||||
allowFileValues?: boolean;
|
allowFileValues?: boolean;
|
||||||
|
allowMultilineValues?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
forceUpdateKey?: string;
|
forceUpdateKey?: string;
|
||||||
nameAutocomplete?: GenericCompletionConfig;
|
nameAutocomplete?: GenericCompletionConfig;
|
||||||
@@ -79,6 +78,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
|||||||
{
|
{
|
||||||
stateKey,
|
stateKey,
|
||||||
allowFileValues,
|
allowFileValues,
|
||||||
|
allowMultilineValues,
|
||||||
className,
|
className,
|
||||||
forceUpdateKey,
|
forceUpdateKey,
|
||||||
nameAutocomplete,
|
nameAutocomplete,
|
||||||
@@ -229,6 +229,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
|||||||
{hoveredIndex === i && <DropMarker />}
|
{hoveredIndex === i && <DropMarker />}
|
||||||
<PairEditorRow
|
<PairEditorRow
|
||||||
allowFileValues={allowFileValues}
|
allowFileValues={allowFileValues}
|
||||||
|
allowMultilineValues={allowMultilineValues}
|
||||||
className="py-1"
|
className="py-1"
|
||||||
forceFocusNamePairId={forceFocusNamePairId}
|
forceFocusNamePairId={forceFocusNamePairId}
|
||||||
forceFocusValuePairId={forceFocusValuePairId}
|
forceFocusValuePairId={forceFocusValuePairId}
|
||||||
@@ -256,12 +257,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{!showAll && pairs.length > MAX_INITIAL_PAIRS && (
|
{!showAll && pairs.length > MAX_INITIAL_PAIRS && (
|
||||||
<Button
|
<Button onClick={toggleShowAll} variant="border" className="m-2" size="xs">
|
||||||
onClick={toggleShowAll}
|
|
||||||
variant="border"
|
|
||||||
className="m-2"
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
Show {pairs.length - MAX_INITIAL_PAIRS} More
|
Show {pairs.length - MAX_INITIAL_PAIRS} More
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -289,6 +285,7 @@ type PairEditorRowProps = {
|
|||||||
} & Pick<
|
} & Pick<
|
||||||
PairEditorProps,
|
PairEditorProps,
|
||||||
| 'allowFileValues'
|
| 'allowFileValues'
|
||||||
|
| 'allowMultilineValues'
|
||||||
| 'forceUpdateKey'
|
| 'forceUpdateKey'
|
||||||
| 'nameAutocomplete'
|
| 'nameAutocomplete'
|
||||||
| 'nameAutocompleteVariables'
|
| 'nameAutocompleteVariables'
|
||||||
@@ -304,6 +301,7 @@ type PairEditorRowProps = {
|
|||||||
|
|
||||||
function PairEditorRow({
|
function PairEditorRow({
|
||||||
allowFileValues,
|
allowFileValues,
|
||||||
|
allowMultilineValues,
|
||||||
className,
|
className,
|
||||||
forceFocusNamePairId,
|
forceFocusNamePairId,
|
||||||
forceFocusValuePairId,
|
forceFocusValuePairId,
|
||||||
@@ -330,7 +328,6 @@ function PairEditorRow({
|
|||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const nameInputRef = useRef<EditorView>(null);
|
const nameInputRef = useRef<EditorView>(null);
|
||||||
const valueInputRef = useRef<EditorView>(null);
|
const valueInputRef = useRef<EditorView>(null);
|
||||||
const valueLanguage = languageFromContentType(pair.contentType ?? null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (forceFocusNamePairId === pair.id) {
|
if (forceFocusNamePairId === pair.id) {
|
||||||
@@ -347,17 +344,6 @@ function PairEditorRow({
|
|||||||
const handleFocus = useCallback(() => onFocus?.(pair), [onFocus, pair]);
|
const handleFocus = useCallback(() => onFocus?.(pair), [onFocus, pair]);
|
||||||
const handleDelete = useCallback(() => onDelete?.(pair, false), [onDelete, pair]);
|
const handleDelete = useCallback(() => onDelete?.(pair, false), [onDelete, pair]);
|
||||||
|
|
||||||
const deleteItems = useMemo(
|
|
||||||
(): DropdownItem[] => [
|
|
||||||
{
|
|
||||||
label: 'Delete',
|
|
||||||
onSelect: handleDelete,
|
|
||||||
color: 'danger',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[handleDelete],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleChangeEnabled = useMemo(
|
const handleChangeEnabled = useMemo(
|
||||||
() => (enabled: boolean) => onChange({ ...pair, enabled }),
|
() => (enabled: boolean) => onChange({ ...pair, enabled }),
|
||||||
[onChange, pair],
|
[onChange, pair],
|
||||||
@@ -396,11 +382,27 @@ function PairEditorRow({
|
|||||||
hide={hide}
|
hide={hide}
|
||||||
onChange={handleChangeValueText}
|
onChange={handleChangeValueText}
|
||||||
defaultValue={pair.value}
|
defaultValue={pair.value}
|
||||||
language={valueLanguage}
|
contentType={pair.contentType ?? null}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
[handleChangeValueText, pair.name, pair.value, valueLanguage],
|
[handleChangeValueText, pair.contentType, pair.name, pair.value],
|
||||||
|
);
|
||||||
|
|
||||||
|
const defaultItems = useMemo(
|
||||||
|
(): DropdownItem[] => [
|
||||||
|
{
|
||||||
|
label: 'Edit Multi-line',
|
||||||
|
onSelect: handleEditMultiLineValue,
|
||||||
|
hidden: !allowMultilineValues,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Delete',
|
||||||
|
onSelect: handleDelete,
|
||||||
|
color: 'danger',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[allowMultilineValues, handleDelete, handleEditMultiLineValue],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [, connectDrop] = useDrop<Pair>(
|
const [, connectDrop] = useDrop<Pair>(
|
||||||
@@ -524,8 +526,9 @@ function PairEditorRow({
|
|||||||
size="sm"
|
size="sm"
|
||||||
onClick={handleEditMultiLineValue}
|
onClick={handleEditMultiLineValue}
|
||||||
title={pair.value}
|
title={pair.value}
|
||||||
|
className="text-xs font-mono"
|
||||||
>
|
>
|
||||||
Edit {formatSize(pair.value.length)}
|
{pair.value.split('\n').join(' ')}
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Input
|
<Input
|
||||||
@@ -537,7 +540,6 @@ function PairEditorRow({
|
|||||||
size="sm"
|
size="sm"
|
||||||
containerClassName={classNames(isLast && 'border-dashed')}
|
containerClassName={classNames(isLast && 'border-dashed')}
|
||||||
validate={valueValidate}
|
validate={valueValidate}
|
||||||
language={valueLanguage}
|
|
||||||
forceUpdateKey={forceUpdateKey}
|
forceUpdateKey={forceUpdateKey}
|
||||||
defaultValue={pair.value}
|
defaultValue={pair.value}
|
||||||
label="Value"
|
label="Value"
|
||||||
@@ -562,7 +564,7 @@ function PairEditorRow({
|
|||||||
editMultiLine={handleEditMultiLineValue}
|
editMultiLine={handleEditMultiLineValue}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Dropdown items={deleteItems}>
|
<Dropdown items={defaultItems}>
|
||||||
<IconButton
|
<IconButton
|
||||||
iconSize="sm"
|
iconSize="sm"
|
||||||
size="xs"
|
size="xs"
|
||||||
@@ -641,6 +643,7 @@ function FileActionsDropdown({
|
|||||||
onSelect: onDelete,
|
onSelect: onDelete,
|
||||||
variant: 'danger',
|
variant: 'danger',
|
||||||
leftSlot: <Icon icon="trash" />,
|
leftSlot: <Icon icon="trash" />,
|
||||||
|
color: 'danger',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[editMultiLine, onChangeContentType, onChangeFile, onDelete, pair.contentType, pair.isFile],
|
[editMultiLine, onChangeContentType, onChangeFile, onDelete, pair.contentType, pair.isFile],
|
||||||
@@ -673,16 +676,17 @@ function isPairEmpty(pair: Pair): boolean {
|
|||||||
|
|
||||||
function MultilineEditDialog({
|
function MultilineEditDialog({
|
||||||
defaultValue,
|
defaultValue,
|
||||||
language,
|
contentType,
|
||||||
onChange,
|
onChange,
|
||||||
hide,
|
hide,
|
||||||
}: {
|
}: {
|
||||||
defaultValue: string;
|
defaultValue: string;
|
||||||
language: EditorProps['language'];
|
contentType: string | null;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
hide: () => void;
|
hide: () => void;
|
||||||
}) {
|
}) {
|
||||||
const [value, setValue] = useState<string>(defaultValue);
|
const [value, setValue] = useState<string>(defaultValue);
|
||||||
|
const language = languageFromContentType(contentType, value);
|
||||||
return (
|
return (
|
||||||
<div className="w-[100vw] max-w-[40rem] h-[50vh] max-h-full grid grid-rows-[minmax(0,1fr)_auto]">
|
<div className="w-[100vw] max-w-[40rem] h-[50vh] max-h-full grid grid-rows-[minmax(0,1fr)_auto]">
|
||||||
<Editor
|
<Editor
|
||||||
@@ -691,6 +695,8 @@ function MultilineEditDialog({
|
|||||||
language={language}
|
language={language}
|
||||||
onChange={setValue}
|
onChange={setValue}
|
||||||
stateKey={null}
|
stateKey={null}
|
||||||
|
useTemplating
|
||||||
|
autocompleteVariables
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
Reference in New Issue
Block a user