UNPKG

@flanksource/clicky-ui

Version:

Flanksource Clicky UI — React component library built on shadcn/ui with light/dark and density theming.

176 lines (175 loc) 6.18 kB
import { jsx, jsxs, Fragment } from "react/jsx-runtime"; import { useMemo } from "react"; import { DataTable } from "../DataTable.js"; import { JsonView } from "../JsonView.js"; const COLS = [ { key: "request.method", label: "Method", shrink: true }, { key: "request.url", label: "URL", grow: true, render: (value) => /* @__PURE__ */ jsx("span", { title: String(value ?? ""), children: String(value ?? "") }) }, { key: "response.status", label: "Status", shrink: true, render: (value) => /* @__PURE__ */ jsx("span", { className: statusColor(Number(value ?? 0)), children: String(value ?? "") }), sortValue: (value) => Number(value ?? 0) }, { key: "time", label: "Time", align: "right", shrink: true, render: (value) => `${Number(value ?? 0).toFixed(0)}ms`, sortValue: (value) => Number(value ?? 0) }, { key: "response.bodySize", label: "Size", align: "right", shrink: true, render: (value) => formatBytes(Number(value ?? 0)), sortValue: (value) => Number(value ?? 0) }, { key: "response.content.mimeType", label: "Type", shrink: true } ]; function HarPanel({ entries, search, emptyLabel = "No HTTP traffic captured", className }) { const filteredEntries = useMemo(() => { if (search === void 0 || search.trim() === "") return entries; return entries.filter((entry) => matchesSearch(search.trim().toLowerCase(), entry)); }, [entries, search]); if (!entries || entries.length === 0) { return /* @__PURE__ */ jsx("div", { className: "p-density-6 text-center text-muted-foreground text-sm", children: emptyLabel }); } return /* @__PURE__ */ jsx("div", { className: `h-full ${className ?? ""}`, children: /* @__PURE__ */ jsx( DataTable, { data: filteredEntries, columns: COLS, autoFilter: true, showGlobalFilter: search === void 0, globalFilterPlaceholder: "Filter URL, method, or body…", defaultSort: { key: "time", dir: "asc" }, emptyMessage: emptyLabel, getRowId: (entry, index) => `${entry.startedDateTime ?? index}-${entry.request.method}-${entry.request.url}-${entry.time}`, renderExpandedRow: (entry) => /* @__PURE__ */ jsx(HarRowDetails, { entry }) } ) }); } function HarRowDetails({ entry }) { var _a, _b; return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-density-4", children: [ /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsx( HeaderList, { title: "Request Headers", ...entry.request.headers ? { headers: entry.request.headers } : {} } ), ((_a = entry.request.postData) == null ? void 0 : _a.text) && /* @__PURE__ */ jsxs("div", { className: "mt-density-2", children: [ /* @__PURE__ */ jsx("div", { className: "font-semibold text-foreground mb-1", children: "Request Body" }), /* @__PURE__ */ jsx("div", { className: "bg-background p-density-2 rounded border border-border overflow-auto max-h-48", children: /* @__PURE__ */ jsx( BodyView, { text: entry.request.postData.text, ...entry.request.postData.mimeType ? { mimeType: entry.request.postData.mimeType } : {} } ) }) ] }) ] }), /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx( HeaderList, { title: "Response Headers", ...entry.response.headers ? { headers: entry.response.headers } : {} } ) }) ] }), ((_b = entry.response.content) == null ? void 0 : _b.text) && /* @__PURE__ */ jsxs("div", { className: "mt-density-3", children: [ /* @__PURE__ */ jsx("div", { className: "font-semibold text-foreground mb-1", children: "Response Body" }), /* @__PURE__ */ jsx("div", { className: "bg-background p-density-2 rounded border border-border overflow-auto max-h-64", children: /* @__PURE__ */ jsx( BodyView, { text: entry.response.content.text, ...entry.response.content.mimeType ? { mimeType: entry.response.content.mimeType } : {} } ) }) ] }) ] }); } function HeaderList({ title, headers }) { return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("div", { className: "font-semibold text-foreground mb-1", children: title }), /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: headers == null ? void 0 : headers.map((h, i) => /* @__PURE__ */ jsxs("div", { className: "whitespace-nowrap", children: [ /* @__PURE__ */ jsxs("span", { className: "text-purple-600 dark:text-purple-400", children: [ h.name, ":" ] }), " ", h.value ] }, i)) }) ] }); } function BodyView({ text, mimeType }) { if (isJsonType(mimeType)) { const parsed = tryParseJson(text); if (parsed !== null) return /* @__PURE__ */ jsx(JsonView, { data: parsed }); } return /* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap break-all", children: text }); } function tryParseJson(text) { try { return JSON.parse(text); } catch { return null; } } function isJsonType(mime) { return !!mime && (mime.includes("json") || mime.includes("javascript")); } function matchesSearch(needle, entry) { var _a, _b; return [ entry.request.method, entry.request.url, (_a = entry.request.postData) == null ? void 0 : _a.text, (_b = entry.response.content) == null ? void 0 : _b.text ].some((value) => !!value && value.toLowerCase().includes(needle)); } function formatBytes(bytes) { if (bytes < 0) return ""; if (bytes < 1024) return `${bytes}B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`; return `${(bytes / 1024 / 1024).toFixed(1)}MB`; } function statusColor(status) { if (status >= 500) return "text-red-600"; if (status >= 400) return "text-amber-600"; if (status >= 300) return "text-blue-600"; if (status >= 200) return "text-green-600"; return "text-muted-foreground"; } export { HarPanel }; //# sourceMappingURL=HarPanel.js.map