mirror of
https://github.com/EthanMarti/infio-copilot.git
synced 2026-01-18 00:47:51 +00:00
update, add copy in svg
This commit is contained in:
parent
1a508078be
commit
7ffdb164b0
@ -1,13 +1,13 @@
|
|||||||
import * as Tooltip from '@radix-ui/react-tooltip';
|
import * as Tooltip from '@radix-ui/react-tooltip';
|
||||||
import { Check, CircleCheckBig, CircleHelp, CopyIcon, FilePlus2 } from 'lucide-react';
|
import { Check, CircleCheckBig, CircleHelp, CopyIcon, FilePlus2 } from 'lucide-react';
|
||||||
import { ReactNode, useState } from 'react';
|
import { ReactNode, useState } from 'react';
|
||||||
import ReactMarkdown from 'react-markdown';
|
|
||||||
import rehypeRaw from 'rehype-raw';
|
|
||||||
import { useApp } from 'src/contexts/AppContext';
|
import { useApp } from 'src/contexts/AppContext';
|
||||||
|
|
||||||
import { t } from '../../../lang/helpers'
|
import { t } from '../../../lang/helpers'
|
||||||
|
|
||||||
function CopyButton({ message }: { message: string }) {
|
import RawMarkdownBlock from './RawMarkdownBlock'
|
||||||
|
|
||||||
|
export function CopyButton({ message }: { message: string }) {
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
|
|
||||||
const handleCopy = async () => {
|
const handleCopy = async () => {
|
||||||
@ -43,7 +43,7 @@ function CopyButton({ message }: { message: string }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function CreateNewFileButton({ message }: { message: string }) {
|
export function CreateNewFileButton({ message }: { message: string }) {
|
||||||
const app = useApp()
|
const app = useApp()
|
||||||
const [created, setCreated] = useState(false)
|
const [created, setCreated] = useState(false)
|
||||||
|
|
||||||
@ -138,12 +138,10 @@ const MarkdownWithIcons = ({
|
|||||||
<>
|
<>
|
||||||
<div className={`${className}`}>
|
<div className={`${className}`}>
|
||||||
<span>{iconName && renderIcon()} {renderTitle()}</span>
|
<span>{iconName && renderIcon()} {renderTitle()}</span>
|
||||||
<ReactMarkdown
|
<RawMarkdownBlock
|
||||||
|
content={markdownContent}
|
||||||
className={`${className}`}
|
className={`${className}`}
|
||||||
rehypePlugins={[rehypeRaw]}
|
/>
|
||||||
>
|
|
||||||
{markdownContent}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</div>
|
</div>
|
||||||
{markdownContent && finish && iconName === "attempt_completion" &&
|
{markdownContent && finish && iconName === "attempt_completion" &&
|
||||||
<div className="infio-chat-message-actions">
|
<div className="infio-chat-message-actions">
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { CopyIcon } from "lucide-react"
|
||||||
import mermaid from "mermaid"
|
import mermaid from "mermaid"
|
||||||
import { memo, useEffect, useRef, useState } from "react"
|
import { memo, useEffect, useRef, useState } from "react"
|
||||||
import styled from "styled-components"
|
import styled from "styled-components"
|
||||||
@ -76,6 +77,42 @@ interface MermaidBlockProps {
|
|||||||
code: string
|
code: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface MermaidToolbarProps {
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MermaidToolbar({ code }: MermaidToolbarProps) {
|
||||||
|
const { showCopyFeedback, copyWithFeedback } = useCopyToClipboard()
|
||||||
|
|
||||||
|
const handleCopy = (e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
// We wrap the code in a markdown block for easy pasting
|
||||||
|
copyWithFeedback("```mermaid\n" + code + "\n```")
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToolbarContainer className="mermaid-toolbar">
|
||||||
|
<ToolbarButton className="mermaid-toolbar-btn" onClick={handleCopy} aria-label={t("common:copy_code")}>
|
||||||
|
<CopyIcon size={12} />
|
||||||
|
</ToolbarButton>
|
||||||
|
</ToolbarContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MermaidButtonProps {
|
||||||
|
code: string
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
function MermaidButton({ code, children }: MermaidButtonProps) {
|
||||||
|
return (
|
||||||
|
<MermaidWrapper>
|
||||||
|
{children}
|
||||||
|
<MermaidToolbar code={code} />
|
||||||
|
</MermaidWrapper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function MermaidBlock({ code }: MermaidBlockProps) {
|
function MermaidBlock({ code }: MermaidBlockProps) {
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
@ -259,15 +296,78 @@ function MermaidBlock({ code }: MermaidBlockProps) {
|
|||||||
)}
|
)}
|
||||||
</ErrorContainer>
|
</ErrorContainer>
|
||||||
) : (
|
) : (
|
||||||
|
<MermaidButton code={code}>
|
||||||
<SvgContainer onClick={handleClick} ref={containerRef} $isLoading={isLoading} />
|
<SvgContainer onClick={handleClick} ref={containerRef} $isLoading={isLoading} />
|
||||||
|
</MermaidButton>
|
||||||
)}
|
)}
|
||||||
</MermaidBlockContainer>
|
</MermaidBlockContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MermaidBlockContainer = styled.div`
|
const MermaidWrapper = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
|
|
||||||
|
&:hover .mermaid-toolbar {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mermaid-toolbar-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: transparent !important;
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
color: var(--text-muted);
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
width: 24px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--background-modifier-hover) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ToolbarContainer = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
z-index: 10;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s ease-in-out;
|
||||||
|
background-color: var(--background-secondary);
|
||||||
|
border: 1px solid var(--background-modifier-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 2px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1; /* Keep it visible when hovering over the toolbar itself */
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ToolbarButton = styled.button`
|
||||||
|
padding: 4px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--text-normal);
|
||||||
|
background-color: var(--background-modifier-hover);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const MermaidBlockContainer = styled.div`
|
||||||
|
position: relative;
|
||||||
`
|
`
|
||||||
|
|
||||||
const LoadingMessage = styled.div`
|
const LoadingMessage = styled.div`
|
||||||
|
|||||||
@ -31,7 +31,7 @@ function ReactMarkdown({
|
|||||||
applyStatus: ApplyStatus
|
applyStatus: ApplyStatus
|
||||||
onApply: (toolArgs: ToolArgs) => void
|
onApply: (toolArgs: ToolArgs) => void
|
||||||
children: string
|
children: string
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
const blocks: ParsedMsgBlock[] = useMemo(
|
const blocks: ParsedMsgBlock[] = useMemo(
|
||||||
() => parseMsgBlocks(children),
|
() => parseMsgBlocks(children),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user