mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-23 17:18:32 +02:00
Response info in new tab
This commit is contained in:
@@ -1,8 +1,5 @@
|
|||||||
import { open } from '@tauri-apps/plugin-shell';
|
|
||||||
import type { HttpResponse } from '../lib/models';
|
import type { HttpResponse } from '../lib/models';
|
||||||
import { IconButton } from './core/IconButton';
|
|
||||||
import { KeyValueRow, KeyValueRows } from './core/KeyValueRow';
|
import { KeyValueRow, KeyValueRows } from './core/KeyValueRow';
|
||||||
import { Separator } from './core/Separator';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
response: HttpResponse;
|
response: HttpResponse;
|
||||||
@@ -13,33 +10,9 @@ export function ResponseHeaders({ response }: Props) {
|
|||||||
<div className="overflow-auto h-full pb-4">
|
<div className="overflow-auto h-full pb-4">
|
||||||
<KeyValueRows>
|
<KeyValueRows>
|
||||||
{response.headers.map((h, i) => (
|
{response.headers.map((h, i) => (
|
||||||
<KeyValueRow key={i} label={h.name} value={h.value} labelClassName="!text-violet-600" />
|
<KeyValueRow labelColor="primary" key={i} label={h.name} value={h.value} />
|
||||||
))}
|
))}
|
||||||
</KeyValueRows>
|
</KeyValueRows>
|
||||||
<Separator className="my-4">Other Info</Separator>
|
|
||||||
<KeyValueRows>
|
|
||||||
<KeyValueRow label="Version" value={response.version} />
|
|
||||||
<KeyValueRow label="Remote Address" value={response.remoteAddr} />
|
|
||||||
<KeyValueRow
|
|
||||||
label={
|
|
||||||
<div className="flex items-center">
|
|
||||||
URL
|
|
||||||
<IconButton
|
|
||||||
iconSize="sm"
|
|
||||||
className="inline-block w-auto ml-1 !h-auto opacity-50 hover:opacity-100"
|
|
||||||
icon="externalLink"
|
|
||||||
onClick={() => open(response.url)}
|
|
||||||
title="Open in browser"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
value={
|
|
||||||
<div className="flex">
|
|
||||||
<span className="select-text cursor-text">{response.url}</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</KeyValueRows>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
39
src-web/components/ResponseInfo.tsx
Normal file
39
src-web/components/ResponseInfo.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { open } from '@tauri-apps/plugin-shell';
|
||||||
|
import type { HttpResponse } from '../lib/models';
|
||||||
|
import { IconButton } from './core/IconButton';
|
||||||
|
import { KeyValueRow, KeyValueRows } from './core/KeyValueRow';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
response: HttpResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ResponseInfo({ response }: Props) {
|
||||||
|
return (
|
||||||
|
<div className="overflow-auto h-full pb-4">
|
||||||
|
<KeyValueRows>
|
||||||
|
<KeyValueRow labelColor="info" label="Version" value={response.version} />
|
||||||
|
<KeyValueRow labelColor="info" label="Remote Address" value={response.remoteAddr} />
|
||||||
|
<KeyValueRow
|
||||||
|
labelColor="info"
|
||||||
|
label={
|
||||||
|
<div className="flex items-center">
|
||||||
|
URL
|
||||||
|
<IconButton
|
||||||
|
iconSize="sm"
|
||||||
|
className="inline-block w-auto ml-1 !h-auto opacity-50 hover:opacity-100"
|
||||||
|
icon="externalLink"
|
||||||
|
onClick={() => open(response.url)}
|
||||||
|
title="Open in browser"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
value={
|
||||||
|
<div className="flex">
|
||||||
|
<span className="select-text cursor-text">{response.url}</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</KeyValueRows>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import { TabContent, Tabs } from './core/Tabs/Tabs';
|
|||||||
import { EmptyStateText } from './EmptyStateText';
|
import { EmptyStateText } from './EmptyStateText';
|
||||||
import { RecentResponsesDropdown } from './RecentResponsesDropdown';
|
import { RecentResponsesDropdown } from './RecentResponsesDropdown';
|
||||||
import { ResponseHeaders } from './ResponseHeaders';
|
import { ResponseHeaders } from './ResponseHeaders';
|
||||||
|
import { ResponseInfo } from './ResponseInfo';
|
||||||
import { AudioViewer } from './responseViewers/AudioViewer';
|
import { AudioViewer } from './responseViewers/AudioViewer';
|
||||||
import { CsvViewer } from './responseViewers/CsvViewer';
|
import { CsvViewer } from './responseViewers/CsvViewer';
|
||||||
import { ImageViewer } from './responseViewers/ImageViewer';
|
import { ImageViewer } from './responseViewers/ImageViewer';
|
||||||
@@ -46,7 +47,7 @@ export const ResponsePane = memo(function ResponsePane({ style, className, activ
|
|||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
value: 'body',
|
value: 'body',
|
||||||
label: 'Preview',
|
label: 'Preview Mode',
|
||||||
options: {
|
options: {
|
||||||
value: viewMode,
|
value: viewMode,
|
||||||
onChange: setViewMode,
|
onChange: setViewMode,
|
||||||
@@ -67,6 +68,10 @@ export const ResponsePane = memo(function ResponsePane({ style, className, activ
|
|||||||
),
|
),
|
||||||
value: 'headers',
|
value: 'headers',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Info',
|
||||||
|
value: 'info',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
[activeResponse?.headers, contentType, setViewMode, viewMode],
|
[activeResponse?.headers, contentType, setViewMode, viewMode],
|
||||||
);
|
);
|
||||||
@@ -148,6 +153,9 @@ export const ResponsePane = memo(function ResponsePane({ style, className, activ
|
|||||||
<TabContent value="headers">
|
<TabContent value="headers">
|
||||||
<ResponseHeaders response={activeResponse} />
|
<ResponseHeaders response={activeResponse} />
|
||||||
</TabContent>
|
</TabContent>
|
||||||
|
<TabContent value="info">
|
||||||
|
<ResponseInfo response={activeResponse} />
|
||||||
|
</TabContent>
|
||||||
<TabContent value="body">
|
<TabContent value="body">
|
||||||
{!activeResponse.contentLength ? (
|
{!activeResponse.contentLength ? (
|
||||||
<div className="pb-2 h-full">
|
<div className="pb-2 h-full">
|
||||||
@@ -166,6 +174,8 @@ export const ResponsePane = memo(function ResponsePane({ style, className, activ
|
|||||||
) : viewMode === 'pretty' && contentType?.includes('html') ? (
|
) : viewMode === 'pretty' && contentType?.includes('html') ? (
|
||||||
<WebPageViewer response={activeResponse} />
|
<WebPageViewer response={activeResponse} />
|
||||||
) : (
|
) : (
|
||||||
|
// ) : viewMode === 'pretty' && contentType?.includes('json') ? (
|
||||||
|
// <JsonAttributeTree attrValue={activeResponse} />
|
||||||
<TextViewer
|
<TextViewer
|
||||||
className="-mr-2" // Pull to the right
|
className="-mr-2" // Pull to the right
|
||||||
response={activeResponse}
|
response={activeResponse}
|
||||||
|
|||||||
@@ -9,9 +9,16 @@ interface Props {
|
|||||||
attrValue: any;
|
attrValue: any;
|
||||||
attrKey?: string | number;
|
attrKey?: string | number;
|
||||||
attrKeyJsonPath?: string;
|
attrKeyJsonPath?: string;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const JsonAttributeTree = ({ depth = 0, attrKey, attrValue, attrKeyJsonPath }: Props) => {
|
export const JsonAttributeTree = ({
|
||||||
|
depth = 0,
|
||||||
|
attrKey,
|
||||||
|
attrValue,
|
||||||
|
attrKeyJsonPath,
|
||||||
|
className,
|
||||||
|
}: Props) => {
|
||||||
attrKeyJsonPath = attrKeyJsonPath ?? `${attrKey}`;
|
attrKeyJsonPath = attrKeyJsonPath ?? `${attrKey}`;
|
||||||
|
|
||||||
const [isExpanded, setIsExpanded] = useState(true);
|
const [isExpanded, setIsExpanded] = useState(true);
|
||||||
@@ -59,7 +66,7 @@ export const JsonAttributeTree = ({ depth = 0, attrKey, attrValue, attrKeyJsonPa
|
|||||||
: null,
|
: null,
|
||||||
isExpandable: attrValue.length > 0,
|
isExpandable: attrValue.length > 0,
|
||||||
label: isExpanded ? `[${attrValue.length || ' '}]` : `[⋯]`,
|
label: isExpanded ? `[${attrValue.length || ' '}]` : `[⋯]`,
|
||||||
labelClassName: 'text-subtler',
|
labelClassName: 'text-fg-subtler',
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
@@ -77,10 +84,18 @@ export const JsonAttributeTree = ({ depth = 0, attrKey, attrValue, attrKeyJsonPa
|
|||||||
}, [attrValue, attrKeyJsonPath, isExpanded, depth]);
|
}, [attrValue, attrKeyJsonPath, isExpanded, depth]);
|
||||||
|
|
||||||
const labelEl = (
|
const labelEl = (
|
||||||
<span className={classNames(labelClassName, 'select-text group-hover:text-fg')}>{label}</span>
|
<span className={classNames(labelClassName, 'select-text group-hover:text-fg-subtle')}>
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div className={classNames(/*depth === 0 && '-ml-4',*/ 'font-mono text-xs')}>
|
<div
|
||||||
|
className={classNames(
|
||||||
|
className,
|
||||||
|
/*depth === 0 && '-ml-4',*/ 'font-mono text-xs',
|
||||||
|
depth === 0 && 'h-full overflow-y-auto pb-2',
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{isExpandable ? (
|
{isExpandable ? (
|
||||||
<button className="group relative flex items-center pl-4 w-full" onClick={toggleExpanded}>
|
<button className="group relative flex items-center pl-4 w-full" onClick={toggleExpanded}>
|
||||||
|
|||||||
@@ -24,13 +24,20 @@ interface Props {
|
|||||||
label: ReactNode;
|
label: ReactNode;
|
||||||
value: ReactNode;
|
value: ReactNode;
|
||||||
labelClassName?: string;
|
labelClassName?: string;
|
||||||
|
labelColor?: 'secondary' | 'primary' | 'info';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function KeyValueRow({ label, value, labelClassName }: Props) {
|
export function KeyValueRow({ label, value, labelColor = 'secondary', labelClassName }: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<td
|
<td
|
||||||
className={classNames('py-0.5 pr-2 text-fg-subtle select-text cursor-text', labelClassName)}
|
className={classNames(
|
||||||
|
'py-0.5 pr-2 select-text cursor-text',
|
||||||
|
labelClassName,
|
||||||
|
labelColor === 'primary' && 'text-fg-primary',
|
||||||
|
labelColor === 'secondary' && 'text-fg-subtle',
|
||||||
|
labelColor === 'info' && 'text-fg-info',
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user