Custom JSON formatter that works with template syntax (#128)

This commit is contained in:
Gregory Schier
2024-10-21 22:17:14 +00:00
committed by GitHub
parent aa7f18a16f
commit e216214085
17 changed files with 414 additions and 117 deletions

View File

@@ -61,7 +61,7 @@ export interface EditorProps {
onKeyDown?: (e: KeyboardEvent) => void;
singleLine?: boolean;
wrapLines?: boolean;
format?: (v: string) => string;
format?: (v: string) => Promise<string>;
autocomplete?: GenericCompletionConfig;
autocompleteVariables?: boolean;
extraExtensions?: Extension[];
@@ -387,10 +387,10 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
icon="magic_wand"
variant="border"
className={classNames(actionClassName)}
onClick={() => {
onClick={async () => {
if (cm.current === null) return;
const { doc } = cm.current.view.state;
const formatted = format(doc.toString());
const formatted = await format(doc.toString());
// Update editor and blur because the cursor will reset anyway
cm.current.view.dispatch({
changes: { from: 0, to: doc.length, insert: formatted },

View File

@@ -4,10 +4,11 @@ import type { ServerSentEvent } from '@yaakapp-internal/sse';
import classNames from 'classnames';
import { motion } from 'framer-motion';
import React, { Fragment, useMemo, useRef, useState } from 'react';
import { useFormatText } from '../../hooks/useFormatText';
import { useResponseBodyEventSource } from '../../hooks/useResponseBodyEventSource';
import { isJSON } from '../../lib/contentType';
import { tryFormatJson } from '../../lib/formatters';
import { Button } from '../core/Button';
import type { EditorProps } from '../core/Editor';
import { Editor } from '../core/Editor';
import { Icon } from '../core/Icon';
import { InlineCode } from '../core/InlineCode';
@@ -95,11 +96,7 @@ function ActualEventStreamViewer({ response }: Props) {
</div>
</VStack>
) : (
<Editor
readOnly
defaultValue={tryFormatJson(activeEvent.data)}
language={language}
/>
<FormattedEditor language={language} text={activeEvent.data} />
)}
</div>
</div>
@@ -110,6 +107,12 @@ function ActualEventStreamViewer({ response }: Props) {
);
}
function FormattedEditor({ text, language }: { text: string; language: EditorProps['language'] }) {
const formatted = useFormatText({ text, language, pretty: true });
if (formatted.data == null) return null;
return <Editor readOnly defaultValue={formatted.data} language={language} />;
}
function EventStreamEventsVirtual({
events,
activeEventIndex,

View File

@@ -31,7 +31,7 @@ export function HTMLOrTextViewer({ response, pretty, textViewerClassName }: Prop
}
if (language === 'html' && pretty) {
return <WebPageViewer response={response} />;
return <WebPageViewer response={response}/>;
} else {
return (
<TextViewer

View File

@@ -5,8 +5,8 @@ import { createGlobalState } from 'react-use';
import { useCopy } from '../../hooks/useCopy';
import { useDebouncedValue } from '../../hooks/useDebouncedValue';
import { useFilterResponse } from '../../hooks/useFilterResponse';
import { useFormatText } from '../../hooks/useFormatText';
import { useToggle } from '../../hooks/useToggle';
import { tryFormatJson, tryFormatXml } from '../../lib/formatters';
import { CopyButton } from '../CopyButton';
import { Banner } from '../core/Banner';
import { Button } from '../core/Button';
@@ -117,6 +117,8 @@ export function TextViewer({
toggleSearch,
]);
const formattedBody = useFormatText({ text, language, pretty });
if (!showLargeResponse && text.length > LARGE_RESPONSE_BYTES) {
return (
<Banner color="primary" className="h-full flex flex-col gap-3">
@@ -140,12 +142,9 @@ export function TextViewer({
);
}
const formattedBody =
pretty && language === 'json'
? tryFormatJson(text)
: pretty && (language === 'xml' || language === 'html')
? tryFormatXml(text)
: text;
if (formattedBody.isFetching) {
return null;
}
let body;
if (isSearching && filterText?.length > 0) {
@@ -155,7 +154,7 @@ export function TextViewer({
body = filteredResponse.data != null ? filteredResponse.data : '';
}
} else {
body = formattedBody;
body = formattedBody.data;
}
return (