| @@ -0,0 +1,44 @@ | |||
| 'use client' | |||
| import React from 'react' | |||
| import Tooltip from '@/app/components/base/tooltip' | |||
| import { t } from 'i18next' | |||
| import s from './style.module.css' | |||
| import copy from 'copy-to-clipboard' | |||
| type ICopyBtnProps = { | |||
| value: string | |||
| className?: string | |||
| } | |||
| const CopyBtn = ({ | |||
| value, | |||
| className, | |||
| }: ICopyBtnProps) => { | |||
| const [isCopied, setIsCopied] = React.useState(false) | |||
| return ( | |||
| <div className={`${className}`}> | |||
| <Tooltip | |||
| selector="copy-btn-tooltip" | |||
| content={(isCopied ? t('appApi.copied') : t('appApi.copy')) as string} | |||
| className='z-10' | |||
| > | |||
| <div | |||
| className={`box-border p-0.5 flex items-center justify-center rounded-md bg-white cursor-pointer`} | |||
| style={{ | |||
| boxShadow: '0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06)' | |||
| }} | |||
| onClick={() => { | |||
| copy(value) | |||
| setIsCopied(true) | |||
| }} | |||
| > | |||
| <div className={`w-6 h-6 hover:bg-gray-50 ${s.copyIcon} ${isCopied ? s.copied : ''}`}></div> | |||
| </div> | |||
| </Tooltip> | |||
| </div> | |||
| ) | |||
| } | |||
| export default CopyBtn | |||
| @@ -0,0 +1,15 @@ | |||
| .copyIcon { | |||
| background-image: url(~@/app/components/develop/secret-key/assets/copy.svg); | |||
| background-position: center; | |||
| background-repeat: no-repeat; | |||
| } | |||
| .copyIcon:hover { | |||
| background-image: url(~@/app/components/develop/secret-key/assets/copy-hover.svg); | |||
| background-position: center; | |||
| background-repeat: no-repeat; | |||
| } | |||
| .copyIcon.copied { | |||
| background-image: url(~@/app/components/develop/secret-key/assets/copied.svg); | |||
| } | |||
| @@ -17,6 +17,7 @@ import AppContext from '@/context/app-context' | |||
| import { Markdown } from '@/app/components/base/markdown' | |||
| import LoadingAnim from './loading-anim' | |||
| import { formatNumber } from '@/utils/format' | |||
| import CopyBtn from './copy-btn' | |||
| const stopIcon = ( | |||
| <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||
| @@ -346,6 +347,10 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedba | |||
| } | |||
| </div> | |||
| <div className='absolute top-[-14px] right-[-14px] flex flex-row justify-end gap-1'> | |||
| <CopyBtn | |||
| value={content} | |||
| className={cn(s.copyBtn, 'mr-1')} | |||
| /> | |||
| {!feedbackDisabled && !item.feedbackDisabled && renderItemOperation(displayScene !== 'console')} | |||
| {/* Admin feedback is displayed only in the background. */} | |||
| {!feedbackDisabled && renderFeedbackRating(localAdminFeedback?.rating, false, false)} | |||
| @@ -38,6 +38,14 @@ | |||
| background: url(./icons/answer.svg) no-repeat; | |||
| } | |||
| .copyBtn { | |||
| display: none; | |||
| } | |||
| .answerWrap:hover .copyBtn { | |||
| display: block; | |||
| } | |||
| .answerWrap .itemOperation { | |||
| display: none; | |||
| } | |||
| @@ -71,7 +71,7 @@ const CustomizeModal: FC<IShareLinkProps> = ({ | |||
| <div className='flex flex-col w-full'> | |||
| <div className='text-gray-900'>{t(`${prefixCustomize}.way1.step2`)}</div> | |||
| <div className='text-gray-500 text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step2Tip`)}</div> | |||
| <pre className='box-border py-3 px-4 bg-gray-100 text-xs font-medium rounded-lg'> | |||
| <pre className='box-border py-3 px-4 bg-gray-100 text-xs font-medium rounded-lg select-text'> | |||
| export const APP_ID = '{appId}'<br /> | |||
| export const API_KEY = {`'<Web API Key From Dify>'`} | |||
| </pre> | |||