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