mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-11 22:40:26 +01:00
Tweak response pane and refactor timings
This commit is contained in:
@@ -62,6 +62,7 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
|
||||
{
|
||||
value: TAB_BODY,
|
||||
label: 'Preview Mode',
|
||||
hidden: (activeResponse?.contentLength || 0) === 0,
|
||||
options: {
|
||||
value: viewMode,
|
||||
onChange: setViewMode,
|
||||
@@ -92,6 +93,7 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
|
||||
setViewMode,
|
||||
viewMode,
|
||||
activeResponse?.requestHeaders.length,
|
||||
activeResponse?.contentLength,
|
||||
],
|
||||
);
|
||||
const activeTab = activeTabs?.[activeRequestId];
|
||||
@@ -163,79 +165,66 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
|
||||
</Banner>
|
||||
)}
|
||||
{/* Show tabs if we have any data (headers, body, etc.) even if there's an error */}
|
||||
{(activeResponse?.headers.length > 0 ||
|
||||
activeResponse?.bodyPath ||
|
||||
!activeResponse?.error) && (
|
||||
<Tabs
|
||||
key={activeRequestId} // Freshen tabs on request change
|
||||
value={activeTab}
|
||||
onChangeValue={setActiveTab}
|
||||
tabs={tabs}
|
||||
label="Response"
|
||||
className="ml-3 mr-3 mb-3 min-h-0 flex-1"
|
||||
tabListClassName="mt-0.5"
|
||||
>
|
||||
<TabContent value={TAB_BODY}>
|
||||
<ErrorBoundary name="Http Response Viewer">
|
||||
<Suspense>
|
||||
<ConfirmLargeResponse response={activeResponse}>
|
||||
{activeResponse.state === 'initialized' ? (
|
||||
<EmptyStateText>
|
||||
<VStack space={3}>
|
||||
<HStack space={3}>
|
||||
<LoadingIcon className="text-text-subtlest" />
|
||||
Sending Request
|
||||
</HStack>
|
||||
<Button size="sm" variant="border" onClick={() => cancel.mutate()}>
|
||||
Cancel
|
||||
</Button>
|
||||
</VStack>
|
||||
</EmptyStateText>
|
||||
) : activeResponse.state === 'closed' &&
|
||||
activeResponse.contentLength === 0 ? (
|
||||
<EmptyStateText>Empty </EmptyStateText>
|
||||
) : mimeType?.match(/^text\/event-stream/i) && viewMode === 'pretty' ? (
|
||||
<EventStreamViewer response={activeResponse} />
|
||||
) : mimeType?.match(/^image\/svg/) ? (
|
||||
<SvgViewer response={activeResponse} />
|
||||
) : mimeType?.match(/^image/i) ? (
|
||||
<EnsureCompleteResponse
|
||||
response={activeResponse}
|
||||
Component={ImageViewer}
|
||||
/>
|
||||
) : mimeType?.match(/^audio/i) ? (
|
||||
<EnsureCompleteResponse
|
||||
response={activeResponse}
|
||||
Component={AudioViewer}
|
||||
/>
|
||||
) : mimeType?.match(/^video/i) ? (
|
||||
<EnsureCompleteResponse
|
||||
response={activeResponse}
|
||||
Component={VideoViewer}
|
||||
/>
|
||||
) : mimeType?.match(/pdf/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} Component={PdfViewer} />
|
||||
) : mimeType?.match(/csv|tab-separated/i) ? (
|
||||
<CsvViewer className="pb-2" response={activeResponse} />
|
||||
) : (
|
||||
<HTMLOrTextViewer
|
||||
textViewerClassName="-mr-2 bg-surface" // Pull to the right
|
||||
response={activeResponse}
|
||||
pretty={viewMode === 'pretty'}
|
||||
/>
|
||||
)}
|
||||
</ConfirmLargeResponse>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
</TabContent>
|
||||
<TabContent value={TAB_HEADERS}>
|
||||
<ResponseHeaders response={activeResponse} />
|
||||
</TabContent>
|
||||
<TabContent value={TAB_INFO}>
|
||||
<ResponseInfo response={activeResponse} />
|
||||
</TabContent>
|
||||
</Tabs>
|
||||
)}
|
||||
<Tabs
|
||||
key={activeRequestId} // Freshen tabs on request change
|
||||
value={activeTab}
|
||||
onChangeValue={setActiveTab}
|
||||
tabs={tabs}
|
||||
label="Response"
|
||||
className="ml-3 mr-3 mb-3 min-h-0 flex-1"
|
||||
tabListClassName="mt-0.5"
|
||||
>
|
||||
<TabContent value={TAB_BODY}>
|
||||
<ErrorBoundary name="Http Response Viewer">
|
||||
<Suspense>
|
||||
<ConfirmLargeResponse response={activeResponse}>
|
||||
{activeResponse.state === 'initialized' ? (
|
||||
<EmptyStateText>
|
||||
<VStack space={3}>
|
||||
<HStack space={3}>
|
||||
<LoadingIcon className="text-text-subtlest" />
|
||||
Sending Request
|
||||
</HStack>
|
||||
<Button size="sm" variant="border" onClick={() => cancel.mutate()}>
|
||||
Cancel
|
||||
</Button>
|
||||
</VStack>
|
||||
</EmptyStateText>
|
||||
) : activeResponse.state === 'closed' &&
|
||||
activeResponse.contentLength === 0 ? (
|
||||
<EmptyStateText>Empty </EmptyStateText>
|
||||
) : mimeType?.match(/^text\/event-stream/i) && viewMode === 'pretty' ? (
|
||||
<EventStreamViewer response={activeResponse} />
|
||||
) : mimeType?.match(/^image\/svg/) ? (
|
||||
<SvgViewer response={activeResponse} />
|
||||
) : mimeType?.match(/^image/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} Component={ImageViewer} />
|
||||
) : mimeType?.match(/^audio/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} Component={AudioViewer} />
|
||||
) : mimeType?.match(/^video/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} Component={VideoViewer} />
|
||||
) : mimeType?.match(/pdf/i) ? (
|
||||
<EnsureCompleteResponse response={activeResponse} Component={PdfViewer} />
|
||||
) : mimeType?.match(/csv|tab-separated/i) ? (
|
||||
<CsvViewer className="pb-2" response={activeResponse} />
|
||||
) : (
|
||||
<HTMLOrTextViewer
|
||||
textViewerClassName="-mr-2 bg-surface" // Pull to the right
|
||||
response={activeResponse}
|
||||
pretty={viewMode === 'pretty'}
|
||||
/>
|
||||
)}
|
||||
</ConfirmLargeResponse>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
</TabContent>
|
||||
<TabContent value={TAB_HEADERS}>
|
||||
<ResponseHeaders response={activeResponse} />
|
||||
</TabContent>
|
||||
<TabContent value={TAB_INFO}>
|
||||
<ResponseInfo response={activeResponse} />
|
||||
</TabContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -25,41 +25,53 @@ export function ResponseHeaders({ response }: Props) {
|
||||
);
|
||||
return (
|
||||
<div className="overflow-auto h-full pb-4 gap-y-3 flex flex-col pr-0.5">
|
||||
<DetailsBanner
|
||||
storageKey={`${response.requestId}.request_headers`}
|
||||
summary={
|
||||
<h2 className="flex items-center">
|
||||
Request <CountBadge showZero count={requestHeaders.length} />
|
||||
</h2>
|
||||
}
|
||||
>
|
||||
{requestHeaders.length === 0 ? (
|
||||
<NoHeaders />
|
||||
) : (
|
||||
<KeyValueRows>
|
||||
{requestHeaders.map((h, i) => (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: none
|
||||
<KeyValueRow labelColor="primary" key={i} label={h.name}>
|
||||
{h.value}
|
||||
</KeyValueRow>
|
||||
))}
|
||||
</KeyValueRows>
|
||||
)}
|
||||
</DetailsBanner>
|
||||
<DetailsBanner
|
||||
defaultOpen
|
||||
storageKey={`${response.requestId}.response_headers`}
|
||||
summary={
|
||||
<h2 className="flex items-center">
|
||||
Response <CountBadge count={responseHeaders.length} />
|
||||
Response <CountBadge showZero count={responseHeaders.length} />
|
||||
</h2>
|
||||
}
|
||||
>
|
||||
<KeyValueRows>
|
||||
{responseHeaders.map((h, i) => (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: none
|
||||
<KeyValueRow labelColor="primary" key={i} label={h.name}>
|
||||
{h.value}
|
||||
</KeyValueRow>
|
||||
))}
|
||||
</KeyValueRows>
|
||||
</DetailsBanner>
|
||||
<DetailsBanner
|
||||
storageKey={`${response.requestId}.request_headers`}
|
||||
summary={
|
||||
<h2 className="flex items-center">
|
||||
Request <CountBadge count={requestHeaders.length} />
|
||||
</h2>
|
||||
}
|
||||
>
|
||||
<KeyValueRows>
|
||||
{requestHeaders.map((h, i) => (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: none
|
||||
<KeyValueRow labelColor="primary" key={i} label={h.name}>
|
||||
{h.value}
|
||||
</KeyValueRow>
|
||||
))}
|
||||
</KeyValueRows>
|
||||
{responseHeaders.length === 0 ? (
|
||||
<NoHeaders />
|
||||
) : (
|
||||
<KeyValueRows>
|
||||
{responseHeaders.map((h, i) => (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: none
|
||||
<KeyValueRow labelColor="primary" key={i} label={h.name}>
|
||||
{h.value}
|
||||
</KeyValueRow>
|
||||
))}
|
||||
</KeyValueRows>
|
||||
)}
|
||||
</DetailsBanner>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function NoHeaders() {
|
||||
return <span className="text-text-subtlest text-sm italic">No Headers</span>;
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ export function ResponseInfo({ response }: Props) {
|
||||
<div className="overflow-auto h-full pb-4">
|
||||
<KeyValueRows>
|
||||
<KeyValueRow labelColor="info" label="Version">
|
||||
{response.version}
|
||||
{response.version ?? <span className="text-text-subtlest">--</span>}
|
||||
</KeyValueRow>
|
||||
<KeyValueRow labelColor="info" label="Remote Address">
|
||||
{response.remoteAddr}
|
||||
{response.remoteAddr ?? <span className="text-text-subtlest">--</span>}
|
||||
</KeyValueRow>
|
||||
<KeyValueRow
|
||||
labelColor="info"
|
||||
|
||||
@@ -6,10 +6,12 @@ interface Props {
|
||||
count2?: number | true;
|
||||
className?: string;
|
||||
color?: Color;
|
||||
showZero?: boolean;
|
||||
}
|
||||
|
||||
export function CountBadge({ count, count2, className, color }: Props) {
|
||||
if (count === 0) return null;
|
||||
export function CountBadge({ count, count2, className, color, showZero }: Props) {
|
||||
if (count === 0 && !showZero) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
aria-hidden
|
||||
|
||||
@@ -678,7 +678,7 @@ export function PairEditorRow({
|
||||
size="xs"
|
||||
icon={isLast || disabled ? 'empty' : 'chevron_down'}
|
||||
title="Select form data type"
|
||||
className="text-text-subtle"
|
||||
className="text-text-subtlest"
|
||||
/>
|
||||
</Dropdown>
|
||||
)}
|
||||
@@ -798,7 +798,13 @@ function FileActionsDropdown({
|
||||
items={fileItems}
|
||||
itemsAfter={itemsAfter}
|
||||
>
|
||||
<IconButton iconSize="sm" size="xs" icon="chevron_down" title="Select form data type" />
|
||||
<IconButton
|
||||
iconSize="sm"
|
||||
size="xs"
|
||||
icon="chevron_down"
|
||||
title="Select form data type"
|
||||
className="text-text-subtlest"
|
||||
/>
|
||||
</RadioDropdown>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user