Skip to content

Commit

Permalink
feat(opentrons-ai-client): add code copy button (#15126)
Browse files Browse the repository at this point in the history
* feat(opentrons-ai-client): add code copy button
  • Loading branch information
koji authored and Carlos-fernandez committed May 20, 2024
1 parent 0e297ee commit 4cc7d87
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 37 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
98 changes: 66 additions & 32 deletions opentrons-ai-client/src/molecules/ChatDisplay/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import React from 'react'
// import { css } from 'styled-components'
import styled from 'styled-components'
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,
} from '@opentrons/components'
Expand All @@ -15,12 +21,26 @@ import type { ChatData } from '../../resources/types'

interface ChatDisplayProps {
chat: ChatData
chatId: string
}

export function ChatDisplay({ chat }: ChatDisplayProps): JSX.Element {
export function ChatDisplay({ chat, chatId }: 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.querySelector(`#${chatId}`)
const code = lastCodeBlock?.textContent ?? ''
await navigator.clipboard.writeText(code)
setIsCopied(true)
}

function CodeText(props: JSX.IntrinsicAttributes): JSX.Element {
return <CodeWrapper {...props} id={chatId} />
}

return (
<Flex
flexDirection={DIRECTION_COLUMN}
Expand All @@ -38,9 +58,9 @@ 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,
Expand All @@ -52,41 +72,55 @@ export function ChatDisplay({ chat }: ChatDisplayProps): JSX.Element {
}}
>
{content}
</Markdown> */}
<Markdown>{content}</Markdown>
</Markdown>
{role === 'assistant' ? (
<PrimaryButton
position={POSITION_ABSOLUTE}
right={SPACING.spacing16}
bottom={`-${SPACING.spacing16}`}
borderRadius={BORDERS.borderRadiusFull}
onClick={handleClickCopy}
>
<Flex alignItems={ALIGN_CENTER} justifyContent={JUSTIFY_CENTER}>
<Icon
size="2rem"
name={isCopied ? 'check' : 'copy-text'}
color={COLORS.white}
/>
</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 ParagraphText(props: JSX.IntrinsicAttributes): JSX.Element {
// return <StyledText {...props} as="p" />
// }
// Note (05/08/2024) the following styles are temp
function ExternalLink(props: JSX.IntrinsicAttributes): JSX.Element {
return <a {...props} target="_blank" rel="noopener noreferrer" />
}

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

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

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

// const CODE_TEXT_STYLE = css`
// padding: ${SPACING.spacing16};
// font-family: monospace;
// color: ${COLORS.white};
// background-color: ${COLORS.black90};
// `
function UnnumberedListText(props: JSX.IntrinsicAttributes): JSX.Element {
return <StyledText {...props} as="ul" />
}

// function CodeText(props: JSX.IntrinsicAttributes): JSX.Element {
// return <StyledText {...props} as="p" css={CODE_TEXT_STYLE} />
// }
const CodeWrapper = styled(Flex)`
font-family: monospace;
padding: ${SPACING.spacing16};
color: ${COLORS.white};
background-color: ${COLORS.black90};
border-radius: ${BORDERS.borderRadius8};
overflow: auto;
`
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
3 changes: 2 additions & 1 deletion opentrons-ai-client/src/organisms/ChatContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ export function ChatContainer(): JSX.Element {
{chatData.length > 0
? chatData.map((chat, index) => (
<ChatDisplay
key={`response-from_${chat.role}_${index}`}
key={`prompt-from_${chat.role}_${index}`}
chat={chat}
chatId={`${chat.role}_${index}`}
/>
))
: null}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"discovery-client",
"labware-designer",
"labware-library",
"opentrons-ai-client",
"protocol-designer",
"shared-data",
"step-generation",
Expand Down Expand Up @@ -105,7 +106,6 @@
"handlebars-loader": "^1.7.1",
"html-webpack-plugin": "^3.2.0",
"identity-obj-proxy": "^3.0.0",
"jotai": "2.8.0",
"jsdom": "^16.4.0",
"lost": "^8.3.1",
"madge": "^3.6.0",
Expand Down

0 comments on commit 4cc7d87

Please sign in to comment.