mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-09 13:25:11 +00:00
Add an export feature along with the mermaid diagram. Add sidebar to loading page.
This commit is contained in:
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user