Improve response history menu (#492)

This commit is contained in:
Gregory Schier
2026-07-02 10:04:57 -07:00
committed by GitHub
parent 9b524e3dc7
commit 95ac3e310a
12 changed files with 351 additions and 75 deletions
@@ -1,9 +1,12 @@
import type { HttpResponse } from "@yaakapp-internal/models";
import { useMemo, useState } from "react";
import { useCopyHttpResponse } from "../../hooks/useCopyHttpResponse";
import { useResponseBodyText } from "../../hooks/useResponseBodyText";
import { useSaveResponse } from "../../hooks/useSaveResponse";
import { languageFromContentType } from "../../lib/contentType";
import { getContentTypeFromHeaders } from "../../lib/model_util";
import type { EditorProps } from "../core/Editor/Editor";
import { IconButton } from "../core/IconButton";
import { EmptyStateText } from "../EmptyStateText";
import { TextViewer } from "./TextViewer";
import { WebPageViewer } from "./WebPageViewer";
@@ -51,6 +54,9 @@ interface HttpTextViewerProps {
function HttpTextViewer({ response, text, language, pretty, className }: HttpTextViewerProps) {
const [currentFilter, setCurrentFilter] = useState<string | null>(null);
const filteredBody = useResponseBodyText({ response, filter: currentFilter });
const saveResponse = useSaveResponse(response);
const copyResponse = useCopyHttpResponse(response);
const actionsDisabled = response.state !== "closed" && response.status >= 100;
const filterCallback = useMemo(
() => (filter: string) => {
@@ -72,6 +78,26 @@ function HttpTextViewer({ response, text, language, pretty, className }: HttpTex
filterStateKey={`response.body.${response.requestId}`}
pretty={pretty}
className={className}
footerActions={[
<IconButton
key="save"
size="sm"
icon="save"
title="Save response to file"
disabled={actionsDisabled}
onClick={() => saveResponse.mutate()}
className="border !border-border-subtle"
/>,
<IconButton
key="copy"
size="sm"
icon="copy"
title="Copy response body"
disabled={actionsDisabled}
onClick={() => copyResponse.mutate()}
className="border !border-border-subtle"
/>,
]}
onFilter={filterCallback}
/>
);
@@ -1,6 +1,6 @@
import classNames from "classnames";
import type { ReactNode } from "react";
import { useCallback, useMemo } from "react";
import { Children, useCallback, useMemo } from "react";
import { createGlobalState } from "react-use";
import { useDebouncedValue } from "@yaakapp-internal/ui";
import { useFormatText } from "../../hooks/useFormatText";
@@ -19,6 +19,7 @@ interface Props {
filterStateKey?: string | null;
pretty?: boolean;
className?: string;
footerActions?: ReactNode;
onFilter?: (filter: string) => {
data: string | null | undefined;
isPending: boolean;
@@ -35,6 +36,7 @@ export function TextViewer({
filterStateKey,
pretty,
className,
footerActions,
onFilter,
}: Props) {
const filterKey = filterStateKey ?? stateKey;
@@ -66,7 +68,7 @@ export function TextViewer({
const canFilter = onFilter && (language === "json" || language === "xml" || language === "html");
const actions = useMemo<ReactNode[]>(() => {
const nodes: ReactNode[] = [];
const nodes: ReactNode[] = isSearching ? [] : Children.toArray(footerActions);
if (!canFilter) return nodes;
@@ -107,6 +109,7 @@ export function TextViewer({
return nodes;
}, [
canFilter,
footerActions,
filterKey,
filterText,
filteredResponse.error,