Skip to content

Commit

Permalink
feat(opentrons-ai-client): add code copy button
Browse files Browse the repository at this point in the history
add code copy button

close AUTH-389
  • Loading branch information
koji committed May 7, 2024
1 parent 80cfe7e commit c422fe8
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"api": "API: An API level is 2.15",
"application": "Application: Your protocol's name, describing what it does.",
"commands": "Commands: List the protocol's steps, specifying quantities in microliters and giving exact source and destination locations.",
"copy_code": "Copy code",
"disclaimer": "OpentronsAI can make mistakes. Review your protocol before running it on an Opentrons robot.",
"got_feedback": "Got feedback? We love to hear it.",
"make_sure_your_prompt": "Make sure your prompt includes the following:",
Expand Down
111 changes: 82 additions & 29 deletions opentrons-ai-client/src/molecules/ChatDisplay/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import React from 'react'
// import { css } from 'styled-components'
import styled, { css } from 'styled-components'

Check failure on line 2 in opentrons-ai-client/src/molecules/ChatDisplay/index.tsx

View workflow job for this annotation

GitHub Actions / js checks

'css' is defined but never used

Check failure on line 2 in opentrons-ai-client/src/molecules/ChatDisplay/index.tsx

View workflow job for this annotation

GitHub Actions / js checks

'css' is defined but never used
import { useTranslation } from 'react-i18next'
import Markdown from 'react-markdown'
import {
ALIGN_CENTER,
BORDERS,
COLORS,
DIRECTION_COLUMN,
Flex,
Icon,
JUSTIFY_CENTER,
POSITION_ABSOLUTE,
POSITION_RELATIVE,
PrimaryButton,
SPACING,
StyledText,
TYPOGRAPHY,
} from '@opentrons/components'

import type { ChatData } from '../../resources/types'
Expand All @@ -19,8 +26,17 @@ interface ChatDisplayProps {

export function ChatDisplay({ chat }: ChatDisplayProps): JSX.Element {
const { t } = useTranslation('protocol_generator')
const [isCopied, setIsCopied] = React.useState<boolean>(false)
const { role, content } = chat
const isUser = role === 'user'

const handleClickCopy = async (): Promise<void> => {
const lastCodeBlock = document.querySelectorAll('pre')[0]
const code = (lastCodeBlock.innerText || lastCodeBlock.textContent) ?? ''
await navigator.clipboard.writeText(code)
setIsCopied(true)
}

return (
<Flex
flexDirection={DIRECTION_COLUMN}
Expand All @@ -38,55 +54,92 @@ export function ChatDisplay({ chat }: ChatDisplayProps): JSX.Element {
width="100%"
flexDirection={DIRECTION_COLUMN}
gridGap={SPACING.spacing16}
position={POSITION_RELATIVE}
>
{/* ToDo (kk:05/02/2024) This part is waiting for Mel's design */}
{/* <Markdown
<Markdown
components={{
div: undefined,
ul: UnnumberedListText,
h2: HeaderText,
li: ListItemText,
p: ParagraphText,
a: ExternalLink,
code: CodeText,
code: Code,
}}
>
{content}
</Markdown> */}
<Markdown>{content}</Markdown>
</Markdown>
{role === 'assistant' ? (
<PrimaryButton
position={POSITION_ABSOLUTE}
right="2rem"
bottom="2rem"
borderRadius={BORDERS.borderRadiusFull}
onClick={handleClickCopy}
>
<Flex
padding={`${SPACING.spacing16} ${SPACING.spacing24}`}
gridGap={SPACING.spacing8}
alignItems={ALIGN_CENTER}
justifyContent={JUSTIFY_CENTER}
>
<Icon
size="2rem"
name={isCopied ? 'check' : 'copy-text'}
color={COLORS.white}
/>
<StyledText
fontSize={TYPOGRAPHY.fontSize22}
lineHeight={TYPOGRAPHY.lineHeight28}
fontWeight={TYPOGRAPHY.fontWeightSemiBold}
>
{t('copy_code')}
</StyledText>
</Flex>
</PrimaryButton>
) : null}
</Flex>
</Flex>
)
}

// ToDo (kk:05/02/2024) This part is waiting for Mel's design
// function ExternalLink(props: JSX.IntrinsicAttributes): JSX.Element {
// return <a {...props} target="_blank" rel="noopener noreferrer" />
// }
function ExternalLink(props: JSX.IntrinsicAttributes): JSX.Element {
return <a {...props} target="_blank" rel="noopener noreferrer" />
}

// function ParagraphText(props: JSX.IntrinsicAttributes): JSX.Element {
// return <StyledText {...props} as="p" />
// }
function ParagraphText(props: JSX.IntrinsicAttributes): JSX.Element {
return <StyledText {...props} as="p" />
}

// function HeaderText(props: JSX.IntrinsicAttributes): JSX.Element {
// return <StyledText {...props} as="h3" />
// }
function HeaderText(props: JSX.IntrinsicAttributes): JSX.Element {
return <StyledText {...props} as="h3" />
}

// function ListItemText(props: JSX.IntrinsicAttributes): JSX.Element {
// return <StyledText {...props} as="li" />
// }
function ListItemText(props: JSX.IntrinsicAttributes): JSX.Element {
return <StyledText {...props} as="li" />
}

// function UnnumberedListText(props: JSX.IntrinsicAttributes): JSX.Element {
// return <StyledText {...props} as="ul" />
// }
function UnnumberedListText(props: JSX.IntrinsicAttributes): JSX.Element {
return <StyledText {...props} as="ul" />
}

// const CODE_TEXT_STYLE = css`
// padding: ${SPACING.spacing16};
// font-family: monospace;
// color: ${COLORS.white};
// background-color: ${COLORS.black90};
// `
const CodeText = styled.code`
font-family: monospace;
color: ${COLORS.white};
background-color: ${COLORS.black90};
`

// function CodeText(props: JSX.IntrinsicAttributes): JSX.Element {
// return <StyledText {...props} as="p" css={CODE_TEXT_STYLE} />
// }
function Code(props: JSX.IntrinsicAttributes): JSX.Element {
return (
<Flex
backgroundColor={COLORS.black90}
padding={SPACING.spacing16}
borderRadius={BORDERS.borderRadius8}
marginBottom={SPACING.spacing32}
>
<CodeText {...props} />
</Flex>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import {
TYPOGRAPHY,
} from '@opentrons/components'

import type { IconName } from '@opentrons/components/src/icons'
import type { IconName, StyleProps } from '@opentrons/components'

interface PrimaryFloatingButtonProps {
interface PrimaryFloatingButtonProps extends StyleProps {
buttonText: string
iconName: IconName
disabled?: boolean
Expand All @@ -26,9 +26,10 @@ export function PrimaryFloatingButton({
buttonText,
iconName,
disabled = false,
...buttonProps
}: PrimaryFloatingButtonProps): JSX.Element {
return (
<Btn css={PRIMARY_FLOATING_STYLE} disabled={disabled}>
<Btn css={PRIMARY_FLOATING_STYLE} disabled={disabled} {...buttonProps}>
<Icon
size="0.75rem"
name={iconName}
Expand Down

0 comments on commit c422fe8

Please sign in to comment.