@flanksource/clicky-ui
Version:
Flanksource Clicky UI — React component library built on shadcn/ui with light/dark and density theming.
1,337 lines (1,336 loc) • 77.7 kB
JavaScript
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
import { QueryClient, QueryClientProvider, useQuery } from "@tanstack/react-query";
import { useState, useMemo, useEffect, createContext, useRef, useContext, Fragment as Fragment$1 } from "react";
import { FilterForm } from "../rpc/FilterForm.js";
import { parseJsonBody } from "../rpc/classify.js";
import { packParameterValues, pruneParameterValues } from "../rpc/formMetadata.js";
import { isPositionalParam } from "../rpc/types.js";
import { useOperations } from "../rpc/useOperations.js";
import { cn } from "../lib/utils.js";
import { DataTable } from "./DataTable.js";
import { Tree } from "./Tree.js";
import { Icon } from "./Icon.js";
import { JsonView } from "./JsonView.js";
import { highlightCode } from "./code-highlight.js";
import { StackTrace } from "./diagnostics/RenderedStackTrace.js";
import { Badge } from "./Badge.js";
import { HoverCard } from "../overlay/HoverCard.js";
import { Modal } from "../overlay/Modal.js";
import { TagList, normalizeTags, TagActionsProvider } from "./cells/TagList.js";
const CLICKY_PRIMARY_VIEW_FORMATS = ["clicky", "json"];
const CLICKY_OVERFLOW_VIEW_FORMATS = [
"pdf",
"html",
"markdown",
"yaml",
"csv",
"pretty",
"excel",
"slack"
];
const CLICKY_DOWNLOAD_FORMATS = ["json", "clicky", ...CLICKY_OVERFLOW_VIEW_FORMATS];
const clickyRuntimeContextDefault = {
operations: [],
operationsLoading: false
};
const ClickyRuntimeContext = createContext(clickyRuntimeContextDefault);
function Clicky(props) {
const [queryClient] = useState(
() => new QueryClient({
defaultOptions: {
queries: {
staleTime: 3e4,
retry: 1
}
}
})
);
const content = /* @__PURE__ */ jsx(
ClickyRuntimeProvider,
{
...props.commandRuntime ? { commandRuntime: props.commandRuntime } : {},
...props.onTableRowClick ? { onTableRowClick: props.onTableRowClick } : {},
...props.getTableRowHref ? { getTableRowHref: props.getTableRowHref } : {},
...props.isTableRowClickable ? { isTableRowClickable: props.isTableRowClickable } : {},
children: props.url ? /* @__PURE__ */ jsx(ClickyRemoteRenderer, { ...props, url: props.url }) : /* @__PURE__ */ jsx(
ClickyContent,
{
data: props.data,
...props.className ? { className: props.className } : {}
}
)
}
);
if (props.url || props.commandRuntime) {
return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: content });
}
return content;
}
function ClickyRuntimeProvider({
commandRuntime,
onTableRowClick,
getTableRowHref,
isTableRowClickable,
children
}) {
if (!commandRuntime) {
return /* @__PURE__ */ jsx(
ClickyRuntimeContext.Provider,
{
value: onTableRowClick || getTableRowHref || isTableRowClickable ? {
...clickyRuntimeContextDefault,
onTableRowClick,
getTableRowHref,
isTableRowClickable
} : clickyRuntimeContextDefault,
children
}
);
}
return /* @__PURE__ */ jsx(
ClickyCommandRuntimeProvider,
{
commandRuntime,
...onTableRowClick ? { onTableRowClick } : {},
...getTableRowHref ? { getTableRowHref } : {},
...isTableRowClickable ? { isTableRowClickable } : {},
children
}
);
}
function ClickyCommandRuntimeProvider({
commandRuntime,
onTableRowClick,
getTableRowHref,
isTableRowClickable,
children
}) {
const { operations, isLoading } = useOperations(commandRuntime.client);
const value = useMemo(
() => ({
commandRuntime,
onTableRowClick,
getTableRowHref,
isTableRowClickable,
operations,
operationsLoading: isLoading
}),
[commandRuntime, getTableRowHref, isLoading, isTableRowClickable, onTableRowClick, operations]
);
return /* @__PURE__ */ jsx(ClickyRuntimeContext.Provider, { value, children });
}
function ClickyContent({
data,
className
}) {
if (data === void 0) {
return /* @__PURE__ */ jsx(
ClickyNotice,
{
className,
title: "No Clicky data",
message: "Provide either a local data payload or a URL."
}
);
}
const parsed = parseClickyData(data);
if (!parsed.ok) {
return /* @__PURE__ */ jsx(ClickyInvalidPayload, { parsed, className });
}
return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx(ClickyNodeRenderer, { node: parsed.document.node }) });
}
function ClickyRemoteRenderer({
data,
url,
view,
download,
className
}) {
var _a, _b;
const availableViews = useMemo(() => getAvailableViews({ data, url, view }), [data, url, view]);
const primaryViews = useMemo(() => availableViews.filter(isPrimaryViewFormat), [availableViews]);
const overflowViews = useMemo(
() => availableViews.filter(isOverflowViewFormat),
[availableViews]
);
const downloadFormats = useMemo(() => getDownloadFormats({ url, download }), [download, url]);
const [activeView, setActiveView] = useState(
() => availableViews[0] ?? "clicky"
);
useEffect(() => {
if (!availableViews.includes(activeView)) {
setActiveView(availableViews[0] ?? "clicky");
}
}, [activeView, availableViews]);
const formattedUrl = buildFormatUrl(url, activeView);
const activeQuery = useQuery({
queryKey: ["clicky", activeView, formattedUrl],
enabled: shouldFetchRemoteView(activeView),
queryFn: async () => fetchRemoteFormat(formattedUrl, activeView)
});
const effectiveClickyData = activeView === "clicky" && ((_a = activeQuery.data) == null ? void 0 : _a.kind) === "text" ? activeQuery.data.text : data;
const fallbackJsonData = useMemo(() => parseJsonValue(data), [data]);
const effectiveJsonData = activeView === "json" && ((_b = activeQuery.data) == null ? void 0 : _b.kind) === "text" ? parseJsonValue(activeQuery.data.text) : fallbackJsonData;
const activeOverflowView = isOverflowViewFormat(activeView) ? activeView : null;
const canDownload = downloadFormats.length > 0;
const loadingMessage = `Fetching ${formattedUrl}`;
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-density-3", className), children: [
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 rounded-md border border-border bg-muted/20 px-density-3 py-density-2", children: [
primaryViews.length > 1 && /* @__PURE__ */ jsx("div", { role: "radiogroup", "aria-label": "Clicky view mode", className: "flex flex-wrap gap-1", children: primaryViews.map((mode) => /* @__PURE__ */ jsx(
"button",
{
type: "button",
role: "radio",
"aria-checked": activeView === mode,
onClick: () => setActiveView(mode),
className: cn(
"rounded-md border px-2.5 py-1 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
activeView === mode ? "border-foreground/20 bg-accent text-foreground" : "border-transparent text-muted-foreground hover:border-foreground/20 hover:text-foreground"
),
children: formatViewLabel(mode)
},
mode
)) }),
overflowViews.length > 0 && /* @__PURE__ */ jsx(
ClickyViewMenu,
{
activeFormat: activeOverflowView,
formats: overflowViews,
onSelect: setActiveView
}
),
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
shouldFetchRemoteView(activeView) && activeQuery.isFetching && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Refreshing…" }),
canDownload && /* @__PURE__ */ jsx(ClickyDownloadMenu, { url, formats: downloadFormats, label: download == null ? void 0 : download.label })
] })
] }),
activeView === "pdf" ? /* @__PURE__ */ jsx(ClickyPdfPreview, { src: formattedUrl }) : activeView === "html" ? /* @__PURE__ */ jsx(ClickyHtmlPreview, { src: formattedUrl }) : activeView === "excel" ? /* @__PURE__ */ jsx(
ClickyUnsupportedPreview,
{
title: "Excel preview",
message: "Excel output is available for download, but not inline preview.",
href: formattedUrl
}
) : activeView === "clicky" ? activeQuery.isPending && effectiveClickyData === void 0 ? /* @__PURE__ */ jsx(ClickyNotice, { title: "Loading Clicky", message: loadingMessage }) : activeQuery.isError && effectiveClickyData === void 0 ? /* @__PURE__ */ jsx(
ClickyNotice,
{
title: "Clicky request failed",
message: activeQuery.error instanceof Error ? activeQuery.error.message : "Request failed",
tone: "destructive"
}
) : /* @__PURE__ */ jsxs(Fragment, { children: [
activeQuery.isError && data !== void 0 && /* @__PURE__ */ jsx(
ClickyNotice,
{
title: "Remote refresh failed",
message: "Showing the local fallback payload instead.",
tone: "warning"
}
),
/* @__PURE__ */ jsx(ClickyContent, { data: effectiveClickyData })
] }) : activeView === "json" ? activeQuery.isPending && effectiveJsonData === void 0 ? /* @__PURE__ */ jsx(ClickyNotice, { title: "Loading JSON", message: loadingMessage }) : activeQuery.isError && effectiveJsonData === void 0 ? /* @__PURE__ */ jsx(
ClickyNotice,
{
title: "JSON request failed",
message: activeQuery.error instanceof Error ? activeQuery.error.message : "Request failed",
tone: "destructive"
}
) : /* @__PURE__ */ jsxs(Fragment, { children: [
activeQuery.isError && fallbackJsonData !== void 0 && /* @__PURE__ */ jsx(
ClickyNotice,
{
title: "Remote refresh failed",
message: "Showing the local fallback payload instead.",
tone: "warning"
}
),
/* @__PURE__ */ jsx(ClickyJsonTree, { value: effectiveJsonData })
] }) : activeQuery.isPending ? /* @__PURE__ */ jsx(ClickyNotice, { title: `Loading ${formatViewLabel(activeView)}`, message: loadingMessage }) : activeQuery.isError ? /* @__PURE__ */ jsx(
ClickyNotice,
{
title: `${formatViewLabel(activeView)} request failed`,
message: activeQuery.error instanceof Error ? activeQuery.error.message : "Request failed",
tone: "destructive"
}
) : /* @__PURE__ */ jsx(ClickyRemotePreview, { format: activeView, response: activeQuery.data })
] });
}
function ClickyViewMenu({
activeFormat,
formats,
onSelect
}) {
const rootRef = useRef(null);
const triggerRef = useRef(null);
const [open, setOpen] = useState(false);
useDismissablePopup(open, rootRef, triggerRef, () => setOpen(false));
return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: "relative", children: [
/* @__PURE__ */ jsx(
"button",
{
ref: triggerRef,
type: "button",
"aria-label": "Open additional view menu",
"aria-haspopup": "menu",
"aria-expanded": open,
"aria-pressed": activeFormat != null,
className: cn(
"inline-flex h-[34px] w-[34px] items-center justify-center rounded-md border border-input bg-background text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
activeFormat && "border-foreground/20 bg-accent text-foreground"
),
onClick: () => setOpen((current) => !current),
children: /* @__PURE__ */ jsx(Icon, { name: "codicon:ellipsis", className: "text-sm" })
}
),
open && /* @__PURE__ */ jsx(
"div",
{
role: "menu",
className: "absolute left-0 top-[calc(100%+0.375rem)] z-50 min-w-[18rem] rounded-md border border-border bg-popover p-1.5 text-popover-foreground shadow-lg shadow-black/5",
children: formats.map((format) => {
const active = format === activeFormat;
const meta = getRemoteFormatMeta(format);
return /* @__PURE__ */ jsxs(
"button",
{
type: "button",
role: "menuitemradio",
"aria-checked": active,
className: cn(
"flex w-full items-start gap-3 rounded-md px-3 py-2 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:outline-none",
active && "bg-accent/70"
),
onClick: () => {
onSelect(format);
setOpen(false);
},
children: [
/* @__PURE__ */ jsx(
Icon,
{
name: meta.icon,
className: cn(
"mt-0.5 shrink-0 text-base text-muted-foreground",
active && "text-foreground"
)
}
),
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
/* @__PURE__ */ jsxs("span", { className: "flex items-center justify-between gap-3", children: [
/* @__PURE__ */ jsx("span", { className: "font-medium", children: meta.label }),
active && /* @__PURE__ */ jsx(Icon, { name: "codicon:check", className: "text-xs text-muted-foreground" })
] }),
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: meta.description })
] })
]
},
format
);
})
}
)
] });
}
function ClickyDownloadMenu({
url,
formats,
label
}) {
const rootRef = useRef(null);
const triggerRef = useRef(null);
const [open, setOpen] = useState(false);
const primaryFormat = "json";
const primaryMeta = getRemoteFormatMeta(primaryFormat);
useDismissablePopup(open, rootRef, triggerRef, () => setOpen(false));
return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: "relative", children: [
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
/* @__PURE__ */ jsxs(
"button",
{
type: "button",
"aria-label": label ? `Download ${primaryMeta.label} ${label}` : `Download ${primaryMeta.label}`,
className: "inline-flex items-center gap-2 rounded-md border border-input bg-background px-3 py-1.5 text-xs font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
onClick: () => submitDownloadRequest(buildFormatUrl(url, primaryFormat)),
children: [
/* @__PURE__ */ jsx(Icon, { name: "codicon:cloud-download", className: "text-sm" }),
/* @__PURE__ */ jsx("span", { children: `Download ${primaryMeta.label}` })
]
}
),
/* @__PURE__ */ jsx(
"button",
{
ref: triggerRef,
type: "button",
"aria-label": "Open download menu",
"aria-haspopup": "menu",
"aria-expanded": open,
className: "inline-flex h-[34px] w-[34px] items-center justify-center rounded-md border border-input bg-background text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
onClick: () => setOpen((current) => !current),
children: /* @__PURE__ */ jsx(Icon, { name: open ? "codicon:chevron-up" : "codicon:chevron-down", className: "text-sm" })
}
)
] }),
open && /* @__PURE__ */ jsx(
"div",
{
role: "menu",
className: "absolute right-0 top-[calc(100%+0.375rem)] z-50 min-w-[18rem] rounded-md border border-border bg-popover p-1.5 text-popover-foreground shadow-lg shadow-black/5",
children: formats.map((format) => {
const meta = getRemoteFormatMeta(format);
return /* @__PURE__ */ jsxs(
"button",
{
type: "button",
role: "menuitem",
className: cn(
"flex w-full items-start gap-3 rounded-md px-3 py-2 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:outline-none"
),
onClick: () => {
submitDownloadRequest(buildFormatUrl(url, format));
setOpen(false);
},
children: [
/* @__PURE__ */ jsx(
Icon,
{
name: meta.icon,
className: "mt-0.5 shrink-0 text-base text-muted-foreground"
}
),
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
/* @__PURE__ */ jsx("span", { className: "font-medium", children: meta.label }),
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: meta.description })
] })
]
},
format
);
})
}
)
] });
}
function useDismissablePopup(open, rootRef, triggerRef, onClose) {
useEffect(() => {
if (!open) return;
const onPointerDown = (event) => {
var _a;
if (!((_a = rootRef.current) == null ? void 0 : _a.contains(event.target))) {
onClose();
}
};
const onKeyDown = (event) => {
var _a;
if (event.key === "Escape") {
onClose();
(_a = triggerRef.current) == null ? void 0 : _a.focus();
}
};
document.addEventListener("mousedown", onPointerDown);
document.addEventListener("keydown", onKeyDown);
return () => {
document.removeEventListener("mousedown", onPointerDown);
document.removeEventListener("keydown", onKeyDown);
};
}, [onClose, open, rootRef, triggerRef]);
}
function parseClickyData(data) {
if (typeof data === "string") {
try {
return normalizeClickyDocument(JSON.parse(data));
} catch (error) {
return {
ok: false,
message: error instanceof Error ? error.message : "Failed to parse JSON",
raw: data
};
}
}
return normalizeClickyDocument(data);
}
function fetchRemoteFormat(url, format) {
return fetch(url, {
headers: {
Accept: getRemoteFormatMeta(format).accept
}
}).then(async (response) => {
if (!response.ok) {
throw new Error(`Request failed with ${response.status} ${response.statusText}`.trim());
}
const contentType = response.headers.get("Content-Type") ?? "";
if (format === "excel") {
return {
kind: "blob",
blob: await response.blob(),
contentType
};
}
return {
kind: "text",
text: await response.text(),
contentType
};
});
}
function ClickyInvalidPayload({
parsed,
className
}) {
return /* @__PURE__ */ jsxs(
"div",
{
className: cn(
"rounded-md border border-destructive/30 bg-destructive/5 p-density-3",
className
),
children: [
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-destructive", children: "Invalid Clicky payload" }),
/* @__PURE__ */ jsxs("pre", { className: "mt-2 whitespace-pre-wrap break-all text-xs text-muted-foreground", children: [
parsed.message,
parsed.raw ? `
${parsed.raw}` : ""
] })
]
}
);
}
function ClickyNotice({
title,
message,
tone = "default",
className
}) {
return /* @__PURE__ */ jsxs(
"div",
{
className: cn(
"rounded-md border p-density-3",
tone === "destructive" && "border-destructive/30 bg-destructive/5",
tone === "warning" && "border-yellow-500/30 bg-yellow-500/5",
tone === "default" && "border-border bg-muted/30",
className
),
children: [
/* @__PURE__ */ jsx(
"div",
{
className: cn(
"text-sm font-medium",
tone === "destructive" && "text-destructive",
tone === "warning" && "text-yellow-700 dark:text-yellow-400"
),
children: title
}
),
/* @__PURE__ */ jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: message })
]
}
);
}
function ClickyPdfPreview({ src }) {
return /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-md border border-border bg-background", children: [
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-border px-3 py-2 text-xs text-muted-foreground", children: [
/* @__PURE__ */ jsx("span", { children: "PDF preview" }),
/* @__PURE__ */ jsx("a", { href: src, target: "_blank", rel: "noreferrer", className: "text-primary hover:underline", children: "Open in new tab" })
] }),
/* @__PURE__ */ jsx("iframe", { title: "Clicky PDF preview", src, className: "h-[720px] w-full bg-white" })
] });
}
function ClickyHtmlPreview({ src }) {
return /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-md border border-border bg-background", children: [
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-border px-3 py-2 text-xs text-muted-foreground", children: [
/* @__PURE__ */ jsx("span", { children: "HTML preview" }),
/* @__PURE__ */ jsx("a", { href: src, target: "_blank", rel: "noreferrer", className: "text-primary hover:underline", children: "Open in new tab" })
] }),
/* @__PURE__ */ jsx(
"iframe",
{
title: "Clicky HTML preview",
src,
className: "h-[720px] w-full bg-white",
sandbox: "allow-same-origin"
}
)
] });
}
function ClickyUnsupportedPreview({
title,
message,
href
}) {
return /* @__PURE__ */ jsx(
ClickyNotice,
{
title,
message: /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsxs("span", { children: [
message,
" "
] }),
/* @__PURE__ */ jsx("a", { href, target: "_blank", rel: "noreferrer", className: "text-primary hover:underline", children: "Open format" })
] })
}
);
}
function ClickyRemotePreview({
format,
response
}) {
const meta = getRemoteFormatMeta(format);
if (format === "slack") {
const parsed = (response == null ? void 0 : response.kind) === "text" ? parseJsonValue(response.text) : void 0;
return /* @__PURE__ */ jsx(ClickyJsonTree, { value: parsed, emptyLabel: meta.label });
}
return /* @__PURE__ */ jsx(
ClickyTextPreview,
{
title: `${meta.label} output`,
content: (response == null ? void 0 : response.kind) === "text" ? response.text : ""
}
);
}
function ClickyTextPreview({ title, content }) {
return /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-md border border-border bg-background", children: [
/* @__PURE__ */ jsx("div", { className: "border-b border-border px-3 py-2 text-xs text-muted-foreground", children: title }),
/* @__PURE__ */ jsx(
"pre",
{
"aria-label": "Clicky text preview",
className: "overflow-auto whitespace-pre-wrap break-words px-3 py-3 font-mono text-xs text-foreground",
children: content || "No content"
}
)
] });
}
function ClickyJsonTree({ value, emptyLabel = "JSON" }) {
const roots = useMemo(() => buildJsonTree(value), [value]);
if (value === void 0) {
return /* @__PURE__ */ jsx(
ClickyNotice,
{
title: `No ${emptyLabel} payload`,
message: "The response did not include a JSON body to inspect."
}
);
}
return /* @__PURE__ */ jsx(
"div",
{
"aria-label": "JSON tree",
className: "overflow-hidden rounded-md border border-border bg-background",
children: /* @__PURE__ */ jsx(
Tree,
{
roots,
className: "min-h-[12rem]",
showControls: roots.some((node) => {
var _a;
return (((_a = node.children) == null ? void 0 : _a.length) ?? 0) > 0;
}),
getKey: (node) => node.id,
getChildren: (node) => node.children,
renderRow: ({ node }) => /* @__PURE__ */ jsx(ClickyJsonTreeRow, { node }),
rowClass: () => "hover:bg-accent",
empty: /* @__PURE__ */ jsx("div", { className: "px-3 py-4 text-sm text-muted-foreground", children: "No JSON fields." }),
toolbarClassName: "pl-3"
}
)
}
);
}
function ClickyJsonTreeRow({ node }) {
const primitiveClass = typeof node.value === "string" ? "text-emerald-700 dark:text-emerald-400" : typeof node.value === "number" ? "text-sky-700 dark:text-sky-400" : typeof node.value === "boolean" || node.value === null ? "text-violet-700 dark:text-violet-400" : "text-muted-foreground";
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2 font-mono text-xs", children: [
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-foreground", children: node.key }),
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground", children: ":" }),
/* @__PURE__ */ jsx("span", { className: cn("min-w-0 break-words", primitiveClass), children: node.preview })
] });
}
function getAvailableViews({
data,
url,
view
}) {
const allowClicky = resolveViewConfigFlag(view, "clicky", data, url);
const allowJson = resolveViewConfigFlag(view, "json", data, url);
const next = [];
if (allowClicky && (url || data !== void 0)) {
next.push("clicky");
}
if (allowJson && (url || data !== void 0)) {
next.push("json");
}
for (const format of CLICKY_OVERFLOW_VIEW_FORMATS) {
if (resolveViewConfigFlag(view, format, data, url) && url) {
next.push(format);
}
}
if (next.length === 0 && (url || data !== void 0)) {
next.push("clicky");
}
return next;
}
function getDownloadFormats({
url,
download
}) {
const enabled = download ? download.all ?? true : !!url;
if (!enabled || !url) {
return [];
}
return [...CLICKY_DOWNLOAD_FORMATS];
}
function getRemoteFormatMeta(format) {
switch (format) {
case "clicky":
return {
label: "Clicky",
description: "Rendered Clicky JSON with the rich Clicky viewer",
icon: "codicon:preview",
accept: "application/json+clicky, application/clicky+json;q=0.9, application/json;q=0.8,*/*;q=0.7"
};
case "json":
return {
label: "JSON",
description: "Plain JSON for inspecting the raw response body",
icon: "vscode-icons:file-type-json",
accept: "application/json, text/plain;q=0.8,*/*;q=0.7"
};
case "pdf":
return {
label: "PDF",
description: "Portable document for sharing and printing",
icon: "vscode-icons:file-type-pdf2",
accept: "application/pdf, */*;q=0.7"
};
case "html":
return {
label: "HTML",
description: "Browser-ready HTML preview of the formatted output",
icon: "vscode-icons:file-type-html",
accept: "text/html, */*;q=0.7"
};
case "markdown":
return {
label: "Markdown",
description: "Markdown formatted for docs, comments, and chat",
icon: "vscode-icons:file-type-markdown",
accept: "text/markdown, text/plain;q=0.8,*/*;q=0.7"
};
case "yaml":
return {
label: "YAML",
description: "YAML for config-friendly inspection and export",
icon: "vscode-icons:file-type-yaml",
accept: "application/yaml, text/yaml;q=0.9, text/plain;q=0.8,*/*;q=0.7"
};
case "csv":
return {
label: "CSV",
description: "Comma-separated values for spreadsheets and imports",
icon: "codicon:table",
accept: "text/csv, text/plain;q=0.8,*/*;q=0.7"
};
case "pretty":
return {
label: "Pretty",
description: "Human-readable plain text output from the formatter",
icon: "codicon:file-code",
accept: "text/plain, */*;q=0.7"
};
case "excel":
return {
label: "Excel",
description: "Spreadsheet workbook for offline analysis",
icon: "codicon:table",
accept: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, */*;q=0.7"
};
case "slack":
return {
label: "Slack",
description: "Slack Block Kit JSON for chat-native output",
icon: "codicon:comment-discussion",
accept: "application/vnd.slack.block-kit+json, application/json;q=0.8,*/*;q=0.7"
};
}
}
function formatViewLabel(mode) {
return getRemoteFormatMeta(mode).label;
}
function buildFormatUrl(url, format) {
const base = typeof window === "undefined" ? "http://localhost" : window.location.origin;
const resolved = new URL(url, base);
resolved.searchParams.set("format", format === "clicky" ? "clicky-json" : format);
if (isAbsoluteUrl(url)) {
return resolved.toString();
}
return `${resolved.pathname}${resolved.search}${resolved.hash}`;
}
function shouldFetchRemoteView(format) {
return format !== "pdf" && format !== "html" && format !== "excel";
}
function isPrimaryViewFormat(format) {
return CLICKY_PRIMARY_VIEW_FORMATS.includes(format);
}
function isOverflowViewFormat(format) {
return CLICKY_OVERFLOW_VIEW_FORMATS.includes(format);
}
function resolveViewConfigFlag(view, format, data, url) {
const defaultEnabled = format === "clicky" || format === "json" ? url != null || data !== void 0 : !!url;
if (view === void 0) {
return defaultEnabled;
}
if (Array.isArray(view)) {
if (view.length === 0) {
return false;
}
if (format === "clicky") {
return view.includes("clicky") || view.includes("json");
}
return view.includes(format);
}
if (format in view) {
return Boolean(view[format]);
}
if (format === "clicky" && view.json === true) {
return true;
}
return defaultEnabled;
}
function parseJsonValue(data) {
if (data === void 0) {
return void 0;
}
if (typeof data !== "string") {
return data;
}
const trimmed = data.trim();
if (!trimmed) {
return "";
}
try {
return JSON.parse(trimmed);
} catch {
return data;
}
}
function buildJsonTree(value) {
if (Array.isArray(value)) {
return value.map((entry, index) => buildJsonTreeNode(String(index), entry, `$[${index}]`));
}
if (isPlainObject(value)) {
return Object.entries(value).map(([key, entry]) => buildJsonTreeNode(key, entry, `$.${key}`));
}
if (value === void 0) {
return [];
}
return [buildJsonTreeNode("$", value, "$")];
}
function buildJsonTreeNode(key, value, id) {
const children = getJsonChildren(value, id);
return {
id,
key,
value,
preview: summarizeJsonValue(value),
...children ? { children } : {}
};
}
function getJsonChildren(value, path) {
if (Array.isArray(value)) {
return value.map(
(entry, index) => buildJsonTreeNode(String(index), entry, `${path}[${index}]`)
);
}
if (isPlainObject(value)) {
return Object.entries(value).map(
([key, entry]) => buildJsonTreeNode(key, entry, `${path}.${key}`)
);
}
return void 0;
}
function summarizeJsonValue(value) {
if (Array.isArray(value)) {
return `[${value.length} item${value.length === 1 ? "" : "s"}]`;
}
if (isPlainObject(value)) {
const count = Object.keys(value).length;
return `{${count} key${count === 1 ? "" : "s"}}`;
}
if (typeof value === "string") {
return JSON.stringify(value);
}
if (value === void 0) {
return "undefined";
}
return String(value);
}
function isPlainObject(value) {
return value != null && typeof value === "object" && !Array.isArray(value);
}
function submitDownloadRequest(url) {
if (typeof document === "undefined") return;
const form = document.createElement("form");
form.method = "GET";
form.action = url;
form.style.display = "none";
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
}
function isAbsoluteUrl(url) {
return /^[a-z][a-z\d+\-.]*:/i.test(url) || url.startsWith("//");
}
function normalizeClickyDocument(data) {
if (!data || typeof data !== "object") {
return {
ok: false,
message: "Payload must be an object",
raw: String(data ?? "")
};
}
const candidate = data;
if ("version" in candidate && candidate.version === 1 && candidate.node && isClickyNode(candidate.node)) {
return { ok: true, document: candidate };
}
if (isClickyNode(candidate)) {
return { ok: true, document: { version: 1, node: candidate } };
}
return {
ok: false,
message: "Payload is neither a Clicky document nor a Clicky node",
raw: JSON.stringify(data, null, 2)
};
}
function isClickyNode(value) {
return !!value && typeof value === "object" && typeof value.kind === "string";
}
function ClickyNodeRenderer({ node }) {
if (!node) return null;
switch (node.kind) {
case "text":
return /* @__PURE__ */ jsx(ClickyText, { node });
case "link":
return /* @__PURE__ */ jsx(ClickyLinkNode, { node });
case "link-command":
return /* @__PURE__ */ jsx(ClickyLinkCommandNode, { node });
case "icon":
return /* @__PURE__ */ jsx(ClickyIconNode, { node });
case "list":
return /* @__PURE__ */ jsx(ClickyList, { node });
case "map":
return /* @__PURE__ */ jsx(ClickyMap, { node });
case "table":
return /* @__PURE__ */ jsx(ClickyTable, { node });
case "tree":
return /* @__PURE__ */ jsx(ClickyTreeNode, { node });
case "code":
return /* @__PURE__ */ jsx(ClickyCodeBlock, { node });
case "collapsed":
return /* @__PURE__ */ jsx(ClickyCollapsed, { node });
case "stacktrace":
return /* @__PURE__ */ jsx(ClickyStackTraceNode, { node });
case "button":
return /* @__PURE__ */ jsx(ClickyButtonNode, { node });
case "button-group":
return /* @__PURE__ */ jsx(ClickyButtonGroup, { node });
case "html":
return /* @__PURE__ */ jsx(ClickyHtmlNode, { node });
case "comment":
return /* @__PURE__ */ jsx(ClickyComment, { node });
case "badge":
return /* @__PURE__ */ jsx(ClickyBadgeNode, { node });
default:
return /* @__PURE__ */ jsx("pre", { className: "rounded-md border border-border bg-muted p-density-3 text-xs", children: JSON.stringify(node, null, 2) });
}
}
function ClickyComment({ node }) {
var _a, _b, _c, _d, _e;
const text = node.text ?? node.plain;
const content = /* @__PURE__ */ jsx(ClickyInlineContent, { node });
const maxWidth = ((_a = node.style) == null ? void 0 : _a.maxWidth) && node.style.maxWidth > 0 ? Math.min(node.style.maxWidth, 50) : 50;
const maxLines = ((_b = node.style) == null ? void 0 : _b.maxLines) && node.style.maxLines > 0 ? Math.min(node.style.maxLines, 3) : 3;
const inlineStyle = toInlineStyle(
{
...node.style,
maxWidth,
maxLines
},
text
);
if (!text && !((_c = node.children) == null ? void 0 : _c.length)) return null;
return /* @__PURE__ */ jsx(
"div",
{
className: cn("mt-1 text-xs leading-snug text-muted-foreground", (_d = node.style) == null ? void 0 : _d.className),
style: inlineStyle,
title: ((_e = node.tooltip) == null ? void 0 : _e.plain) ?? text,
children: content
}
);
}
function ClickyText({ node }) {
var _a, _b;
const inlineStyle = toInlineStyle(node.style, node.text ?? node.plain);
const content = /* @__PURE__ */ jsx(ClickyInlineContent, { node });
if (!node.style && !node.tooltip) {
return /* @__PURE__ */ jsx(Fragment, { children: content });
}
return /* @__PURE__ */ jsx(
"span",
{
style: inlineStyle,
title: (_a = node.tooltip) == null ? void 0 : _a.plain,
className: cn(
(_b = node.style) == null ? void 0 : _b.className,
(node.text ?? "").includes("\n") && "whitespace-pre-wrap"
),
children: content
}
);
}
function ClickyInlineContent({ node }) {
var _a;
return /* @__PURE__ */ jsxs(Fragment, { children: [
node.text,
(_a = node.children) == null ? void 0 : _a.map((child, index) => /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(ClickyNodeRenderer, { node: child }) }, index))
] });
}
function ClickyLinkNode({ node }) {
var _a, _b;
const content = /* @__PURE__ */ jsx(ClickyInlineContent, { node });
const inlineStyle = toInlineStyle(node.style, node.text ?? node.plain);
const target = browserAnchorTarget(node.target);
const rel = target === "_blank" ? "noopener noreferrer" : void 0;
if (!node.href) {
return /* @__PURE__ */ jsx("span", { style: inlineStyle, children: content });
}
return /* @__PURE__ */ jsx(
"a",
{
href: node.href,
target,
rel,
title: (_a = node.tooltip) == null ? void 0 : _a.plain,
style: inlineStyle,
className: cn((_b = node.style) == null ? void 0 : _b.className, "inline cursor-pointer"),
children: content
}
);
}
function ClickyLinkCommandNode({ node }) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const runtime = useContext(ClickyRuntimeContext);
const request = useMemo(() => clickyNodeToCommandRequest(node), [node]);
const resolved = useMemo(
() => resolveClickyCommand(request, runtime.commandRuntime, runtime.operations),
[request, runtime.commandRuntime, runtime.operations]
);
const target = request.target ?? "Dialog";
const hasRuntime = runtime.commandRuntime != null;
const [dialogOpen, setDialogOpen] = useState(false);
const [expanded, setExpanded] = useState(false);
if (!hasRuntime) {
return /* @__PURE__ */ jsx("span", { title: (_a = node.tooltip) == null ? void 0 : _a.plain, className: cn((_b = node.style) == null ? void 0 : _b.className), children: /* @__PURE__ */ jsx(ClickyInlineContent, { node }) });
}
if (target === "_self" || target === "_window" || target === "_tab") {
const href = ((_d = (_c = runtime.commandRuntime) == null ? void 0 : _c.hrefForCommand) == null ? void 0 : _d.call(_c, resolved)) ?? buildCommandExecutionHref(resolved);
if (!href) {
return /* @__PURE__ */ jsx("span", { title: (_e = node.tooltip) == null ? void 0 : _e.plain, className: cn((_f = node.style) == null ? void 0 : _f.className), children: /* @__PURE__ */ jsx(ClickyInlineContent, { node }) });
}
return /* @__PURE__ */ jsx(
"a",
{
href,
target: browserAnchorTarget(target),
rel: target === "_self" ? void 0 : "noopener noreferrer",
title: (_g = node.tooltip) == null ? void 0 : _g.plain,
style: toInlineStyle(node.style, node.text ?? node.plain),
className: cn((_h = node.style) == null ? void 0 : _h.className, "inline cursor-pointer"),
children: /* @__PURE__ */ jsx(ClickyInlineContent, { node })
}
);
}
if (target === "_clicky") {
return /* @__PURE__ */ jsx(
ClickyLinkCommandTrigger,
{
node,
onClick: () => {
var _a2, _b2;
return (_b2 = (_a2 = runtime.commandRuntime) == null ? void 0 : _a2.onNavigate) == null ? void 0 : _b2.call(_a2, resolved);
}
}
);
}
if (target === "Hover") {
return /* @__PURE__ */ jsx(
HoverCard,
{
delay: 75,
cardClassName: "w-[28rem] max-w-[calc(100vw-2rem)] whitespace-normal p-0",
trigger: /* @__PURE__ */ jsx(ClickyLinkCommandTrigger, { node }),
children: /* @__PURE__ */ jsx("div", { className: "max-h-[26rem] overflow-auto p-density-3", children: /* @__PURE__ */ jsx(ClickyAsyncCommandResult, { resolved }) })
}
);
}
if (target === "Expand") {
return /* @__PURE__ */ jsxs("span", { className: "inline", children: [
/* @__PURE__ */ jsx(
ClickyLinkCommandTrigger,
{
node,
ariaExpanded: expanded,
onClick: () => setExpanded((current) => !current)
}
),
expanded && /* @__PURE__ */ jsx("div", { className: "mt-2 rounded-md border border-border bg-background p-density-3", children: /* @__PURE__ */ jsx(ClickyAsyncCommandResult, { resolved }) })
] });
}
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(
ClickyLinkCommandTrigger,
{
node,
ariaExpanded: dialogOpen,
onClick: () => setDialogOpen(true)
}
),
/* @__PURE__ */ jsx(
ClickyCommandDialog,
{
open: dialogOpen,
onClose: () => setDialogOpen(false),
resolved
}
)
] });
}
function ClickyLinkCommandTrigger({
node,
onClick,
ariaExpanded
}) {
var _a, _b;
const inlineStyle = toInlineStyle(node.style, node.text ?? node.plain);
const handleClick = (event) => {
event.preventDefault();
onClick == null ? void 0 : onClick();
};
return /* @__PURE__ */ jsx(
"button",
{
type: "button",
title: (_a = node.tooltip) == null ? void 0 : _a.plain,
"aria-expanded": ariaExpanded,
onClick: handleClick,
style: inlineStyle,
className: cn(
"inline cursor-pointer appearance-none border-0 bg-transparent p-0 text-left align-baseline text-inherit",
(_b = node.style) == null ? void 0 : _b.className
),
children: /* @__PURE__ */ jsx(ClickyInlineContent, { node })
}
);
}
function ClickyCommandDialog({
open,
onClose,
resolved
}) {
const runtime = useContext(ClickyRuntimeContext);
const operation = resolved.operation;
const parameters = (operation == null ? void 0 : operation.operation.parameters) ?? [];
const initialValues = useMemo(
() => buildCommandParameterValues(parameters, resolved.request),
[parameters, resolved.request]
);
const [result, setResult] = useState(null);
const [error, setError] = useState("");
const [isExecuting, setIsExecuting] = useState(false);
const [hasAutoStarted, setHasAutoStarted] = useState(false);
const shouldAutoRun = open && resolved.request.autoRun === true && operation != null && missingRequiredParameters(parameters, initialValues).length === 0;
useEffect(() => {
if (!open) {
setHasAutoStarted(false);
setResult(null);
setError("");
}
}, [open, resolved.request.command]);
async function runCommand(values) {
if (!runtime.commandRuntime || !operation) {
return;
}
setIsExecuting(true);
setError("");
try {
const response = await executeClickyCommand(runtime.commandRuntime.client, operation, values);
setResult(response);
} catch (err) {
setResult(null);
setError(err instanceof Error ? err.message : String(err ?? "Unknown error"));
} finally {
setIsExecuting(false);
}
}
useEffect(() => {
if (!shouldAutoRun || hasAutoStarted) {
return;
}
setHasAutoStarted(true);
void runCommand(initialValues);
}, [hasAutoStarted, initialValues, shouldAutoRun]);
return /* @__PURE__ */ jsx(
Modal,
{
open,
onClose,
title: (operation == null ? void 0 : operation.operation.summary) || resolved.request.command,
size: "xl",
children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
operation ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 text-xs text-muted-foreground", children: [
/* @__PURE__ */ jsx("span", { className: "rounded-md border border-border bg-muted px-2 py-1 font-medium uppercase", children: operation.method }),
/* @__PURE__ */ jsx("code", { children: operation.path })
] }) : runtime.operationsLoading ? /* @__PURE__ */ jsx(ClickyNotice, { title: "Loading commands", message: "Resolving the command definition." }) : /* @__PURE__ */ jsx(
ClickyNotice,
{
title: "Unknown command",
message: `No operation matched "${resolved.request.command}".`,
tone: "destructive"
}
),
operation && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-border bg-muted/20 p-density-3", children: /* @__PURE__ */ jsx(
FilterForm,
{
client: runtime.commandRuntime.client,
path: operation.path,
method: operation.method,
parameters,
initialValues,
isSubmitting: isExecuting,
submitLabel: "Run command",
submittingLabel: "Running…",
onSubmit: runCommand
}
) }),
error ? /* @__PURE__ */ jsx(ClickyNotice, { title: "Command failed", message: error, tone: "destructive" }) : /* @__PURE__ */ jsx(
ClickyCommandResponse,
{
response: result,
pending: isExecuting,
emptyMessage: "Run the command to load a Clicky response."
}
)
] })
}
);
}
function ClickyAsyncCommandResult({ resolved }) {
const runtime = useContext(ClickyRuntimeContext);
const operation = resolved.operation;
const parameters = (operation == null ? void 0 : operation.operation.parameters) ?? [];
const initialValues = useMemo(
() => buildCommandParameterValues(parameters, resolved.request),
[parameters, resolved.request]
);
const missing = useMemo(
() => missingRequiredParameters(parameters, initialValues),
[initialValues, parameters]
);
const query = useQuery({
queryKey: [
"clicky-command",
resolved.request.command,
operation == null ? void 0 : operation.method,
operation == null ? void 0 : operation.path,
initialValues
],
enabled: runtime.commandRuntime != null && operation != null && missing.length === 0,
retry: 0,
queryFn: async () => executeClickyCommand(runtime.commandRuntime.client, operation, initialValues)
});
if (!runtime.commandRuntime) {
return /* @__PURE__ */ jsx(
ClickyNotice,
{
title: "Command runtime unavailable",
message: "This Clicky view was rendered without a command runtime.",
tone: "warning"
}
);
}
if (!operation) {
if (runtime.operationsLoading) {
return /* @__PURE__ */ jsx(ClickyNotice, { title: "Loading commands", message: "Resolving the command definition." });
}
return /* @__PURE__ */ jsx(
ClickyNotice,
{
title: "Unknown command",
message: `No operation matched "${resolved.request.command}".`,
tone: "destructive"
}
);
}
if (missing.length > 0) {
return /* @__PURE__ */ jsx(
ClickyNotice,
{
title: "Missing required parameters",
message: `This link is missing: ${missing.map((param) => prettifyName(param.name)).join(", ")}.`,
tone: "warning"
}
);
}
if (query.isPending) {
return /* @__PURE__ */ jsx(ClickyNotice, { title: "Running command", message: "Loading Clicky response…" });
}
if (query.isError) {
return /* @__PURE__ */ jsx(
ClickyNotice,
{
title: "Command failed",
message: query.error instanceof Error ? query.error.message : "Request failed",
tone: "destructive"
}
);
}
return /* @__PURE__ */ jsx(ClickyCommandResponse, { response: query.data, emptyMessage: "No response body returned." });
}
function ClickyCommandResponse({
response,
pending = false,
emptyMessage
}) {
if (pending) {
return /* @__PURE__ */ jsx(ClickyNotice, { title: "Running command", message: "Loading Clicky response…" });
}
if (!response) {
return /* @__PURE__ */ jsx(ClickyNotice, { title: "No response", message: emptyMessage });
}
const parsedPayload = isExecutionResponse(response) ? response.parsed ?? parseJsonBody(response) : response;
const rawText = isExecutionResponse(response) ? response.stdout || response.output || response.message || "" : typeof response === "string" ? response : JSON.stringify(response, null, 2);
const clickyPayload = typeof parsedPayload === "string" || parsedPayload != null && typeof parsedPayload === "object" ? parsedPayload : rawText;
const parsedClicky = clickyPayload === "" ? null : parseClickyData(clickyPayload);
if (parsedClicky == null ? void 0 : parsedClicky.ok) {
return /* @__PURE__ */ jsx("div", { className: "rounded-md border border-border bg-background p-density-3", children: /* @__PURE__ */ jsx(ClickyNodeRenderer, { node: parsedClicky.document.node }) });
}
if (parsedPayload != null && typeof parsedPayload === "object") {
return /* @__PURE__ */ jsx(ClickyJsonTree, { value: parsedPayload });
}
return /* @__PURE__ */ jsx(ClickyTextPreview, { title: "Response body", content: rawText });
}
function ClickyIconNode({ node }) {
var _a;
const inlineStyle = toInlineStyle(node.style, node.plain ?? node.unicode);
return /* @__PURE__ */ jsx("span", { style: inlineStyle, title: (_a = node.tooltip) == null ? void 0 : _a.plain, className: "inline-flex items-center", children: node.iconify ? /* @__PURE__ */ jsx(Icon, { name: node.iconify }) : /* @__PURE__ */ jsx("span", { children: node.unicode ?? node.plain }) });
}
function ClickyList({ node }) {
const items = node.items ?? [];
if (items.length === 0) return null;
if (node.inline) {
return /* @__PURE__ */ jsx("span", { className: "inline-flex flex-wrap items-start gap-1", children: items.map((item, index) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
index > 0 && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "," }),
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
!node.ordered && node.bullet && /* @__PURE__ */ jsx(ClickyNodeRenderer, { node: node.bull