Add an export feature along with the mermaid diagram. Add sidebar to loading page.

This commit is contained in:
sabaimran
2025-01-08 23:53:58 -08:00
parent 889f34c7bf
commit ec02757fd1
2 changed files with 107 additions and 14 deletions

View File

@@ -1,4 +1,9 @@
import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
import { CircleNotch } from "@phosphor-icons/react"; import { CircleNotch } from "@phosphor-icons/react";
import { AppSidebar } from "../appSidebar/appSidebar";
import { Separator } from "@/components/ui/separator";
import { useIsMobileWidth } from "@/app/common/utils";
import { KhojLogoType } from "../logo/khojLogo";
interface LoadingProps { interface LoadingProps {
className?: string; className?: string;
@@ -7,12 +12,29 @@ interface LoadingProps {
} }
export default function Loading(props: LoadingProps) { export default function Loading(props: LoadingProps) {
const isMobileWidth = useIsMobileWidth();
return ( return (
// NOTE: We can display usage tips here for casual learning moments. // NOTE: We can display usage tips here for casual learning moments.
<SidebarProvider>
<AppSidebar conversationId={""} />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
{isMobileWidth ? (
<a className="p-0 no-underline" href="/">
<KhojLogoType className="h-auto w-16" />
</a>
) : (
<h2 className="text-lg">Ask Anything</h2>
)}
</header>
</SidebarInset>
<div <div
className={ className={
props.className || props.className ||
"bg-background opacity-50 flex items-center justify-center h-screen" "bg-background opacity-50 flex items-center justify-center h-full w-full fixed top-0 left-0 z-50"
} }
> >
<div> <div>
@@ -22,6 +44,7 @@ export default function Loading(props: LoadingProps) {
</span> </span>
</div> </div>
</div> </div>
</SidebarProvider>
); );
} }

View File

@@ -1,6 +1,7 @@
import React, { useEffect, useState, useRef } from "react"; import React, { useEffect, useState, useRef } from "react";
import mermaid from "mermaid"; import mermaid from "mermaid";
import { Info } from "@phosphor-icons/react"; import { Download, Info } from "@phosphor-icons/react";
import { Button } from "@/components/ui/button";
interface MermaidProps { interface MermaidProps {
chart: string; chart: string;
@@ -41,8 +42,71 @@ const Mermaid: React.FC<MermaidProps> = ({ chart }) => {
mermaid.contentLoaded(); mermaid.contentLoaded();
}, []); }, []);
const handleExport = async () => {
if (!elementRef.current) return;
try {
// Get SVG element
const svgElement = elementRef.current.querySelector("svg");
if (!svgElement) throw new Error("No SVG found");
// Get SVG viewBox dimensions
const viewBox = svgElement.getAttribute("viewBox")?.split(" ").map(Number) || [
0, 0, 0, 0,
];
const [, , viewBoxWidth, viewBoxHeight] = viewBox;
// Create canvas with viewBox dimensions
const canvas = document.createElement("canvas");
const scale = 2; // For better resolution
canvas.width = viewBoxWidth * scale;
canvas.height = viewBoxHeight * scale;
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("Failed to get canvas context");
// Convert SVG to data URL
const svgData = new XMLSerializer().serializeToString(svgElement);
const svgBlob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" });
const svgUrl = URL.createObjectURL(svgBlob);
// Create and load image
const img = new Image();
img.src = svgUrl;
await new Promise((resolve, reject) => {
img.onload = () => {
// Scale context for better resolution
ctx.scale(scale, scale);
ctx.drawImage(img, 0, 0, viewBoxWidth, viewBoxHeight);
canvas.toBlob((blob) => {
if (!blob) {
reject(new Error("Failed to create blob"));
return;
}
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `mermaid-diagram-${Date.now()}.png`;
a.click();
// Cleanup
URL.revokeObjectURL(url);
URL.revokeObjectURL(svgUrl);
resolve(true);
}, "image/png");
};
img.onerror = () => reject(new Error("Failed to load SVG"));
});
} catch (error) {
console.error("Error exporting diagram:", error);
setMermaidError("Failed to export diagram");
}
};
useEffect(() => { useEffect(() => {
console.log("Rendering mermaid chart:", chart);
if (elementRef.current) { if (elementRef.current) {
elementRef.current.removeAttribute("data-processed"); elementRef.current.removeAttribute("data-processed");
@@ -79,6 +143,12 @@ const Mermaid: React.FC<MermaidProps> = ({ chart }) => {
{chart} {chart}
</div> </div>
)} )}
{!mermaidError && (
<Button onClick={handleExport} variant={"secondary"} className="mt-3">
<Download className="w-5 h-5" />
Export as PNG
</Button>
)}
</div> </div>
); );
}; };