mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-11 17:46:41 -05:00
Bulk editor (#45)
Bulk editor for all pair editors except multipart/form-data
This commit is contained in:
@@ -43,7 +43,7 @@ type Pair = string | boolean;
|
||||
|
||||
type PairsByName = Record<string, Pair[]>;
|
||||
|
||||
export function pluginHookImport(ctx: any, rawData: string) {
|
||||
export function pluginHookImport(_: any, rawData: string) {
|
||||
if (!rawData.match(/^\s*curl /)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ import { describe, expect, test } from 'vitest';
|
||||
import { HttpRequest, Model, Workspace } from '../../../src-web/lib/models';
|
||||
import { pluginHookImport } from '../src';
|
||||
|
||||
const ctx = {};
|
||||
|
||||
describe('importer-curl', () => {
|
||||
test('Imports basic GET', () => {
|
||||
expect(pluginHookImport('curl https://yaak.app')).toEqual({
|
||||
expect(pluginHookImport(ctx, 'curl https://yaak.app')).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
httpRequests: [
|
||||
@@ -17,7 +19,7 @@ describe('importer-curl', () => {
|
||||
});
|
||||
|
||||
test('Explicit URL', () => {
|
||||
expect(pluginHookImport('curl --url https://yaak.app')).toEqual({
|
||||
expect(pluginHookImport(ctx, 'curl --url https://yaak.app')).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
httpRequests: [
|
||||
@@ -30,7 +32,7 @@ describe('importer-curl', () => {
|
||||
});
|
||||
|
||||
test('Missing URL', () => {
|
||||
expect(pluginHookImport('curl -X POST')).toEqual({
|
||||
expect(pluginHookImport(ctx, 'curl -X POST')).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
httpRequests: [
|
||||
@@ -43,7 +45,7 @@ describe('importer-curl', () => {
|
||||
});
|
||||
|
||||
test('URL between', () => {
|
||||
expect(pluginHookImport('curl -v https://yaak.app -X POST')).toEqual({
|
||||
expect(pluginHookImport(ctx, 'curl -v https://yaak.app -X POST')).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
httpRequests: [
|
||||
@@ -57,7 +59,7 @@ describe('importer-curl', () => {
|
||||
});
|
||||
|
||||
test('Random flags', () => {
|
||||
expect(pluginHookImport('curl --random -Z -Y -S --foo https://yaak.app')).toEqual({
|
||||
expect(pluginHookImport(ctx, 'curl --random -Z -Y -S --foo https://yaak.app')).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
httpRequests: [
|
||||
@@ -70,7 +72,7 @@ describe('importer-curl', () => {
|
||||
});
|
||||
|
||||
test('Imports --request method', () => {
|
||||
expect(pluginHookImport('curl --request POST https://yaak.app')).toEqual({
|
||||
expect(pluginHookImport(ctx, 'curl --request POST https://yaak.app')).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
httpRequests: [
|
||||
@@ -84,7 +86,7 @@ describe('importer-curl', () => {
|
||||
});
|
||||
|
||||
test('Imports -XPOST method', () => {
|
||||
expect(pluginHookImport('curl -XPOST --request POST https://yaak.app')).toEqual({
|
||||
expect(pluginHookImport(ctx, 'curl -XPOST --request POST https://yaak.app')).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
httpRequests: [
|
||||
@@ -99,7 +101,10 @@ describe('importer-curl', () => {
|
||||
|
||||
test('Imports multiple requests', () => {
|
||||
expect(
|
||||
pluginHookImport('curl \\\n https://yaak.app\necho "foo"\ncurl example.com;curl foo.com'),
|
||||
pluginHookImport(
|
||||
ctx,
|
||||
'curl \\\n https://yaak.app\necho "foo"\ncurl example.com;curl foo.com',
|
||||
),
|
||||
).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
@@ -114,7 +119,7 @@ describe('importer-curl', () => {
|
||||
|
||||
test('Imports form data', () => {
|
||||
expect(
|
||||
pluginHookImport('curl -X POST -F "a=aaa" -F b=bbb" -F f=@filepath https://yaak.app'),
|
||||
pluginHookImport(ctx, 'curl -X POST -F "a=aaa" -F b=bbb" -F f=@filepath https://yaak.app'),
|
||||
).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
@@ -144,7 +149,7 @@ describe('importer-curl', () => {
|
||||
});
|
||||
|
||||
test('Imports data params as form url-encoded', () => {
|
||||
expect(pluginHookImport('curl -d a -d b -d c=ccc https://yaak.app')).toEqual({
|
||||
expect(pluginHookImport(ctx, 'curl -d a -d b -d c=ccc https://yaak.app')).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
httpRequests: [
|
||||
@@ -174,7 +179,7 @@ describe('importer-curl', () => {
|
||||
|
||||
test('Imports data params as text', () => {
|
||||
expect(
|
||||
pluginHookImport('curl -H Content-Type:text/plain -d a -d b -d c=ccc https://yaak.app'),
|
||||
pluginHookImport(ctx, 'curl -H Content-Type:text/plain -d a -d b -d c=ccc https://yaak.app'),
|
||||
).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
@@ -194,6 +199,7 @@ describe('importer-curl', () => {
|
||||
test('Imports multi-line JSON', () => {
|
||||
expect(
|
||||
pluginHookImport(
|
||||
ctx,
|
||||
`curl -H Content-Type:application/json -d $'{\n "foo":"bar"\n}' https://yaak.app`,
|
||||
),
|
||||
).toEqual({
|
||||
@@ -214,7 +220,7 @@ describe('importer-curl', () => {
|
||||
|
||||
test('Imports multiple headers', () => {
|
||||
expect(
|
||||
pluginHookImport('curl -H Foo:bar --header Name -H AAA:bbb -H :ccc https://yaak.app'),
|
||||
pluginHookImport(ctx, 'curl -H Foo:bar --header Name -H AAA:bbb -H :ccc https://yaak.app'),
|
||||
).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
@@ -234,7 +240,7 @@ describe('importer-curl', () => {
|
||||
});
|
||||
|
||||
test('Imports basic auth', () => {
|
||||
expect(pluginHookImport('curl --user user:pass https://yaak.app')).toEqual({
|
||||
expect(pluginHookImport(ctx, 'curl --user user:pass https://yaak.app')).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
httpRequests: [
|
||||
@@ -252,7 +258,7 @@ describe('importer-curl', () => {
|
||||
});
|
||||
|
||||
test('Imports digest auth', () => {
|
||||
expect(pluginHookImport('curl --digest --user user:pass https://yaak.app')).toEqual({
|
||||
expect(pluginHookImport(ctx, 'curl --digest --user user:pass https://yaak.app')).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
httpRequests: [
|
||||
@@ -270,7 +276,7 @@ describe('importer-curl', () => {
|
||||
});
|
||||
|
||||
test('Imports cookie as header', () => {
|
||||
expect(pluginHookImport('curl --cookie "foo=bar" https://yaak.app')).toEqual({
|
||||
expect(pluginHookImport(ctx, 'curl --cookie "foo=bar" https://yaak.app')).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
httpRequests: [
|
||||
@@ -284,7 +290,7 @@ describe('importer-curl', () => {
|
||||
});
|
||||
|
||||
test('Imports query params from the URL', () => {
|
||||
expect(pluginHookImport('curl "https://yaak.app?foo=bar&baz=a%20a"')).toEqual({
|
||||
expect(pluginHookImport(ctx, 'curl "https://yaak.app?foo=bar&baz=a%20a"')).toEqual({
|
||||
resources: {
|
||||
workspaces: [baseWorkspace()],
|
||||
httpRequests: [
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { pluginHookImport } from '../src';
|
||||
|
||||
const ctx = {};
|
||||
|
||||
describe('importer-yaak', () => {
|
||||
test('Skips invalid imports', () => {
|
||||
expect(pluginHookImport('not JSON')).toBeUndefined();
|
||||
expect(pluginHookImport('[]')).toBeUndefined();
|
||||
expect(pluginHookImport(JSON.stringify({ resources: {} }))).toBeUndefined();
|
||||
expect(pluginHookImport(ctx, 'not JSON')).toBeUndefined();
|
||||
expect(pluginHookImport(ctx, '[]')).toBeUndefined();
|
||||
expect(pluginHookImport(ctx, JSON.stringify({ resources: {} }))).toBeUndefined();
|
||||
});
|
||||
|
||||
test('converts schema 1 to 2', () => {
|
||||
const imported = pluginHookImport(
|
||||
ctx,
|
||||
JSON.stringify({
|
||||
yaakSchema: 1,
|
||||
resources: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import type { HttpRequest } from '../lib/models';
|
||||
import type { Pair, PairEditorProps } from './core/PairEditor';
|
||||
import { PairEditor } from './core/PairEditor';
|
||||
import { PairOrBulkEditor } from './core/PairOrBulkEditor';
|
||||
|
||||
type Props = {
|
||||
forceUpdateKey: string;
|
||||
@@ -27,7 +27,8 @@ export function FormUrlencodedEditor({ body, forceUpdateKey, onChange }: Props)
|
||||
);
|
||||
|
||||
return (
|
||||
<PairEditor
|
||||
<PairOrBulkEditor
|
||||
preferenceName="form_urlencoded"
|
||||
valueAutocompleteVariables
|
||||
nameAutocompleteVariables
|
||||
namePlaceholder="entry_name"
|
||||
|
||||
@@ -125,7 +125,7 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
|
||||
{...extraEditorProps}
|
||||
/>
|
||||
<div className="grid grid-rows-[auto_minmax(0,1fr)] grid-cols-1 min-h-[5rem]">
|
||||
<Separator variant="primary" className="pb-1">
|
||||
<Separator dashed className="pb-1">
|
||||
Variables
|
||||
</Separator>
|
||||
<Editor
|
||||
|
||||
@@ -6,7 +6,7 @@ import { mimeTypes } from '../lib/data/mimetypes';
|
||||
import type { HttpRequest } from '../lib/models';
|
||||
import type { GenericCompletionConfig } from './core/Editor/genericCompletion';
|
||||
import type { PairEditorProps } from './core/PairEditor';
|
||||
import { PairEditor } from './core/PairEditor';
|
||||
import { PairOrBulkEditor } from './core/PairOrBulkEditor';
|
||||
|
||||
type Props = {
|
||||
forceUpdateKey: string;
|
||||
@@ -16,7 +16,8 @@ type Props = {
|
||||
|
||||
export function HeadersEditor({ headers, onChange, forceUpdateKey }: Props) {
|
||||
return (
|
||||
<PairEditor
|
||||
<PairOrBulkEditor
|
||||
preferenceName="headers"
|
||||
valueAutocompleteVariables
|
||||
nameAutocompleteVariables
|
||||
pairs={headers}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { HttpRequest } from '../lib/models';
|
||||
import { PairEditor } from './core/PairEditor';
|
||||
import { PairOrBulkEditor } from './core/PairOrBulkEditor';
|
||||
|
||||
type Props = {
|
||||
forceUpdateKey: string;
|
||||
@@ -9,7 +9,8 @@ type Props = {
|
||||
|
||||
export function UrlParametersEditor({ urlParameters, forceUpdateKey, onChange }: Props) {
|
||||
return (
|
||||
<PairEditor
|
||||
<PairOrBulkEditor
|
||||
preferenceName="url_parameters"
|
||||
valueAutocompleteVariables
|
||||
nameAutocompleteVariables
|
||||
namePlaceholder="param_name"
|
||||
|
||||
48
src-web/components/core/BulkPairEditor.tsx
Normal file
48
src-web/components/core/BulkPairEditor.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { Editor } from './Editor';
|
||||
import type { PairEditorProps } from './PairEditor';
|
||||
|
||||
type Props = Pick<
|
||||
PairEditorProps,
|
||||
'onChange' | 'pairs' | 'namePlaceholder' | 'valuePlaceholder'
|
||||
> & {
|
||||
foo?: string;
|
||||
};
|
||||
|
||||
export function BulkPairEditor({ pairs, onChange, namePlaceholder, valuePlaceholder }: Props) {
|
||||
const pairsText = useMemo(() => {
|
||||
return pairs
|
||||
.filter((p) => !(p.name.trim() === '' && p.value.trim() === ''))
|
||||
.map((p) => `${p.name}: ${p.value}`)
|
||||
.join('\n');
|
||||
}, [pairs]);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(text: string) => {
|
||||
const pairs = text
|
||||
.split('\n')
|
||||
.filter((l: string) => l.trim())
|
||||
.map(lineToPair);
|
||||
onChange(pairs);
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
return (
|
||||
<Editor
|
||||
placeholder={`${namePlaceholder ?? 'name'}: ${valuePlaceholder ?? 'value'}`}
|
||||
defaultValue={pairsText}
|
||||
contentType="pairs"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function lineToPair(l: string): PairEditorProps['pairs'][0] {
|
||||
const [name, ...values] = l.split(':');
|
||||
const pair: PairEditorProps['pairs'][0] = {
|
||||
name: (name ?? '').trim(),
|
||||
value: values.join(':').trim(),
|
||||
};
|
||||
return pair;
|
||||
}
|
||||
@@ -102,7 +102,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
// Use ref so we can update the handler without re-initializing the editor
|
||||
const handleChange = useRef<EditorProps['onChange']>(onChange);
|
||||
useEffect(() => {
|
||||
handleChange.current = onChange;
|
||||
handleChange.current = onChange ? onChange : onChange;
|
||||
}, [onChange]);
|
||||
|
||||
// Use ref so we can update the handler without re-initializing the editor
|
||||
|
||||
@@ -35,6 +35,7 @@ import { graphql, graphqlLanguageSupport } from 'cm6-graphql';
|
||||
import { EditorView } from 'codemirror';
|
||||
import type { Environment, Workspace } from '../../../lib/models';
|
||||
import type { EditorProps } from './index';
|
||||
import { pairs } from './pairs/extension';
|
||||
import { text } from './text/extension';
|
||||
import { twig } from './twig/extension';
|
||||
import { url } from './url/extension';
|
||||
@@ -71,6 +72,7 @@ const syntaxExtensions: Record<string, LanguageSupport> = {
|
||||
'application/xml': xml(),
|
||||
'text/xml': xml(),
|
||||
url: url(),
|
||||
pairs: pairs(),
|
||||
};
|
||||
|
||||
export function getLanguageExtension({
|
||||
|
||||
11
src-web/components/core/Editor/pairs/extension.ts
Normal file
11
src-web/components/core/Editor/pairs/extension.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { LanguageSupport, LRLanguage } from '@codemirror/language';
|
||||
import { parser } from './pairs';
|
||||
|
||||
const urlLanguage = LRLanguage.define({
|
||||
parser,
|
||||
languageData: {},
|
||||
});
|
||||
|
||||
export function pairs() {
|
||||
return new LanguageSupport(urlLanguage, []);
|
||||
}
|
||||
7
src-web/components/core/Editor/pairs/highlight.ts
Normal file
7
src-web/components/core/Editor/pairs/highlight.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { styleTags, tags as t } from '@lezer/highlight';
|
||||
|
||||
export const highlight = styleTags({
|
||||
Sep: t.bracket,
|
||||
Key: t.attributeName,
|
||||
Value: t.string,
|
||||
});
|
||||
9
src-web/components/core/Editor/pairs/pairs.grammar
Normal file
9
src-web/components/core/Editor/pairs/pairs.grammar
Normal file
@@ -0,0 +1,9 @@
|
||||
@top pairs { (Key? Sep Value)* }
|
||||
|
||||
@tokens {
|
||||
Sep { ":" }
|
||||
Key { ![:]+ }
|
||||
Value { ![\n]+ }
|
||||
}
|
||||
|
||||
@external propSource highlight from "./highlight"
|
||||
19
src-web/components/core/Editor/pairs/pairs.ts
Normal file
19
src-web/components/core/Editor/pairs/pairs.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
import {LRParser} from "@lezer/lr"
|
||||
import {highlight} from "./highlight"
|
||||
export const parser = LRParser.deserialize({
|
||||
version: 14,
|
||||
states: "!QQQOPOOOYOQO'#CaO_OPO'#CaQQOPOOOOOO,58{,58{OdOQO,58{OOOO-E6_-E6_OOOO1G.g1G.g",
|
||||
stateData: "i~OQQORPO~OSSO~ORTO~OSVO~O",
|
||||
goto: "]UPPPPPVQRORUR",
|
||||
nodeNames: "⚠ pairs Key Sep Value",
|
||||
maxTerm: 6,
|
||||
propSources: [highlight],
|
||||
skippedNodes: [0],
|
||||
repeatNodeCount: 1,
|
||||
tokenData: "#oRRVOYhYZ!UZ![h![!]#[!];'Sh;'S;=`#U<%lOhRoVQPSQOYhYZ!UZ![h![!]!m!];'Sh;'S;=`#U<%lOhP!ZSQPO![!U!];'S!U;'S;=`!g<%lO!UP!jP;=`<%l!UQ!rSSQOY!mZ;'S!m;'S;=`#O<%lO!mQ#RP;=`<%l!mR#XP;=`<%lhR#cSRPSQOY!mZ;'S!m;'S;=`#O<%lO!m",
|
||||
tokenizers: [0, 1],
|
||||
topRules: {"pairs":[0,1]},
|
||||
tokenPrec: 0
|
||||
})
|
||||
|
||||
@@ -5,6 +5,9 @@ import { memo } from 'react';
|
||||
|
||||
const icons = {
|
||||
alert: lucide.AlertTriangleIcon,
|
||||
text: lucide.FileTextIcon,
|
||||
table: lucide.TableIcon,
|
||||
fileCode: lucide.FileCodeIcon,
|
||||
archive: lucide.ArchiveIcon,
|
||||
arrowBigDownDash: lucide.ArrowBigDownDashIcon,
|
||||
arrowBigLeftDash: lucide.ArrowBigLeftDashIcon,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { open } from '@tauri-apps/plugin-dialog';
|
||||
import classNames from 'classnames';
|
||||
import type { EditorView } from 'codemirror';
|
||||
import { Fragment, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { XYCoord } from 'react-dnd';
|
||||
import { useDrag, useDrop } from 'react-dnd';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
@@ -49,7 +49,7 @@ type PairContainer = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const PairEditor = memo(function PairEditor({
|
||||
export function PairEditor({
|
||||
className,
|
||||
forceUpdateKey,
|
||||
nameAutocomplete,
|
||||
@@ -163,8 +163,8 @@ export const PairEditor = memo(function PairEditor({
|
||||
<div
|
||||
className={classNames(
|
||||
className,
|
||||
'@container',
|
||||
'pb-2 mb-auto',
|
||||
'@container relative',
|
||||
'pb-2 mb-auto h-full',
|
||||
!noScroll && 'overflow-y-auto max-h-full',
|
||||
// Move over the width of the drag handle
|
||||
'-ml-3',
|
||||
@@ -204,7 +204,7 @@ export const PairEditor = memo(function PairEditor({
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
enum ItemTypes {
|
||||
ROW = 'pair-row',
|
||||
|
||||
37
src-web/components/core/PairOrBulkEditor.tsx
Normal file
37
src-web/components/core/PairOrBulkEditor.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import classNames from 'classnames';
|
||||
import { useKeyValue } from '../../hooks/useKeyValue';
|
||||
import { BulkPairEditor } from './BulkPairEditor';
|
||||
import { IconButton } from './IconButton';
|
||||
import type { PairEditorProps } from './PairEditor';
|
||||
import { PairEditor } from './PairEditor';
|
||||
|
||||
interface Props extends PairEditorProps {
|
||||
preferenceName: string;
|
||||
}
|
||||
|
||||
export function PairOrBulkEditor({ preferenceName, ...props }: Props) {
|
||||
const { value: useBulk, set: setUseBulk } = useKeyValue<boolean>({
|
||||
namespace: 'global',
|
||||
key: ['bulk_edit', preferenceName],
|
||||
fallback: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="relative h-full w-full group/wrapper">
|
||||
{useBulk ? <BulkPairEditor {...props} /> : <PairEditor {...props} />}
|
||||
<div className="absolute right-0 bottom-0">
|
||||
<IconButton
|
||||
size="sm"
|
||||
variant="border"
|
||||
title={useBulk ? 'Bulk edit' : 'Regular Edit'}
|
||||
className={classNames(
|
||||
'transition-opacity opacity-0 group-hover:opacity-80 hover:!opacity-100 shadow',
|
||||
'bg-background text-fg-subtle hover:text-fg group-hover/wrapper:opacity-100',
|
||||
)}
|
||||
onClick={() => setUseBulk((b) => !b)}
|
||||
icon={useBulk ? 'table' : 'fileCode'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,18 +3,19 @@ import type { ReactNode } from 'react';
|
||||
|
||||
interface Props {
|
||||
orientation?: 'horizontal' | 'vertical';
|
||||
variant?: 'primary' | 'secondary';
|
||||
dashed?: boolean;
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export function Separator({ className, orientation = 'horizontal', children }: Props) {
|
||||
export function Separator({ className, dashed, orientation = 'horizontal', children }: Props) {
|
||||
return (
|
||||
<div role="separator" className={classNames(className, 'flex items-center')}>
|
||||
{children && <div className="text-sm text-fg-subtler mr-2 whitespace-nowrap">{children}</div>}
|
||||
<div
|
||||
className={classNames(
|
||||
'bg-background-highlight',
|
||||
'h-0 border-t border-t-background-highlight',
|
||||
dashed && 'border-dashed',
|
||||
orientation === 'horizontal' && 'w-full h-[1px]',
|
||||
orientation === 'vertical' && 'h-full w-[1px]',
|
||||
)}
|
||||
|
||||
@@ -102,7 +102,7 @@ export function TextViewer({ response, pretty, className }: Props) {
|
||||
);
|
||||
|
||||
return result;
|
||||
}, [canFilter, filterText, isJson, isSearching, setFilterText, toggleSearch]);
|
||||
}, [canFilter, filterText, isJson, isSearching, response.id, setFilterText, toggleSearch]);
|
||||
|
||||
return (
|
||||
<Editor
|
||||
|
||||
Reference in New Issue
Block a user