From c24dad4fb79ff4504a6d755421464f64f2ccbe48 Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Wed, 20 Mar 2024 22:26:12 -0400 Subject: [PATCH 01/18] added nomnoml --- package.json | 1 + 1 file changed, 1 insertion(+) 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", From ea81319880e3a601b9f39d7a6043a4e1491c1a3d Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Thu, 21 Mar 2024 11:05:57 -0400 Subject: [PATCH 02/18] began drafting NomnomlPreview.tsx --- src/components/NomnomlPreview.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/components/NomnomlPreview.tsx diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx new file mode 100644 index 00000000..bf627815 --- /dev/null +++ b/src/components/NomnomlPreview.tsx @@ -0,0 +1,13 @@ +import { type ReactNode } from "react"; +import nomnoml from "nomnoml"; + +type NomnomlPreviewProps = { + children: ReactNode & ReactNode[]; +}; + +const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { + const code = String(children); +}; + +// Memoize to reduce re-renders/flickering when content hasn't changed +export default memo(NomnomlPreview); From 92cbc4d571f3b7be50570c547e67bfb4dfa4462f Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Thu, 21 Mar 2024 11:27:55 -0400 Subject: [PATCH 03/18] began adding handleCopy and useEffect based on MermaidPreview component --- src/components/NomnomlPreview.tsx | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx index bf627815..3721a20b 100644 --- a/src/components/NomnomlPreview.tsx +++ b/src/components/NomnomlPreview.tsx @@ -1,12 +1,34 @@ -import { type ReactNode } from "react"; +import { useRef, type ReactNode, useCallback, useEffect } from "react"; import nomnoml from "nomnoml"; +import { useClipboard } from "@chakra-ui/react"; +import { useAlert } from "../hooks/use-alert"; +import { nanoid } from "nanoid"; 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; + } + }); }; // Memoize to reduce re-renders/flickering when content hasn't changed From 7abeee8da1f7fabe01262e48e01189a8005dd3fe Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Thu, 21 Mar 2024 11:29:50 -0400 Subject: [PATCH 04/18] mirrored imports based on MermaidPreview component --- src/components/NomnomlPreview.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx index 3721a20b..e89a904f 100644 --- a/src/components/NomnomlPreview.tsx +++ b/src/components/NomnomlPreview.tsx @@ -1,8 +1,9 @@ -import { useRef, type ReactNode, useCallback, useEffect } from "react"; +import { memo, useCallback, useEffect, useRef, type ReactNode } from "react"; +import { Card, CardBody, IconButton, useClipboard } from "@chakra-ui/react"; import nomnoml from "nomnoml"; -import { useClipboard } from "@chakra-ui/react"; -import { useAlert } from "../hooks/use-alert"; +import { TbCopy } from "react-icons/tb"; import { nanoid } from "nanoid"; +import { useAlert } from "../hooks/use-alert"; type NomnomlPreviewProps = { children: ReactNode & ReactNode[]; From 970b9e9a7a278e159bd9e918c226289d2dce84a8 Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Sun, 24 Mar 2024 17:25:06 -0400 Subject: [PATCH 05/18] drafted fetchNomnoml() and modeled Card component off of MermaidPreview.tsx --- src/components/NomnomlPreview.tsx | 47 +++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx index e89a904f..86a37fbc 100644 --- a/src/components/NomnomlPreview.tsx +++ b/src/components/NomnomlPreview.tsx @@ -1,6 +1,5 @@ import { memo, useCallback, useEffect, useRef, type ReactNode } from "react"; import { Card, CardBody, IconButton, useClipboard } from "@chakra-ui/react"; -import nomnoml from "nomnoml"; import { TbCopy } from "react-icons/tb"; import { nanoid } from "nanoid"; import { useAlert } from "../hooks/use-alert"; @@ -29,7 +28,51 @@ const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { if (!diagramDiv) { return; } - }); + + // console.log(code); + // const nomnomlDiagramId = `nomnoml-diagram-${nanoid().toLowerCase()}`; + // console.log(nomnoml.renderSvg(code)); + try { + const fetchNomnoml = async () => { + const nomnoml = await import("nomnoml"); + console.log("code", code); + console.log("type of code", typeof code); + const svg = await nomnoml.renderSvg(code); + console.log(svg); + console.log("type of svg", typeof svg); + + setValue(svg); + diagramDiv.innerHTML = svg; + }; + fetchNomnoml(); + } catch (err) { + // setValue(err); + console.warn(`Error rendering nomnoml diagram`, err); + } + }, [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 From 45a66309c1fc821acefcf5ce187773632c724b47 Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Sun, 24 Mar 2024 19:31:21 -0400 Subject: [PATCH 06/18] added error handling to catch block --- src/components/NomnomlPreview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx index 86a37fbc..d4db7fb7 100644 --- a/src/components/NomnomlPreview.tsx +++ b/src/components/NomnomlPreview.tsx @@ -45,8 +45,8 @@ const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { diagramDiv.innerHTML = svg; }; fetchNomnoml(); - } catch (err) { - // setValue(err); + } catch (err: any) { + setValue(err); console.warn(`Error rendering nomnoml diagram`, err); } }, [diagramRef, code, setValue]); From b0ff9559e8807f62b95aeb1f3469c031dab2aa1b Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Sun, 24 Mar 2024 19:32:32 -0400 Subject: [PATCH 07/18] added elif block for handling nomnoml codeblocks --- src/components/Markdown.tsx | 5 +++++ 1 file changed, 5 insertions(+) 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 = ( + + ); } } From 5021fd012827b2cd9a8731f29eb5fde80babddd0 Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Mon, 25 Mar 2024 22:43:58 -0400 Subject: [PATCH 08/18] added width autofitting to nomnoml svg --- src/components/NomnomlPreview.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx index d4db7fb7..6c4777d3 100644 --- a/src/components/NomnomlPreview.tsx +++ b/src/components/NomnomlPreview.tsx @@ -43,6 +43,12 @@ const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { setValue(svg); diagramDiv.innerHTML = svg; + + // Adjust the width of the SVG to fit the content + const svgElement = diagramDiv.querySelector("svg"); + if (svgElement) { + svgElement.style.width = "100%"; + } }; fetchNomnoml(); } catch (err: any) { From 799930f3a56440d6ca9979d8958c67f27e764705 Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Mon, 25 Mar 2024 22:55:41 -0400 Subject: [PATCH 09/18] removed nanoid, commented code, console.log statements --- src/components/NomnomlPreview.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx index 6c4777d3..5abd1d58 100644 --- a/src/components/NomnomlPreview.tsx +++ b/src/components/NomnomlPreview.tsx @@ -1,7 +1,6 @@ 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 { nanoid } from "nanoid"; import { useAlert } from "../hooks/use-alert"; type NomnomlPreviewProps = { @@ -29,17 +28,10 @@ const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { return; } - // console.log(code); - // const nomnomlDiagramId = `nomnoml-diagram-${nanoid().toLowerCase()}`; - // console.log(nomnoml.renderSvg(code)); try { const fetchNomnoml = async () => { const nomnoml = await import("nomnoml"); - console.log("code", code); - console.log("type of code", typeof code); const svg = await nomnoml.renderSvg(code); - console.log(svg); - console.log("type of svg", typeof svg); setValue(svg); diagramDiv.innerHTML = svg; From e6f63f94058d19afae61f7790bf17d0886a3ffac Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Tue, 26 Mar 2024 00:31:12 -0400 Subject: [PATCH 10/18] added nomnoml to pnpm lock file --- pnpm-lock.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 From 72c19e86afaed7cbf0ad4dd25d3898dbabbe5981 Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Tue, 26 Mar 2024 11:52:52 -0400 Subject: [PATCH 11/18] updated Instructions blurb --- src/components/Message/AppMessage/Instructions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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| ✅ | ❌ | ❌ | From ee32550286427985f26f7132566c5d72cc7fab0c Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Tue, 26 Mar 2024 12:15:26 -0400 Subject: [PATCH 12/18] moved try/catch syntax inside fetchNomnoml to properly catch any errors from renderSvg() --- src/components/NomnomlPreview.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx index 5abd1d58..6f3314f5 100644 --- a/src/components/NomnomlPreview.tsx +++ b/src/components/NomnomlPreview.tsx @@ -28,8 +28,8 @@ const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { return; } - try { - const fetchNomnoml = async () => { + const fetchNomnoml = async () => { + try { const nomnoml = await import("nomnoml"); const svg = await nomnoml.renderSvg(code); @@ -41,12 +41,13 @@ const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { if (svgElement) { svgElement.style.width = "100%"; } - }; - fetchNomnoml(); - } catch (err: any) { - setValue(err); - console.warn(`Error rendering nomnoml diagram`, err); - } + } catch (err: any) { + // If diagram fails, use error vs. diagram for copying (to debug) + setValue(err); + console.warn(`Error rendering nomnoml diagram`, err); + } + }; + fetchNomnoml(); }, [diagramRef, code, setValue]); return ( From 86ef79d2edbccf7ee53e0f0d48a98b77d611da0c Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Tue, 26 Mar 2024 12:17:46 -0400 Subject: [PATCH 13/18] updated comments --- src/components/NomnomlPreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx index 6f3314f5..200b1b6f 100644 --- a/src/components/NomnomlPreview.tsx +++ b/src/components/NomnomlPreview.tsx @@ -42,7 +42,7 @@ const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { svgElement.style.width = "100%"; } } catch (err: any) { - // If diagram fails, use error vs. diagram for copying (to debug) + // When the diagram fails, use the error vs. diagram for copying (to debug) setValue(err); console.warn(`Error rendering nomnoml diagram`, err); } From 2d51fce6e4cfab489212772c0d47bd8761380977 Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Tue, 26 Mar 2024 12:20:01 -0400 Subject: [PATCH 14/18] updated comments --- src/components/NomnomlPreview.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx index 200b1b6f..ed7fc715 100644 --- a/src/components/NomnomlPreview.tsx +++ b/src/components/NomnomlPreview.tsx @@ -30,13 +30,14 @@ const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { const fetchNomnoml = async () => { try { + // Load nomnoml dynamically at runtime if needed const nomnoml = await import("nomnoml"); const svg = await nomnoml.renderSvg(code); setValue(svg); diagramDiv.innerHTML = svg; - // Adjust the width of the SVG to fit the content + // Adjust the width of the SVG to fit the content inside the CardBody const svgElement = diagramDiv.querySelector("svg"); if (svgElement) { svgElement.style.width = "100%"; From bb7c55737566be0231fdb525ec725bb8b8409680 Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Tue, 26 Mar 2024 12:26:59 -0400 Subject: [PATCH 15/18] updated default system prompt to include nomnoml responses --- src/lib/system-prompt.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/system-prompt.ts b/src/lib/system-prompt.ts index d28e3fad..07d95d21 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$$ `; From 1ee55296634b80e3ab4d427173b03860ec854b1c Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Wed, 27 Mar 2024 13:36:04 -0400 Subject: [PATCH 16/18] added error message to UI for when nomnoml rendering fails --- src/components/NomnomlPreview.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx index ed7fc715..11c364ae 100644 --- a/src/components/NomnomlPreview.tsx +++ b/src/components/NomnomlPreview.tsx @@ -35,6 +35,8 @@ const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { 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 @@ -45,6 +47,10 @@ const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { } 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 = `Syntax error in Nomnoml code`; + diagramDiv.innerHTML = errMessage; console.warn(`Error rendering nomnoml diagram`, err); } }; From fe81d70e0a8009ba08c8b4ab4b4dcd7870351c2e Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Wed, 27 Mar 2024 13:41:30 -0400 Subject: [PATCH 17/18] updated error message --- src/components/NomnomlPreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/NomnomlPreview.tsx b/src/components/NomnomlPreview.tsx index 11c364ae..a9e1e14c 100644 --- a/src/components/NomnomlPreview.tsx +++ b/src/components/NomnomlPreview.tsx @@ -49,7 +49,7 @@ const NomnomlPreview = ({ children }: NomnomlPreviewProps) => { setValue(err); // Render custom error message instead of error or blank content - const errMessage = `Syntax error in Nomnoml code`; + const errMessage = `Error rendering Nomnoml diagram`; diagramDiv.innerHTML = errMessage; console.warn(`Error rendering nomnoml diagram`, err); } From 30cc7e03e6da7ac23f320c0e8d1d6eb6a41526eb Mon Sep 17 00:00:00 2001 From: Roy Jeffrey Wignarajah Date: Thu, 28 Mar 2024 18:02:07 -0400 Subject: [PATCH 18/18] updated visual topics rule typo --- src/lib/system-prompt.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/system-prompt.ts b/src/lib/system-prompt.ts index 07d95d21..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 nomnoml or 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$$ `;