diff --git a/apps/webapp/app/components/code/CodeBlock.tsx b/apps/webapp/app/components/code/CodeBlock.tsx index a7050202ea..db1d165220 100644 --- a/apps/webapp/app/components/code/CodeBlock.tsx +++ b/apps/webapp/app/components/code/CodeBlock.tsx @@ -1,8 +1,11 @@ +import { ArrowsPointingOutIcon } from "@heroicons/react/20/solid"; import { Clipboard, ClipboardCheck } from "lucide-react"; import type { Language, PrismTheme } from "prism-react-renderer"; import { Highlight, Prism } from "prism-react-renderer"; import { forwardRef, ReactNode, useCallback, useState } from "react"; import { cn } from "~/utils/cn"; +import { Button } from "../primitives/Buttons"; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../primitives/Dialog"; import { Paragraph } from "../primitives/Paragraph"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../primitives/Tooltip"; @@ -51,6 +54,9 @@ type CodeBlockProps = { /** title text for the Title row */ rowTitle?: string; + + /** Whether to show the open in modal button */ + showOpenInModal?: boolean; }; const dimAmount = 0.5; @@ -178,6 +184,7 @@ export const CodeBlock = forwardRef( { showCopyButton = true, showLineNumbers = true, + showOpenInModal = true, highlightedRanges, code, className, @@ -193,6 +200,9 @@ export const CodeBlock = forwardRef( ) => { const [mouseOver, setMouseOver] = useState(false); const [copied, setCopied] = useState(false); + const [modalCopied, setModalCopied] = useState(false); + const [isModalOpen, setIsModalOpen] = useState(false); + const onCopied = useCallback( (event: React.MouseEvent) => { event.preventDefault(); @@ -206,6 +216,19 @@ export const CodeBlock = forwardRef( [code] ); + const onModalCopied = useCallback( + (event: React.MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + navigator.clipboard.writeText(code); + setModalCopied(true); + setTimeout(() => { + setModalCopied(false); + }, 1500); + }, + [code] + ); + code = code.trim(); const lineCount = code.split("\n").length; const maxLineWidth = lineCount.toString().length; @@ -222,147 +245,130 @@ export const CodeBlock = forwardRef( const shouldHighlight = lineCount <= 1000; return ( -
- {showChrome && } - {rowTitle && } - {showCopyButton && ( - - - setMouseOver(true)} - onMouseLeave={() => setMouseOver(false)} - className={cn( - "absolute right-3 z-50 transition-colors duration-100 focus-custom hover:cursor-pointer", - showChrome ? "top-10" : "top-2.5", - copied ? "text-emerald-500" : "text-charcoal-500 hover:text-charcoal-300" - )} + <> +
+ {showChrome && } + {rowTitle && } +
+ {showCopyButton && ( + + + setMouseOver(true)} + onMouseLeave={() => setMouseOver(false)} + className={cn( + "transition-colors duration-100 focus-custom hover:cursor-pointer", + copied ? "text-success" : "text-text-dimmed hover:text-text-bright" + )} + > + {copied ? ( + + ) : ( + + )} + + + {copied ? "Copied" : "Copy"} + + + + )} + {showOpenInModal && ( + + + setIsModalOpen(true)}> + + + + Expand + + + + )} +
+ + {shouldHighlight ? ( + + ) : ( +
+
+                {code}
+              
+
+ )} +
+ + + + + + {fileName && fileName} + {rowTitle && rowTitle} + + + + + {shouldHighlight ? ( + + ) : (
-
-                  {tokens
-                    .map((line, index) => {
-                      if (
-                        index === tokens.length - 1 &&
-                        line.length === 1 &&
-                        line[0].content === "\n"
-                      ) {
-                        return null;
-                      }
-
-                      const lineNumber = index + 1;
-                      const lineProps = getLineProps({ line, key: index });
-
-                      let hasAnyHighlights = highlightLines ? highlightLines.length > 0 : false;
-
-                      let shouldDim = hasAnyHighlights;
-                      if (hasAnyHighlights && highlightLines?.includes(lineNumber)) {
-                        shouldDim = false;
-                      }
-
-                      return (
-                        
- {showLineNumbers && ( -
- {lineNumber} -
- )} - -
- {line.map((token, key) => { - const tokenProps = getTokenProps({ token, key }); - return ( - - ); - })} -
-
-
- ); - }) - .filter(Boolean)} +
+                  {code}
                 
)} - - ) : ( -
-
-              {code}
-            
-
- )} -
+
+
+ ); } ); @@ -394,3 +400,118 @@ export function TitleRow({ title }: { title: ReactNode }) {
); } + +type HighlightCodeProps = { + theme: PrismTheme; + code: string; + language: Language; + showLineNumbers: boolean; + highlightLines?: number[]; + maxLineWidth?: number; + className?: string; + preClassName?: string; +}; + +function HighlightCode({ + theme, + code, + language, + showLineNumbers, + highlightLines, + maxLineWidth, + className, + preClassName, +}: HighlightCodeProps) { + return ( + + {({ + className: inheritedClassName, + style: inheritedStyle, + tokens, + getLineProps, + getTokenProps, + }) => ( +
+
+            {tokens
+              .map((line, index) => {
+                if (index === tokens.length - 1 && line.length === 1 && line[0].content === "\n") {
+                  return null;
+                }
+
+                const lineNumber = index + 1;
+                const lineProps = getLineProps({ line, key: index });
+
+                let hasAnyHighlights = highlightLines ? highlightLines.length > 0 : false;
+
+                let shouldDim = hasAnyHighlights;
+                if (hasAnyHighlights && highlightLines?.includes(lineNumber)) {
+                  shouldDim = false;
+                }
+
+                return (
+                  
+ {showLineNumbers && ( +
+ {lineNumber} +
+ )} + +
+ {line.map((token, key) => { + const tokenProps = getTokenProps({ token, key }); + return ( + + ); + })} +
+
+
+ ); + }) + .filter(Boolean)} +
+
+ )} +
+ ); +} diff --git a/apps/webapp/app/components/primitives/Dialog.tsx b/apps/webapp/app/components/primitives/Dialog.tsx index 5e67afab77..7a78b5e02b 100644 --- a/apps/webapp/app/components/primitives/Dialog.tsx +++ b/apps/webapp/app/components/primitives/Dialog.tsx @@ -94,7 +94,7 @@ const DialogTitle = React.forwardRef< >(({ className, ...props }, ref) => ( ));