diff --git a/src/components/workspace/request-editor.tsx b/src/components/workspace/request-editor.tsx index 266175e..a5f9a4a 100644 --- a/src/components/workspace/request-editor.tsx +++ b/src/components/workspace/request-editor.tsx @@ -219,6 +219,15 @@ function KVTable({ ); } +interface HistoryInitialData { + method: HttpMethod; + url: string; + headers: KVRow[]; + params: KVRow[]; + bodyType: string; + bodyContent: string; +} + interface RequestEditorProps { requestId: string | null; collectionRelPath: string | null; @@ -226,6 +235,7 @@ interface RequestEditorProps { environments?: EnvFile[]; onOpenEnvTab?: (envName: string) => void; onHistoryEntry?: (entry: HistoryEntry) => void; + historyInitialData?: HistoryInitialData; } const AUTH_TYPES: { value: AuthConfig["type"]; label: string }[] = [ @@ -235,7 +245,7 @@ const AUTH_TYPES: { value: AuthConfig["type"]; label: string }[] = [ { value: "apikey", label: "API Key" }, ]; -export function RequestEditor({ requestId, collectionRelPath, workspacePath, environments, onOpenEnvTab, onHistoryEntry }: RequestEditorProps) { +export function RequestEditor({ requestId, collectionRelPath, workspacePath, environments, onOpenEnvTab, onHistoryEntry, historyInitialData }: RequestEditorProps) { const [details, setDetails] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); @@ -310,6 +320,19 @@ export function RequestEditor({ requestId, collectionRelPath, workspacePath, env }; }, [requestId, collectionRelPath, workspacePath]); + // Initialize from history data when opened from history panel + useEffect(() => { + if (!historyInitialData) return; + setMethod(historyInitialData.method); + setUrl(historyInitialData.url); + setHeaders(historyInitialData.headers); + setParams(historyInitialData.params); + setBodyType(historyInitialData.bodyType); + setBodyContent(historyInitialData.bodyContent); + setResponseData(null); + setSendError(null); + }, [historyInitialData]); + // Load auth config when request changes useEffect(() => { if (!requestId) { @@ -566,6 +589,9 @@ export function RequestEditor({ requestId, collectionRelPath, workspacePath, env statusText: result.statusText, timeMs: result.timeMs, timestamp: new Date().toISOString(), + headers: headers.filter((h) => h.key.trim() !== "" || h.value.trim() !== ""), + params: params.filter((p) => p.key.trim() !== "" || p.value.trim() !== ""), + body: { type: bodyType as "none" | "json" | "text" | "form-data" | "x-www-form-urlencoded", content: bodyContent }, }); } catch (err: unknown) { let msg: string; @@ -592,6 +618,9 @@ export function RequestEditor({ requestId, collectionRelPath, workspacePath, env statusText: null, timeMs: null, timestamp: new Date().toISOString(), + headers: headers.filter((h) => h.key.trim() !== "" || h.value.trim() !== ""), + params: params.filter((p) => p.key.trim() !== "" || p.value.trim() !== ""), + body: { type: bodyType as "none" | "json" | "text" | "form-data" | "x-www-form-urlencoded", content: bodyContent }, }); } finally { if (unlistenProgress) unlistenProgress(); @@ -688,7 +717,7 @@ export function RequestEditor({ requestId, collectionRelPath, workspacePath, env { id: "settings", label: "Settings" }, ]; - if (!requestId) { + if (!requestId && !historyInitialData) { return (
diff --git a/src/components/workspace/request-tab-bar.tsx b/src/components/workspace/request-tab-bar.tsx index a336d20..0dbd093 100644 --- a/src/components/workspace/request-tab-bar.tsx +++ b/src/components/workspace/request-tab-bar.tsx @@ -1,7 +1,7 @@ "use client"; import { cn } from "@/lib/utils"; -import type { HttpMethod } from "@/lib/types"; +import type { HistoryEntry, HttpMethod } from "@/lib/types"; import { BookOpen, Globe, Plus, X } from "lucide-react"; import { useState, useRef, useEffect, useCallback } from "react"; @@ -48,7 +48,16 @@ export interface CollectionDocTabItem { isDirty?: boolean; } -export type TabItem = RequestTabItem | EnvironmentTabItem | FolderReadmeTabItem | CollectionDocTabItem; +export interface HistoryRequestTabItem { + id: string; + kind: "history-request"; + name: string; + method: HttpMethod; + historyEntry: HistoryEntry; + isDirty?: boolean; +} + +export type TabItem = RequestTabItem | EnvironmentTabItem | FolderReadmeTabItem | CollectionDocTabItem | HistoryRequestTabItem; interface RequestTabBarProps { tabs: TabItem[]; @@ -155,7 +164,7 @@ export function RequestTabBar({ {activeTabId === tab.id && (
)} - {tab.kind === "request" ? ( + {(tab.kind === "request" || tab.kind === "history-request") ? ( {tab.method} diff --git a/src/components/workspace/workspace-view.tsx b/src/components/workspace/workspace-view.tsx index 187fd80..f8d230e 100644 --- a/src/components/workspace/workspace-view.tsx +++ b/src/components/workspace/workspace-view.tsx @@ -30,9 +30,9 @@ import { updateEnvVariable, type EnvFile } from "@/lib/environments"; -import { addHistoryEntry, clearRequestHistory, getRequestHistory } from "@/lib/settings"; +import { addHistoryEntry, clearRequestHistory, deleteHistoryEntry, getRequestHistory } from "@/lib/settings"; import type { HistoryEntry } from "@/lib/types"; -import { Loader2, Plus, Trash2 } from "lucide-react"; +import { Loader2, Plus, Trash2, X } from "lucide-react"; import { useCallback, useEffect, useMemo, useState } from "react"; import { ActivityBar, type ActivityTab } from "./activity-bar"; import { CollectionDocView } from "./collection-doc-view"; @@ -47,6 +47,7 @@ import { type CollectionDocTabItem, type EnvironmentTabItem, type FolderReadmeTabItem, + type HistoryRequestTabItem, type RequestTabItem, type TabItem, } from "./request-tab-bar"; @@ -129,6 +130,29 @@ function WorkspaceContent() { setHistory([]); }, []); + const handleDeleteHistoryEntry = useCallback(async (entryId: string) => { + const updated = await deleteHistoryEntry(entryId); + setHistory(updated); + }, []); + + const handleSelectHistoryEntry = useCallback( + (entry: HistoryEntry) => { + const tabId = `history-${entry.id}`; + if (!openTabs.some((t) => t.id === tabId)) { + const newTab: HistoryRequestTabItem = { + id: tabId, + kind: "history-request", + name: entry.url, + method: entry.method, + historyEntry: entry, + }; + setOpenTabs((prev) => [...prev, newTab]); + } + setActiveTabId(tabId); + }, + [openTabs] + ); + const loadEnvs = useCallback(async () => { if (!folderPath) return; try { @@ -710,10 +734,14 @@ function WorkspaceContent() { ) : (
{history.map((entry) => ( -
- + +
))}
)} @@ -831,6 +869,24 @@ function WorkspaceContent() { environments={environments} onOpenEnvTab={handleSelectEnv} /> + ) : activeTab?.kind === "history-request" ? ( + ) : ( { + try { + const store = await getStore(); + if (!store) return []; + + const existing = + (await store.get(KEYS.REQUEST_HISTORY)) ?? []; + const updated = existing.filter((e) => e.id !== entryId); + await store.set(KEYS.REQUEST_HISTORY, updated); + await store.save(); + return updated; + } catch { + return []; + } +} + export async function clearRequestHistory(): Promise { try { const store = await getStore(); diff --git a/src/lib/types.ts b/src/lib/types.ts index 63b782b..c2233f7 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -84,4 +84,7 @@ export interface HistoryEntry { statusText: string | null; timeMs: number | null; timestamp: string; // ISO 8601 + headers: RequestHeader[]; + params: QueryParam[]; + body: RequestBody; }