diff --git a/web/app/(shareLayout)/layout.tsx b/web/app/(shareLayout)/layout.tsx index 94ac1deb0b..0782603ebc 100644 --- a/web/app/(shareLayout)/layout.tsx +++ b/web/app/(shareLayout)/layout.tsx @@ -1,6 +1,7 @@ import React from 'react' import type { FC } from 'react' import type { Metadata } from 'next' +import { SharePageContextProvider } from '@/context/share-page-context' export const metadata: Metadata = { icons: 'data:,', // prevent browser from using default favicon @@ -11,7 +12,9 @@ const Layout: FC<{ }> = ({ children }) => { return (
- {children} + + {children} +
) } diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/csv-downloader.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/csv-downloader.tsx index d2189b4581..fbf7b0d419 100644 --- a/web/app/components/app/annotation/batch-add-annotation-modal/csv-downloader.tsx +++ b/web/app/components/app/annotation/batch-add-annotation-modal/csv-downloader.tsx @@ -42,7 +42,7 @@ const CSVDownload: FC = () => { {t('appAnnotation.batchModal.answer')} - + {t('appAnnotation.batchModal.question')} 1 {t('appAnnotation.batchModal.answer')} 1 diff --git a/web/app/components/app/configuration/debug/debug-with-multiple-model/text-generation-item.tsx b/web/app/components/app/configuration/debug/debug-with-multiple-model/text-generation-item.tsx index 57c8f83f3f..ae6889dc28 100644 --- a/web/app/components/app/configuration/debug/debug-with-multiple-model/text-generation-item.tsx +++ b/web/app/components/app/configuration/debug/debug-with-multiple-model/text-generation-item.tsx @@ -124,18 +124,9 @@ const TextGenerationItem: FC = ({ doSend(v.payload.message, v.payload.files) }) - const varList = modelConfig.configs.prompt_variables.map((item: any) => { - return { - label: item.key, - value: inputs[item.key], - } - }) - return ( = ({ messageId={messageId} isError={false} onRetry={() => { }} - appId={appId} - varList={varList} + inSidePanel /> ) } diff --git a/web/app/components/app/configuration/debug/index.tsx b/web/app/components/app/configuration/debug/index.tsx index 480bd782ae..2998b89894 100644 --- a/web/app/components/app/configuration/debug/index.tsx +++ b/web/app/components/app/configuration/debug/index.tsx @@ -516,9 +516,6 @@ const Debug: FC = ({ messageId={messageId} isError={false} onRetry={() => { }} - supportAnnotation - appId={appId} - varList={varList} siteInfo={null} /> diff --git a/web/app/components/app/log/list.tsx b/web/app/components/app/log/list.tsx index 2e688bc381..d416992baa 100644 --- a/web/app/components/app/log/list.tsx +++ b/web/app/components/app/log/list.tsx @@ -416,10 +416,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) { supportFeedback feedback={detail.message.feedbacks.find((item: any) => item.from_source === 'admin')} onFeedback={feedback => onFeedback(detail.message.id, feedback)} - supportAnnotation isShowTextToSpeech - appId={appDetail?.id} - varList={varList} siteInfo={null} /> diff --git a/web/app/components/app/text-generate/index.tsx b/web/app/components/app/text-generate/index.tsx deleted file mode 100644 index 4cacbdbd52..0000000000 --- a/web/app/components/app/text-generate/index.tsx +++ /dev/null @@ -1,26 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' -import { format } from '@/service/base' - -export type ITextGenerationProps = { - value: string - className?: string -} - -const TextGeneration: FC = ({ - value, - className, -}) => { - return ( -
-
- ) -} - -export default React.memo(TextGeneration) diff --git a/web/app/components/app/text-generate/item/index.tsx b/web/app/components/app/text-generate/item/index.tsx index 5f0b645570..8eb3a57a90 100644 --- a/web/app/components/app/text-generate/item/index.tsx +++ b/web/app/components/app/text-generate/item/index.tsx @@ -1,39 +1,40 @@ 'use client' import type { FC } from 'react' -import React, { useEffect, useRef, useState } from 'react' +import React, { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { + RiBookmark3Line, RiClipboardLine, + RiFileList3Line, + RiPlayList2Line, + RiReplay15Line, + RiSparklingFill, + RiSparklingLine, + RiThumbDownLine, + RiThumbUpLine, } from '@remixicon/react' import copy from 'copy-to-clipboard' import { useParams } from 'next/navigation' -import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline' import { useBoolean } from 'ahooks' -import { HashtagIcon } from '@heroicons/react/24/solid' import ResultTab from './result-tab' -import cn from '@/utils/classnames' import { Markdown } from '@/app/components/base/markdown' import Loading from '@/app/components/base/loading' import Toast from '@/app/components/base/toast' -import AudioBtn from '@/app/components/base/audio-btn' import type { FeedbackType } from '@/app/components/base/chat/chat/type' import { fetchMoreLikeThis, updateFeedback } from '@/service/share' -import { File02 } from '@/app/components/base/icons/src/vender/line/files' -import { Bookmark } from '@/app/components/base/icons/src/vender/line/general' -import { Stars02 } from '@/app/components/base/icons/src/vender/line/weather' -import { RefreshCcw01 } from '@/app/components/base/icons/src/vender/line/arrows' -import AnnotationCtrlBtn from '@/app/components/base/features/new-feature-panel/annotation-reply/annotation-ctrl-btn' import { fetchTextGenerationMessage } from '@/service/debug' -import EditReplyModal from '@/app/components/app/annotation/edit-annotation-modal' import { useStore as useAppStore } from '@/app/components/app/store' import WorkflowProcessItem from '@/app/components/base/chat/chat/answer/workflow-process' import type { WorkflowProcess } from '@/app/components/base/chat/types' import type { SiteInfo } from '@/models/share' import { useChatContext } from '@/app/components/base/chat/chat/context' +import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' +import NewAudioButton from '@/app/components/base/new-audio-button' +import cn from '@/utils/classnames' const MAX_DEPTH = 3 -export interface IGenerationItemProps { +export type IGenerationItemProps = { isWorkflow?: boolean workflowProcessData?: WorkflowProcess className?: string @@ -56,31 +57,12 @@ export interface IGenerationItemProps { taskId?: string controlClearMoreLikeThis?: number supportFeedback?: boolean - supportAnnotation?: boolean isShowTextToSpeech?: boolean - appId?: string - varList?: { label: string; value: string | number | object }[] - innerClassName?: string - contentClassName?: string - footerClassName?: string hideProcessDetail?: boolean siteInfo: SiteInfo | null + inSidePanel?: boolean } -export const SimpleBtn = ({ className, isDisabled, onClick, children }: { - className?: string - isDisabled?: boolean - onClick?: () => void - children: React.ReactNode -}) => ( -
!isDisabled && onClick?.()} - > - {children} -
-) - export const copyIcon = ( @@ -109,22 +91,16 @@ const GenerationItem: FC = ({ taskId, controlClearMoreLikeThis, supportFeedback, - supportAnnotation, isShowTextToSpeech, - appId, - varList, - innerClassName, - contentClassName, hideProcessDetail, siteInfo, + inSidePanel, }) => { const { t } = useTranslation() const params = useParams() const isTop = depth === 1 - const ref = useRef(null) const [completionRes, setCompletionRes] = useState('') const [childMessageId, setChildMessageId] = useState(null) - const hasChild = !!childMessageId const [childFeedback, setChildFeedback] = useState({ rating: null, }) @@ -140,8 +116,6 @@ const GenerationItem: FC = ({ setChildFeedback(childFeedback) } - const [isShowReplyModal, setIsShowReplyModal] = useState(false) - const question = (varList && varList?.length > 0) ? varList?.map(({ label, value }) => `${label}:${value}`).join('&') : '' const [isQuerying, { setTrue: startQuerying, setFalse: stopQuerying }] = useBoolean(false) const childProps = { @@ -161,6 +135,7 @@ const GenerationItem: FC = ({ controlClearMoreLikeThis, isWorkflow, siteInfo, + taskId, } const handleMoreLikeThis = async () => { @@ -178,19 +153,6 @@ const GenerationItem: FC = ({ stopQuerying() } - const mainStyle = (() => { - const res: React.CSSProperties = !isTop - ? { - background: depth % 2 === 0 ? 'linear-gradient(90.07deg, #F9FAFB 0.05%, rgba(249, 250, 251, 0) 99.93%)' : '#fff', - } - : {} - - if (hasChild) - res.boxShadow = '0px 1px 2px rgba(16, 24, 40, 0.05)' - - return res - })() - useEffect(() => { if (controlClearMoreLikeThis) { setChildMessageId(null) @@ -228,123 +190,125 @@ const GenerationItem: FC = ({ setShowPromptLogModal(true) } - const ratingContent = ( - <> - {!isWorkflow && !isError && messageId && !feedback?.rating && ( - - <> -
{ - onFeedback?.({ - rating: 'like', - }) - }} - className='flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-100'> - -
-
{ - onFeedback?.({ - rating: 'dislike', - }) - }} - className='flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-100'> - -
- - - )} - {!isWorkflow && !isError && messageId && feedback?.rating === 'like' && ( -
{ - onFeedback?.({ - rating: null, - }) - }} - className='flex w-7 h-7 items-center justify-center rounded-md cursor-pointer !text-primary-600 border border-primary-200 bg-primary-100 hover:border-primary-300 hover:bg-primary-200'> - -
- )} - {!isWorkflow && !isError && messageId && feedback?.rating === 'dislike' && ( -
{ - onFeedback?.({ - rating: null, - }) - }} - className='flex w-7 h-7 items-center justify-center rounded-md cursor-pointer !text-red-600 border border-red-200 bg-red-100 hover:border-red-300 hover:bg-red-200'> - -
- )} - - ) - const [currentTab, setCurrentTab] = useState('DETAIL') + const showResultTabs = !!workflowProcessData?.resultText || !!workflowProcessData?.files?.length + const switchTab = async (tab: string) => { + setCurrentTab(tab) + } + useEffect(() => { + if (workflowProcessData?.resultText || !!workflowProcessData?.files?.length) + switchTab('RESULT') + else + switchTab('DETAIL') + }, [workflowProcessData?.files?.length, workflowProcessData?.resultText]) return ( -
- {isLoading - ? ( -
- ) - : ( -
- {(isTop && taskId) && ( -
- - {taskId} -
) - } -
-
- {siteInfo && workflowProcessData && ( - - )} - {workflowProcessData && !isError && ( - - )} - {isError && ( -
{t('share.generation.batchFailed.outputPlaceholder')}
- )} - {!workflowProcessData && !isError && (typeof content === 'string') && ( + <> +
+ {isLoading && ( +
+ )} + {!isLoading && ( + <> + {/* result content */} +
+ {workflowProcessData && ( + <> +
+ {taskId && ( +
+ + {t('share.generation.execution')} + · + {taskId} +
+ )} + {siteInfo && workflowProcessData && ( + + )} + {showResultTabs && ( +
+
switchTab('RESULT')} + >{t('runLog.result')}
+
switchTab('DETAIL')} + >{t('runLog.detail')}
+
+ )} +
+ {!isError && ( + + )} + + )} + {!workflowProcessData && taskId && ( +
+ + {t('share.generation.execution')} + · + {`${taskId}${depth > 1 ? `-${depth - 1}` : ''}`} +
+ )} + {isError && ( +
{t('share.generation.batchFailed.outputPlaceholder')}
+ )} + {!workflowProcessData && !isError && (typeof content === 'string') && ( +
- )} -
+
+ )}
- -
-
- { - !isInWebApp && !isInstalledApp && !isResponding && ( - - - {!isMobile &&
{t('common.operation.log')}
} -
- ) - } - {((currentTab === 'RESULT' && workflowProcessData?.resultText) || !isWorkflow) && ( - { + {/* meta data */} +
+ {!isWorkflow && {content?.length} {t('common.unit.char')}} + {/* action buttons */} +
+ {!isInWebApp && !isInstalledApp && !isResponding && ( +
+ + + {/*
{t('common.operation.log')}
*/} +
+
+ )} +
+ {moreLikeThis && ( + + + + )} + {isShowTextToSpeech && ( + + )} + {((currentTab === 'RESULT' && workflowProcessData?.resultText) || !isWorkflow) && ( + { const copyContent = isWorkflow ? workflowProcessData?.resultText : content if (typeof copyContent === 'string') copy(copyContent) @@ -352,117 +316,68 @@ const GenerationItem: FC = ({ copy(JSON.stringify(copyContent)) Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') }) }}> - - {!isMobile &&
{t('common.operation.copy')}
} - - )} - - {isInWebApp && ( - <> - {!isWorkflow && ( - { onSave?.(messageId as string) }} - > - - {!isMobile &&
{t('common.operation.save')}
} -
+ +
+ )} + {isInWebApp && isError && ( + + + + )} + {isInWebApp && !isWorkflow && ( + { onSave?.(messageId as string) }}> + + + )} +
+ {(supportFeedback || isInWebApp) && !isWorkflow && !isError && messageId && ( +
+ {!feedback?.rating && ( + <> + onFeedback?.({ rating: 'like' })}> + + + onFeedback?.({ rating: 'dislike' })}> + + + )} - {(moreLikeThis && depth < MAX_DEPTH) && ( - - - {!isMobile &&
{t('appDebug.feature.moreLikeThis.title')}
} -
+ {feedback?.rating === 'like' && ( + onFeedback?.({ rating: null })}> + + )} - {isError && ( - - - {!isMobile &&
{t('share.generation.batchFailed.retry')}
} -
+ {feedback?.rating === 'dislike' && ( + onFeedback?.({ rating: null })}> + + )} - {!isError && messageId && !isWorkflow && ( -
- )} - {ratingContent} - - )} - - {supportAnnotation && ( - <> -
- { - - }} - onEdit={() => setIsShowReplyModal(true)} - onRemoved={() => { }} - /> - - )} - - setIsShowReplyModal(false)} - query={question} - answer={content} - onAdded={() => { }} - onEdited={() => { }} - createdAt={0} - onRemove={() => { }} - onlyEditResponse - /> - - {supportFeedback && ( -
- {ratingContent}
)} - - {isShowTextToSpeech && ( - <> -
- - - )} -
-
- {!workflowProcessData && ( -
{content?.length} {t('common.unit.char')}
- )}
- -
+ {/* more like this elements */} + {!isTop && ( +
+
+
+ +
+
+ )} + )} - +
{((childMessageId || isQuerying) && depth < 3) && ( -
- -
+ )} - -
+ ) } export default React.memo(GenerationItem) diff --git a/web/app/components/app/text-generate/item/result-tab.tsx b/web/app/components/app/text-generate/item/result-tab.tsx index 1e324d2096..7a9f6369a0 100644 --- a/web/app/components/app/text-generate/item/result-tab.tsx +++ b/web/app/components/app/text-generate/item/result-tab.tsx @@ -1,9 +1,6 @@ import { memo, - useEffect, } from 'react' -import { useTranslation } from 'react-i18next' -import cn from '@/utils/classnames' import { Markdown } from '@/app/components/base/markdown' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' @@ -14,79 +11,45 @@ const ResultTab = ({ data, content, currentTab, - onCurrentTabChange, }: { data?: WorkflowProcess content: any currentTab: string - onCurrentTabChange: (tab: string) => void }) => { - const { t } = useTranslation() - - const switchTab = async (tab: string) => { - onCurrentTabChange(tab) - } - useEffect(() => { - if (data?.resultText || !!data?.files?.length) - switchTab('RESULT') - else - switchTab('DETAIL') - }, [data?.files?.length, data?.resultText]) - return ( -
- {(data?.resultText || !!data?.files?.length) && ( -
-
switchTab('RESULT')} - >{t('runLog.result')}
-
switchTab('DETAIL')} - >{t('runLog.detail')}
+ <> + {currentTab === 'RESULT' && ( +
+ {data?.resultText && } + {!!data?.files?.length && ( +
+ {data?.files.map((item: any) => ( +
+
{item.varName}
+ +
+ ))} +
+ )}
)} -
- {currentTab === 'RESULT' && ( - <> - {data?.resultText && } - {!!data?.files?.length && ( -
- {data?.files.map((item: any) => ( -
-
{item.varName}
- -
- ))} -
- )} - - )} - {currentTab === 'DETAIL' && content && ( -
- JSON OUTPUT
} - language={CodeLanguage.json} - value={content} - isJSONStringifyBeauty - /> -
- )} -
-
+ {currentTab === 'DETAIL' && content && ( +
+ JSON OUTPUT
} + language={CodeLanguage.json} + value={content} + isJSONStringifyBeauty + /> +
+ )} + ) } diff --git a/web/app/components/app/text-generate/saved-items/index.tsx b/web/app/components/app/text-generate/saved-items/index.tsx index 8bfebbc17f..f3cc585fc4 100644 --- a/web/app/components/app/text-generate/saved-items/index.tsx +++ b/web/app/components/app/text-generate/saved-items/index.tsx @@ -1,15 +1,19 @@ 'use client' import type { FC } from 'react' import React from 'react' +import { + RiClipboardLine, + RiDeleteBinLine, +} from '@remixicon/react' import { useTranslation } from 'react-i18next' import copy from 'copy-to-clipboard' import NoData from './no-data' import cn from '@/utils/classnames' import type { SavedMessage } from '@/models/debug' import { Markdown } from '@/app/components/base/markdown' -import { SimpleBtn, copyIcon } from '@/app/components/app/text-generate/item' import Toast from '@/app/components/base/toast' -import AudioBtn from '@/app/components/base/audio-btn' +import ActionButton from '@/app/components/base/action-button' +import NewAudioButton from '@/app/components/base/new-audio-button' export type ISavedItemsProps = { className?: string @@ -19,12 +23,6 @@ export type ISavedItemsProps = { onStartCreateContent: () => void } -const removeIcon = ( - - - -) - const SavedItems: FC = ({ className, isShowTextToSpeech, @@ -35,56 +33,37 @@ const SavedItems: FC = ({ const { t } = useTranslation() return ( -
+
{list.length === 0 ? ( -
- -
+ ) : (<> {list.map(({ id, answer }) => ( -
- -
-
- { - copy(answer) - Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') }) - }}> - {copyIcon} -
{t('common.operation.copy')}
-
- - { - onRemove(id) - }}> - {removeIcon} -
{t('common.operation.remove')}
-
- - {isShowTextToSpeech && ( - <> -
- - - )} +
+
+ +
+
+ {answer.length} {t('common.unit.char')} +
+
+
+ {isShowTextToSpeech && } + { + copy(answer) + Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') }) + }}> + + + { + onRemove(id) + }}> + +
-
{answer?.length} {t('common.unit.char')}
))} diff --git a/web/app/components/app/text-generate/saved-items/no-data/index.tsx b/web/app/components/app/text-generate/saved-items/no-data/index.tsx index 0b087a647f..e10602a926 100644 --- a/web/app/components/app/text-generate/saved-items/no-data/index.tsx +++ b/web/app/components/app/text-generate/saved-items/no-data/index.tsx @@ -2,47 +2,38 @@ import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' -import { PlusIcon } from '@heroicons/react/24/outline' +import { + RiAddLine, + RiBookmark3Line, +} from '@remixicon/react' import Button from '@/app/components/base/button' export type INoDataProps = { onStartCreateContent: () => void } -const markIcon = ( - - - -) - -const lightIcon = ( - -) - const NoData: FC = ({ onStartCreateContent, }) => { const { t } = useTranslation() return ( -
-
- {markIcon} +
+
+
-
- {t('share.generation.savedNoData.title')} - {lightIcon} +
+ {t('share.generation.savedNoData.title')}
-
+
{t('share.generation.savedNoData.description')}
) diff --git a/web/app/components/base/action-button/index.css b/web/app/components/base/action-button/index.css index 13f333b11d..2cabe7aecc 100644 --- a/web/app/components/base/action-button/index.css +++ b/web/app/components/base/action-button/index.css @@ -5,6 +5,10 @@ @apply inline-flex justify-center items-center cursor-pointer text-text-tertiary hover:text-text-secondary hover:bg-state-base-hover } + .action-btn-hover { + @apply bg-state-base-hover + } + .action-btn-disabled { @apply cursor-not-allowed } diff --git a/web/app/components/base/action-button/index.tsx b/web/app/components/base/action-button/index.tsx index 845edfbd6d..c90d1a8de8 100644 --- a/web/app/components/base/action-button/index.tsx +++ b/web/app/components/base/action-button/index.tsx @@ -8,6 +8,7 @@ enum ActionButtonState { Active = 'active', Disabled = 'disabled', Default = '', + Hover = 'hover', } const actionButtonVariants = cva( @@ -41,6 +42,8 @@ function getActionButtonState(state: ActionButtonState) { return 'action-btn-active' case ActionButtonState.Disabled: return 'action-btn-disabled' + case ActionButtonState.Hover: + return 'action-btn-hover' default: return '' } diff --git a/web/app/components/base/audio-gallery/AudioPlayer.module.css b/web/app/components/base/audio-gallery/AudioPlayer.module.css deleted file mode 100644 index c5dd277fd6..0000000000 --- a/web/app/components/base/audio-gallery/AudioPlayer.module.css +++ /dev/null @@ -1,117 +0,0 @@ -.audioPlayer { - display: flex; - flex-direction: row; - align-items: center; - background-color: var(--color-components-chat-input-audio-bg-alt); - border-radius: 10px; - padding: 8px; - min-width: 240px; - max-width: 420px; - max-height: 40px; - backdrop-filter: blur(5px); - border: 1px solid var(--color-components-panel-border-subtle); - box-shadow: 0 1px 2px var(--color-shadow-shadow-3); - gap: 8px; -} - -.playButton { - display: inline-flex; - width: 16px; - height: 16px; - border-radius: 50%; - background-color: var(--color-components-button-primary-bg); - color: var(--color-components-chat-input-audio-bg-alt); - border: none; - cursor: pointer; - align-items: center; - justify-content: center; - transition: background-color 0.1s; - flex-shrink: 0; -} - -.playButton:hover { - background-color: var(--color-components-button-primary-bg-hover); -} - -.playButton:disabled { - background-color: var(--color-components-button-primary-bg-disabled); -} - -.audioControls { - flex-grow: 1; -} - -.progressBarContainer { - height: 32px; - display: flex; - align-items: center; - justify-content: center; -} - -.waveform { - position: relative; - display: flex; - cursor: pointer; - height: 24px; - width: 100%; - flex-grow: 1; - align-items: center; - justify-content: center; -} - -.progressBar { - position: absolute; - top: 0; - left: 0; - opacity: 0.5; - border-radius: 2px; - flex: none; - order: 55; - flex-grow: 0; - height: 100%; - background-color: rgba(66, 133, 244, 0.3); - pointer-events: none; -} - -.timeDisplay { - /* position: absolute; */ - color: var(--color-text-accent-secondary); - font-size: 12px; - order: 0; - height: 100%; - width: 50px; - display: inline-flex; - align-items: center; - justify-content: center; -} - -/* .currentTime { - position: absolute; - bottom: calc(100% + 5px); - transform: translateX(-50%); - background-color: rgba(255,255,255,.8); - padding: 2px 4px; - border-radius:10px; - box-shadow: 0 1px 5px rgba(0, 0, 0, 0.08); -} */ - -.duration { - padding: 2px 4px; - border-radius: 10px; -} - -.source_unavailable { - border: none; - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; - position: absolute; - color: #bdbdbf; -} - -.playButton svg path, -.playButton svg rect { - fill: currentColor; -} \ No newline at end of file diff --git a/web/app/components/base/audio-gallery/AudioPlayer.tsx b/web/app/components/base/audio-gallery/AudioPlayer.tsx index 95d4c69c83..d6d265c8d2 100644 --- a/web/app/components/base/audio-gallery/AudioPlayer.tsx +++ b/web/app/components/base/audio-gallery/AudioPlayer.tsx @@ -1,7 +1,13 @@ import React, { useCallback, useEffect, useRef, useState } from 'react' import { t } from 'i18next' -import styles from './AudioPlayer.module.css' +import { + RiPauseCircleFill, + RiPlayLargeFill, +} from '@remixicon/react' import Toast from '@/app/components/base/toast' +import { useAppContext } from '@/context/app-context' +import { Theme } from '@/types/app' +import cn from '@/utils/classnames' type AudioPlayerProps = { src: string @@ -18,6 +24,7 @@ const AudioPlayer: React.FC = ({ src }) => { const [hasStartedPlaying, setHasStartedPlaying] = useState(false) const [hoverTime, setHoverTime] = useState(0) const [isAudioAvailable, setIsAudioAvailable] = useState(true) + const { theme } = useAppContext() useEffect(() => { const audio = audioRef.current @@ -230,11 +237,11 @@ const AudioPlayer: React.FC = ({ src }) => { let color if (index * barWidth <= playedWidth) - color = '#296DFF' + color = theme === Theme.light ? '#296DFF' : '#84ABFF' else if ((index * barWidth / width) * duration <= hoverTime) - color = 'rgba(21,90,239,.40)' + color = theme === Theme.light ? 'rgba(21,90,239,.40)' : 'rgba(200, 206, 218, 0.28)' else - color = 'rgba(21,90,239,.20)' + color = theme === Theme.light ? 'rgba(21,90,239,.20)' : 'rgba(200, 206, 218, 0.14)' const barHeight = value * height const rectX = index * barWidth @@ -253,7 +260,7 @@ const AudioPlayer: React.FC = ({ src }) => { ctx.fillRect(rectX, rectY, rectWidth, rectHeight) } }) - }, [currentTime, duration, hoverTime, waveformData]) + }, [currentTime, duration, hoverTime, theme, waveformData]) useEffect(() => { drawWaveform() @@ -279,40 +286,32 @@ const AudioPlayer: React.FC = ({ src }) => { }, [duration]) return ( -
+