newpr 1.0.25 → 1.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newpr",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"description": "AI-powered large PR review tool - understand PRs with 1000+ lines of changes",
|
|
5
5
|
"module": "src/cli/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"katex": "^0.16.28",
|
|
76
76
|
"lucide-react": "^0.567.0",
|
|
77
77
|
"meriyah": "^7.1.0",
|
|
78
|
+
"mermaid": "^11.12.3",
|
|
78
79
|
"react": "19.1.0",
|
|
79
80
|
"react-dom": "19.1.0",
|
|
80
81
|
"react-markdown": "^10.1.0",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { memo, useState, useEffect, useMemo } from "react";
|
|
1
|
+
import { memo, useState, useEffect, useMemo, isValidElement } from "react";
|
|
2
2
|
import ReactMarkdown from "react-markdown";
|
|
3
3
|
import remarkGfm from "remark-gfm";
|
|
4
4
|
import remarkMath from "remark-math";
|
|
@@ -211,6 +211,75 @@ function preprocess(text: string): string {
|
|
|
211
211
|
return processed.replace(/\x00MATH_BLOCK_(\d+)\x00/g, (_, idx) => mathBlocks[Number(idx)]!);
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
let mermaidCounter = 0;
|
|
215
|
+
let mermaidModule: typeof import("mermaid") | null = null;
|
|
216
|
+
let mermaidLoading: Promise<typeof import("mermaid")> | null = null;
|
|
217
|
+
|
|
218
|
+
function loadMermaid(): Promise<typeof import("mermaid")> {
|
|
219
|
+
if (mermaidModule) return Promise.resolve(mermaidModule);
|
|
220
|
+
if (!mermaidLoading) {
|
|
221
|
+
mermaidLoading = import("mermaid").then((m) => {
|
|
222
|
+
mermaidModule = m;
|
|
223
|
+
return m;
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return mermaidLoading;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function MermaidBlock({ code, dark }: { code: string; dark: boolean }) {
|
|
230
|
+
const [svg, setSvg] = useState<string>("");
|
|
231
|
+
const [error, setError] = useState(false);
|
|
232
|
+
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
let cancelled = false;
|
|
235
|
+
const id = `mermaid-${++mermaidCounter}`;
|
|
236
|
+
|
|
237
|
+
loadMermaid()
|
|
238
|
+
.then(({ default: mermaid }) => {
|
|
239
|
+
if (cancelled) return;
|
|
240
|
+
mermaid.initialize({
|
|
241
|
+
startOnLoad: false,
|
|
242
|
+
theme: dark ? "dark" : "default",
|
|
243
|
+
securityLevel: "loose",
|
|
244
|
+
fontFamily: "inherit",
|
|
245
|
+
});
|
|
246
|
+
return mermaid.render(id, code);
|
|
247
|
+
})
|
|
248
|
+
.then((result) => {
|
|
249
|
+
if (!cancelled && result) {
|
|
250
|
+
setSvg(result.svg);
|
|
251
|
+
setError(false);
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
.catch(() => {
|
|
255
|
+
if (!cancelled) setError(true);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
return () => {
|
|
259
|
+
cancelled = true;
|
|
260
|
+
};
|
|
261
|
+
}, [code, dark]);
|
|
262
|
+
|
|
263
|
+
if (error) {
|
|
264
|
+
return <code className="text-xs font-mono whitespace-pre-wrap">{code}</code>;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (!svg) {
|
|
268
|
+
return (
|
|
269
|
+
<div className="flex items-center justify-center py-6 text-xs text-muted-foreground/40">
|
|
270
|
+
Rendering diagram…
|
|
271
|
+
</div>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return (
|
|
276
|
+
<div
|
|
277
|
+
className="my-2 overflow-x-auto [&>svg]:mx-auto [&>svg]:max-w-full"
|
|
278
|
+
dangerouslySetInnerHTML={{ __html: svg }}
|
|
279
|
+
/>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
214
283
|
export const Markdown = memo(function Markdown({ children, onAnchorClick, activeId, streaming = false }: MarkdownProps) {
|
|
215
284
|
const processed = useMemo(() => preprocess(children), [children]);
|
|
216
285
|
const hl = useHighlighter();
|
|
@@ -234,6 +303,10 @@ export const Markdown = memo(function Markdown({ children, onAnchorClick, active
|
|
|
234
303
|
}
|
|
235
304
|
return <code className="px-1.5 py-0.5 rounded bg-muted text-xs font-mono">{children}</code>;
|
|
236
305
|
}
|
|
306
|
+
if (className?.includes("language-mermaid")) {
|
|
307
|
+
const raw = String(children).replace(/\n$/, "");
|
|
308
|
+
return <MermaidBlock code={raw} dark={dark} />;
|
|
309
|
+
}
|
|
237
310
|
const lang = langFromClassName(className);
|
|
238
311
|
if (lang && hl) {
|
|
239
312
|
const code = String(children).replace(/\n$/, "");
|
|
@@ -250,9 +323,14 @@ export const Markdown = memo(function Markdown({ children, onAnchorClick, active
|
|
|
250
323
|
}
|
|
251
324
|
return <code className="px-1.5 py-0.5 rounded bg-muted text-xs font-mono">{children}</code>;
|
|
252
325
|
},
|
|
253
|
-
pre: ({ children }) =>
|
|
254
|
-
|
|
255
|
-
|
|
326
|
+
pre: ({ children }) => {
|
|
327
|
+
if (isValidElement(children) && children.type === MermaidBlock) {
|
|
328
|
+
return <div className="rounded-lg border border-border/50 bg-muted/30 p-4 mb-3 overflow-x-auto">{children}</div>;
|
|
329
|
+
}
|
|
330
|
+
return (
|
|
331
|
+
<pre className="bg-muted rounded-lg p-4 overflow-x-auto mb-3 whitespace-pre text-xs font-mono [&>span>pre]:!bg-transparent [&>span>pre]:!p-0 [&>span>pre]:!m-0">{children}</pre>
|
|
332
|
+
);
|
|
333
|
+
},
|
|
256
334
|
span: ({ children, ...props }) => {
|
|
257
335
|
const allProps = props as Record<string, unknown>;
|
|
258
336
|
const lineRef = allProps["data-line-ref"] as string | undefined;
|