UNPKG

@flanksource/clicky-ui

Version:

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

236 lines (235 loc) 9.53 kB
import { jsxs, jsx } from "react/jsx-runtime"; import { useMemo, useState, useEffect } from "react"; import { parseJavaStackTrace } from "./stacktrace-parse.js"; import { loadShikiTransformers, highlightCode } from "../code-highlight.js"; import { Icon } from "../Icon.js"; function StackTrace({ input, language = "java", resolver, contextLines = 3, hideRuntimeOnly = false, include, exclude, className }) { const parsed = useMemo(() => { if (typeof input === "string") { return parseJavaStackTrace(input); } return input; }, [input, language]); const enrichedFrames = useMemo(() => { if (!resolver) return parsed.frames; return parsed.frames.map((frame) => { if (frame.sourceLines && frame.sourceLines.length > 0) return frame; const resolved = resolver(frame, contextLines); if (!resolved) return frame; const next = { ...frame, sourceLines: resolved.lines, sourceStartLine: resolved.startLine }; if (resolved.language !== void 0) next.sourceLanguage = resolved.language; return next; }); }, [parsed.frames, resolver, contextLines]); const visibleFrames = useMemo(() => { return enrichedFrames.filter((frame) => { if (hideRuntimeOnly && frame.runtime) return false; if (exclude && exclude.some((p) => { var _a; return (_a = frame.class) == null ? void 0 : _a.startsWith(p); })) return false; if (include && include.length > 0) { return include.some((p) => { var _a; return (_a = frame.class) == null ? void 0 : _a.startsWith(p); }); } return true; }); }, [enrichedFrames, hideRuntimeOnly, include, exclude]); if (visibleFrames.length === 0 && !parsed.exceptionClass) return null; const hasFilteredFrames = visibleFrames.length !== enrichedFrames.length; return /* @__PURE__ */ jsxs( "div", { className: [ "overflow-hidden rounded-md border border-border bg-background text-foreground", className ].filter(Boolean).join(" "), children: [ (parsed.exceptionClass || parsed.message) && /* @__PURE__ */ jsx("div", { className: "border-b border-border bg-red-500/5 px-3 py-2", children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [ /* @__PURE__ */ jsx( Icon, { name: "codicon:error", className: "mt-0.5 shrink-0 text-sm text-red-600 dark:text-red-400" } ), /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [ parsed.exceptionClass && /* @__PURE__ */ jsx("div", { className: "break-all font-mono text-xs font-semibold text-red-700 dark:text-red-300", children: parsed.exceptionClass }), parsed.message && /* @__PURE__ */ jsx("div", { className: "mt-0.5 whitespace-pre-wrap break-words text-xs text-foreground", children: parsed.message }) ] }) ] }) }), parsed.causedBy.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-1 border-b border-border bg-orange-500/5 px-3 py-2", children: parsed.causedBy.map((cause, i) => /* @__PURE__ */ jsxs( "div", { className: "flex min-w-0 items-start gap-2 font-mono text-[11px] text-orange-700 dark:text-orange-300", children: [ /* @__PURE__ */ jsx(Icon, { name: "codicon:debug-stackframe-dot", className: "mt-0.5 shrink-0 text-xs" }), /* @__PURE__ */ jsx("span", { className: "shrink-0 opacity-75", children: "Caused by" }), /* @__PURE__ */ jsx("span", { className: "min-w-0 break-all", children: cause }) ] }, i )) }), /* @__PURE__ */ jsx("div", { className: "divide-y divide-border/60", children: visibleFrames.map((frame, idx) => /* @__PURE__ */ jsx(FrameWithSource, { frame, index: idx }, `${frame.functionName}-${idx}`)) }), hasFilteredFrames && /* @__PURE__ */ jsxs("div", { className: "border-t border-border bg-muted/30 px-3 py-1.5 text-[11px] text-muted-foreground", children: [ "Showing ", visibleFrames.length, " of ", enrichedFrames.length, " frames" ] }) ] } ); } function FrameWithSource({ frame, index }) { const hasSource = frame.sourceLines && frame.sourceLines.length > 0; return /* @__PURE__ */ jsxs("div", { className: hasSource ? "bg-amber-500/[0.03]" : void 0, children: [ /* @__PURE__ */ jsx(StackFrameRow, { frame, index }), hasSource ? /* @__PURE__ */ jsx(SourceWindow, { frame }) : null ] }); } function StackFrameRow({ frame, index }) { const iconName = frame.nativeMethod ? "codicon:chip" : frame.runtime ? "codicon:debug-step-over" : "codicon:symbol-method"; const className = frame.class; const methodName = frame.displayName || frame.method || frame.functionName; return /* @__PURE__ */ jsxs( "div", { className: [ "grid grid-cols-[2rem_minmax(0,1fr)] gap-2 px-3 py-1.5 text-xs", frame.runtime ? "text-muted-foreground" : "text-foreground" ].join(" "), children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-end gap-1 pt-0.5 font-mono text-[10px] text-muted-foreground", children: [ /* @__PURE__ */ jsx("span", { children: index + 1 }), /* @__PURE__ */ jsx(Icon, { name: iconName, className: "mt-px shrink-0 text-[11px]" }) ] }), /* @__PURE__ */ jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-0.5", children: [ /* @__PURE__ */ jsx("span", { className: "min-w-0 break-all font-mono font-semibold leading-4", children: methodName }), className && /* @__PURE__ */ jsx("span", { className: "min-w-0 break-all font-mono text-[11px] text-muted-foreground", children: className }), frame.location && /* @__PURE__ */ jsx("span", { className: "rounded border border-border bg-muted/40 px-1.5 py-px font-mono text-[10px] text-muted-foreground", children: frame.location }) ] }) }) ] } ); } function inlineCommentPrefix(language) { switch (language) { case "python": case "py": case "bash": case "sh": case "shell": case "ruby": case "rb": case "yaml": case "yml": case "toml": return "#"; default: return "//"; } } function buildAnnotatedSource(lines, startLine, focalLine, language) { const focalIdx = focalLine - startLine; if (focalIdx < 0 || focalIdx >= lines.length) { return lines.join("\n"); } const prefix = inlineCommentPrefix(language); return lines.map((src, i) => i === focalIdx ? `${src} ${prefix} [!code error]` : src).join("\n"); } function SourceWindow({ frame }) { const start = frame.sourceStartLine ?? 0; const focal = frame.line ?? -1; const lines = frame.sourceLines ?? []; const lineNumbers = frame.sourceLineNumbers; const language = frame.sourceLanguage ?? "java"; const rows = useMemo( () => lines.map((src, i) => ({ source: src, lineNumber: (lineNumbers == null ? void 0 : lineNumbers[i]) ?? start + i })), [lineNumbers, lines, start] ); const hasExplicitLineNumbers = lineNumbers != null && lineNumbers.length > 0; const annotatedSource = useMemo( () => buildAnnotatedSource(lines, start, focal, language), [lines, start, focal, language] ); const [html, setHtml] = useState(null); useEffect(() => { if (!annotatedSource || hasExplicitLineNumbers) { setHtml(null); return; } let cancelled = false; loadShikiTransformers().then( ({ transformerNotationErrorLevel }) => highlightCode(annotatedSource, { lang: language, transformers: [transformerNotationErrorLevel()] }) ).then((out) => { if (!cancelled) setHtml(out); }); return () => { cancelled = true; }; }, [annotatedSource, hasExplicitLineNumbers, language]); const counterStart = start - 1; if (!html || hasExplicitLineNumbers) { return /* @__PURE__ */ jsx(SourceRows, { rows, focal }); } return /* @__PURE__ */ jsx( "div", { className: "stacktrace-source ml-6 overflow-x-auto rounded border border-border/50 bg-muted/30 p-2 text-[11px] leading-relaxed", style: { ["--shiki-start"]: counterStart }, dangerouslySetInnerHTML: { __html: html } } ); } function SourceRows({ rows, focal }) { return /* @__PURE__ */ jsx("div", { className: "mx-3 mb-2 ml-12 overflow-x-auto rounded border border-border/60 bg-muted/30 py-1 font-mono text-[11px] leading-relaxed", children: rows.map((row, i) => { const isFocal = row.lineNumber === focal; return /* @__PURE__ */ jsxs( "div", { className: [ "grid min-w-max grid-cols-[3.5rem_minmax(24rem,1fr)] gap-3 px-2", isFocal ? "bg-red-500/10 font-semibold text-red-800 dark:text-red-300" : "text-foreground" ].join(" "), children: [ /* @__PURE__ */ jsxs("span", { className: "select-none text-right text-muted-foreground", children: [ isFocal ? ">" : "", row.lineNumber ] }), /* @__PURE__ */ jsx("code", { className: "whitespace-pre", children: row.source || " " }) ] }, `${row.lineNumber}-${i}` ); }) }); } export { StackTrace }; //# sourceMappingURL=RenderedStackTrace.js.map