diff --git a/package.json b/package.json index 1cf2d45f..903e7930 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "lodash-es": "^4.17.21", "mermaid": "^10.8.0", "nanoid": "^5.0.5", + "nomnoml": "^1.6.2", "openai": "^4.26.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d1a43a50..bbbc2a54 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ dependencies: nanoid: specifier: ^5.0.5 version: 5.0.5 + nomnoml: + specifier: ^1.6.2 + version: 1.6.2 openai: specifier: ^4.26.1 version: 4.26.1 @@ -6564,6 +6567,10 @@ packages: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true + /graphre@0.1.3: + resolution: {integrity: sha512-F4OO/+BQj3R2UB3PQOGLgdAUy+SQuwy2A5cVGELAQDJlloSyyHqRe60ESFouRf8W0K+sm6gtWKotOwZoX3BL8w==} + dev: false + /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true @@ -8378,6 +8385,13 @@ packages: vm-browserify: 1.1.2 dev: true + /nomnoml@1.6.2: + resolution: {integrity: sha512-R3dA3FS8txilobpd2ZdnCuEwmXYYljxza2wR41YE/cIHMbt0eXsjlXHlAsD+F/fVe536f0WkAQ+VQFRC3hPZCA==} + hasBin: true + dependencies: + graphre: 0.1.3 + dev: false + /non-layered-tidy-tree-layout@2.0.2: resolution: {integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==} dev: false @@ -10723,6 +10737,7 @@ packages: /workbox-google-analytics@7.0.0: resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} + deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained dependencies: workbox-background-sync: 7.0.0 workbox-core: 7.0.0 diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx index f2d7aca1..67f27981 100644 --- a/src/components/Markdown.tsx +++ b/src/components/Markdown.tsx @@ -17,6 +17,7 @@ import oneLight from "react-syntax-highlighter/dist/esm/styles/hljs/atom-one-lig import CodeHeader from "./CodeHeader"; import HtmlPreview from "./HtmlPreview"; import MermaidPreview from "./MermaidPreview"; +import NomnomlPreview from "./NomnomlPreview"; const fixLanguageName = (language: string | null) => { if (!language) { @@ -108,6 +109,10 @@ function Markdown({ preview = ( ); + } else if (language === "nomnoml") { + preview = ( + + ); } } diff --git a/src/components/Message/AppMessage/Instructions.tsx b/src/components/Message/AppMessage/Instructions.tsx index c1ae0b30..f4be6eee 100644 --- a/src/components/Message/AppMessage/Instructions.tsx +++ b/src/components/Message/AppMessage/Instructions.tsx @@ -29,7 +29,7 @@ We think ChatCraft is the best platform for learning, experimenting, and getting | ------------------------------------- |:---------:|:-------:|:-------:| | Optimized for conversations about code | ✅ | ❌ | ❌ | | Work with models from multiple AI vendors | ✅ |❌ | ❌ | -| Previews for Mermaid Diagrams, HTML | ✅ | ❌ | ❌ | +| Previews for Mermaid/Nomnoml Diagrams, HTML | ✅ | ❌ | ❌ | | Edit Generated AI Replies | ✅ | ❌ | ✅ | | Use Custom System Prompts | ✅ | ✅ | ❌ | | Easy to retry with different AI models| ✅ | ❌ | ❌ | diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx new file mode 100644 index 00000000..a9e1e14c --- /dev/null +++ b/src/components/NomnomlPreview.tsx @@ -0,0 +1,85 @@ +import { memo, useCallback, useEffect, useRef, type ReactNode } from "react"; +import { Card, CardBody, IconButton, useClipboard } from "@chakra-ui/react"; +import { TbCopy } from "react-icons/tb"; +import { useAlert } from "../hooks/use-alert"; + +type NomnomlPreviewProps = { + children: ReactNode & ReactNode[]; +}; + +const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { + const { onCopy, value, setValue } = useClipboard(""); + const { info } = useAlert(); + const diagramRef = useRef(null); + const code = String(children); + + const handleCopy = useCallback(() => { + onCopy(); + info({ + title: "Copied to Clipboard", + message: "Nomnoml SVG diagram was copied to your clipboard.", + }); + }, [onCopy, info]); + + // Render the diagram as an SVG into our card's body + useEffect(() => { + const diagramDiv = diagramRef.current; + if (!diagramDiv) { + return; + } + + const fetchNomnoml = async () => { + try { + // Load nomnoml dynamically at runtime if needed + const nomnoml = await import("nomnoml"); + const svg = await nomnoml.renderSvg(code); + + setValue(svg); + + // Render nomnoml svg if successful + diagramDiv.innerHTML = svg; + + // Adjust the width of the SVG to fit the content inside the CardBody + const svgElement = diagramDiv.querySelector("svg"); + if (svgElement) { + svgElement.style.width = "100%"; + } + } catch (err: any) { + // When the diagram fails, use the error vs. diagram for copying (to debug) + setValue(err); + + // Render custom error message instead of error or blank content + const errMessage = `Error rendering Nomnoml diagram`; + diagramDiv.innerHTML = errMessage; + console.warn(`Error rendering nomnoml diagram`, err); + } + }; + fetchNomnoml(); + }, [diagramRef, code, setValue]); + + return ( + + } + color="gray.600" + _dark={{ color: "gray.300" }} + variant="ghost" + onClick={() => handleCopy()} + isDisabled={!value} + /> + + +
+ + + ); +}; + +// Memoize to reduce re-renders/flickering when content hasn't changed +export default memo(NomnomlPreview); diff --git a/src/lib/system-prompt.ts b/src/lib/system-prompt.ts index d28e3fad..1c2d894a 100644 --- a/src/lib/system-prompt.ts +++ b/src/lib/system-prompt.ts @@ -8,7 +8,7 @@ I follow these rules when responding: - Use GitHub flavored Markdown - ALWAYS include the programming language name (js) or type of data (csv) at the start of Markdown code blocks - Format ALL lines of code to 80 characters or fewer -- Use Mermaid diagrams when discussing visual topics +- Use Nomnoml or Mermaid diagrams when discussing visual topics - If using functions, only use the specific functions I have been provided with - If responding with math markdown, inline or otherwise, I use KaTeX syntax in math Markdown by enclosing EVERY mathematical expression, equation, variable, and formula with double-dollar signs \`($$)\`, for example: $$O(n\\log n)$$, $$1024 * 1024 = 1048576$$, $$1024^2$$, $$X$$ `;