Add toggle for pretty view

This commit is contained in:
Gregory Schier
2023-02-27 09:08:48 -08:00
parent fe18cd1806
commit 9dc8234f4b
8 changed files with 93 additions and 16 deletions

43
package-lock.json generated
View File

@@ -12,6 +12,7 @@
"@codemirror/lang-html": "^6.4.2",
"@codemirror/lang-javascript": "^6.1.4",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-xml": "^6.0.2",
"@codemirror/language": "^6.6.0",
"@codemirror/search": "^6.2.3",
"@lezer/generator": "^1.2.2",
@@ -497,6 +498,18 @@
"@lezer/json": "^1.0.0"
}
},
"node_modules/@codemirror/lang-xml": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.0.2.tgz",
"integrity": "sha512-JQYZjHL2LAfpiZI2/qZ/qzDuSqmGKMwyApYmEUUCTxLM4MWS7sATUEfIguZQr9Zjx/7gcdnewb039smF6nC2zw==",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.4.0",
"@codemirror/state": "^6.0.0",
"@lezer/common": "^1.0.0",
"@lezer/xml": "^1.0.0"
}
},
"node_modules/@codemirror/language": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.6.0.tgz",
@@ -1126,6 +1139,15 @@
"@lezer/common": "^1.0.0"
}
},
"node_modules/@lezer/xml": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.1.tgz",
"integrity": "sha512-jMDXrV953sDAUEMI25VNrI9dz94Ai96FfeglytFINhhwQ867HKlCE2jt3AwZTCT7M528WxdDWv/Ty8e9wizwmQ==",
"dependencies": {
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0"
}
},
"node_modules/@motionone/animation": {
"version": "10.15.1",
"resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.15.1.tgz",
@@ -7055,6 +7077,18 @@
"@lezer/json": "^1.0.0"
}
},
"@codemirror/lang-xml": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.0.2.tgz",
"integrity": "sha512-JQYZjHL2LAfpiZI2/qZ/qzDuSqmGKMwyApYmEUUCTxLM4MWS7sATUEfIguZQr9Zjx/7gcdnewb039smF6nC2zw==",
"requires": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.4.0",
"@codemirror/state": "^6.0.0",
"@lezer/common": "^1.0.0",
"@lezer/xml": "^1.0.0"
}
},
"@codemirror/language": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.6.0.tgz",
@@ -7450,6 +7484,15 @@
"@lezer/common": "^1.0.0"
}
},
"@lezer/xml": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.1.tgz",
"integrity": "sha512-jMDXrV953sDAUEMI25VNrI9dz94Ai96FfeglytFINhhwQ867HKlCE2jt3AwZTCT7M528WxdDWv/Ty8e9wizwmQ==",
"requires": {
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0"
}
},
"@motionone/animation": {
"version": "10.15.1",
"resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.15.1.tgz",

View File

@@ -16,6 +16,7 @@
"@codemirror/lang-html": "^6.4.2",
"@codemirror/lang-javascript": "^6.1.4",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-xml": "^6.0.2",
"@codemirror/language": "^6.6.0",
"@codemirror/search": "^6.2.3",
"@lezer/generator": "^1.2.2",

View File

@@ -0,0 +1,9 @@
import type { LinkProps } from 'react-router-dom';
import { Link } from 'react-router-dom';
import { Button, ButtonProps } from './Button';
type Props = ButtonProps<typeof Link> & LinkProps;
export function ButtonLink({ ...props }: Props) {
return <Button as={Link} {...props} />;
}

View File

@@ -25,6 +25,7 @@ import {
rectangularSelection,
} from '@codemirror/view';
import { html } from '@codemirror/lang-html';
import { xml } from '@codemirror/lang-xml';
import { parseMixed } from '@lezer/common';
import { EditorState } from '@codemirror/state';
import { json } from '@codemirror/lang-json';
@@ -44,15 +45,15 @@ export const myHighlightStyle = HighlightStyle.define([
{
tag: [t.documentMeta, t.blockComment, t.lineComment, t.docComment, t.comment],
color: '#757b93',
fontStyle: 'italic',
},
{ tag: [t.name], color: '#4699de' },
{ tag: [t.name, t.tagName, t.angleBracket, t.docString], color: '#4699de' },
{ tag: [t.variableName], color: '#31c434' },
{ tag: [t.bool], color: '#e864f6' },
{ tag: [t.attributeName], color: '#8f68ff' },
{ tag: [t.attributeName], color: '#a773ff' },
{ tag: [t.attributeValue], color: '#ff964b' },
{ tag: [t.string], color: '#e8b045' },
{ tag: [t.keyword, t.meta], color: '#45e8a4' },
{ tag: [t.comment], color: '#cec4cc', fontStyle: 'italic' },
]);
// export const defaultHighlightStyle = HighlightStyle.define([
@@ -81,6 +82,8 @@ const syntaxExtensions: Record<string, { base: LanguageSupport; ext: any[] }> =
'application/json': { base: json(), ext: [] },
'application/javascript': { base: javascript(), ext: [] },
'text/html': { base: html(), ext: [] },
'application/xml': { base: xml(), ext: [] },
'text/xml': { base: xml(), ext: [] },
};
export function syntaxExtension({
@@ -90,7 +93,8 @@ export function syntaxExtension({
contentType: string;
useTemplating?: boolean;
}) {
const { base, ext } = syntaxExtensions[contentType] ?? { base: json(), ext: [] };
const justContentType = contentType.split(';')[0] ?? contentType;
const { base, ext } = syntaxExtensions[justContentType] ?? { base: json(), ext: [] };
if (!useTemplating) {
return [base];
}

View File

@@ -2,10 +2,14 @@ import {
ArchiveIcon,
CameraIcon,
CheckIcon,
CodeIcon,
EyeOpenIcon,
GearIcon,
HomeIcon,
MoonIcon,
PaperPlaneIcon,
PlusCircledIcon,
PlusIcon,
QuestionMarkIcon,
SunIcon,
TriangleDownIcon,
@@ -19,17 +23,23 @@ type IconName =
| 'home'
| 'camera'
| 'gear'
| 'eye'
| 'triangle-down'
| 'paper-plane'
| 'update'
| 'question'
| 'check'
| 'plus'
| 'plus-circled'
| 'sun'
| 'code'
| 'moon';
const icons: Record<IconName, NamedExoticComponent<{ className: string }>> = {
'paper-plane': PaperPlaneIcon,
'triangle-down': TriangleDownIcon,
plus: PlusIcon,
'plus-circled': PlusCircledIcon,
archive: ArchiveIcon,
camera: CameraIcon,
check: CheckIcon,
@@ -39,6 +49,8 @@ const icons: Record<IconName, NamedExoticComponent<{ className: string }>> = {
sun: SunIcon,
moon: MoonIcon,
question: QuestionMarkIcon,
eye: EyeOpenIcon,
code: CodeIcon,
};
export interface IconProps {

View File

@@ -15,6 +15,7 @@ interface Props {
export function ResponsePane({ requestId, error }: Props) {
const [activeResponseId, setActiveResponseId] = useState<string | null>(null);
const [viewMode, setViewMode] = useState<'pretty' | 'raw'>('pretty');
const responses = useResponses(requestId);
const response = activeResponseId
? responses.data.find((r) => r.id === activeResponseId)
@@ -73,15 +74,23 @@ export function ResponsePane({ requestId, error }: Props) {
<>
<HStack
items="center"
className="italic text-gray-500 text-sm w-full pointer-events-none h-10 mb-3 flex-shrink-0"
className="italic text-gray-500 text-sm w-full h-10 mb-3 flex-shrink-0"
>
{response.status}
{response.statusReason && ` ${response.statusReason}`}
&nbsp;&bull;&nbsp;
{response.elapsed}ms &nbsp;&bull;&nbsp;
{Math.round(response.body.length / 1000)} KB
<div>
{response.status}
{response.statusReason && ` ${response.statusReason}`}
&nbsp;&bull;&nbsp;
{response.elapsed}ms &nbsp;&bull;&nbsp;
{Math.round(response.body.length / 1000)} KB
</div>
<IconButton
icon={viewMode === 'pretty' ? 'eye' : 'code'}
size="sm"
className="ml-auto"
onClick={() => setViewMode((m) => (m === 'pretty' ? 'raw' : 'pretty'))}
/>
</HStack>
{contentType.includes('html') ? (
{viewMode === 'pretty' && contentType.includes('html') ? (
<iframe
title="Response preview"
srcDoc={contentForIframe}

View File

@@ -27,7 +27,7 @@ export function Sidebar({ className, activeRequestId, workspaceId, requests, ...
<IconButton size="sm" icon="sun" onClick={toggleTheme} />
<IconButton
size="sm"
icon="camera"
icon="plus-circled"
onClick={() => createRequest.mutate({ name: 'Test Request' })}
/>
</HStack>

View File

@@ -1,15 +1,14 @@
import { Link } from 'react-router-dom';
import { useWorkspaces } from '../hooks/useWorkspaces';
import { Button } from '../components/Button';
import { ButtonLink } from '../components/ButtonLink';
export function Workspaces() {
const workspaces = useWorkspaces();
return (
<ul className="p-12">
{workspaces.data?.map((w) => (
<Button as={Link} key={w.id} to={`/workspaces/${w.id}`}>
<ButtonLink key={w.id} to={`/workspaces/${w.id}`}>
{w.name}
</Button>
</ButtonLink>
))}
</ul>
);