diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx index d46563c430b813..d7a2a615cbda01 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx @@ -166,7 +166,7 @@ const ConfigPopup: FC = ({
-
{t(`${I18N_PREFIX}.tracing`)}
+
{t(`${I18N_PREFIX}.tracing`)}
diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx index e6a520648f4e81..0800602924fc2c 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx @@ -172,7 +172,7 @@ const ProviderConfigModal: FC = ({
-
{t(`${I18N_PREFIX}.title`)}{t(`app.tracing.${type}.title`)}
+
{t(`${I18N_PREFIX}.title`)}{t(`app.tracing.${type}.title`)}
diff --git a/web/app/components/app/app-publisher/index.tsx b/web/app/components/app/app-publisher/index.tsx index 54853cf875b1e5..7dafaaf724b10b 100644 --- a/web/app/components/app/app-publisher/index.tsx +++ b/web/app/components/app/app-publisher/index.tsx @@ -5,9 +5,17 @@ import { } from 'react' import { useTranslation } from 'react-i18next' import dayjs from 'dayjs' -import { RiArrowDownSLine, RiPlanetLine } from '@remixicon/react' +import { + RiArrowDownSLine, + RiPlanetLine, + RiPlayCircleLine, + RiPlayList2Line, + RiTerminalBoxLine, +} from '@remixicon/react' +import { useKeyPress } from 'ahooks' import Toast from '../../base/toast' import type { ModelAndParameter } from '../configuration/debug/types' +import { getKeyboardKeyCodeBySystem } from '../../workflow/utils' import SuggestedAction from './suggested-action' import PublishWithMultipleModel from './publish-with-multiple-model' import Button from '@/app/components/base/button' @@ -20,10 +28,7 @@ import { fetchInstalledAppList } from '@/service/explore' import EmbeddedModal from '@/app/components/app/overview/embedded' import { useStore as useAppStore } from '@/app/components/app/store' import { useGetLanguage } from '@/context/i18n' -import { PlayCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' import { CodeBrowser } from '@/app/components/base/icons/src/vender/line/development' -import { LeftIndent02 } from '@/app/components/base/icons/src/vender/line/editor' -import { FileText } from '@/app/components/base/icons/src/vender/line/files' import WorkflowToolConfigureButton from '@/app/components/tools/workflow-tool/configure-button' import type { InputVar } from '@/app/components/workflow/types' import { appDefaultIconBackground } from '@/config' @@ -38,7 +43,6 @@ export type AppPublisherProps = { multipleModelConfigs?: ModelAndParameter[] /** modelAndParameter is passed when debugWithMultipleModel is true */ onPublish?: (modelAndParameter?: ModelAndParameter) => Promise | any - onRestore?: () => Promise | any onToggle?: (state: boolean) => void crossAxisOffset?: number toolPublished?: boolean @@ -46,6 +50,8 @@ export type AppPublisherProps = { onRefreshData?: () => void } +const PUBLISH_SHORTCUT = ['⌘', '⇧', 'P'] + const AppPublisher = ({ disabled = false, publishDisabled = false, @@ -54,7 +60,6 @@ const AppPublisher = ({ debugWithMultipleModel = false, multipleModelConfigs = [], onPublish, - onRestore, onToggle, crossAxisOffset = 0, toolPublished, @@ -84,14 +89,6 @@ const AppPublisher = ({ } } - const handleRestore = useCallback(async () => { - try { - await onRestore?.() - setOpen(false) - } - catch (e) { } - }, [onRestore]) - const handleTrigger = useCallback(() => { const state = !open @@ -122,6 +119,14 @@ const AppPublisher = ({ const [embeddingModalOpen, setEmbeddingModalOpen] = useState(false) + useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.shift.p`, (e) => { + e.preventDefault() + if (publishDisabled || published) + return + handlePublish() + } + , { exactMatch: true, useCapture: true }) + return ( -
+
{publishedAt ? t('workflow.common.latestPublished') : t('workflow.common.currentDraftUnpublished')}
{publishedAt ? ( -
-
- {t('workflow.common.publishedAt')} {formatTimeFromNow(publishedAt)} -
- +
+ {t('workflow.common.publishedAt')} {formatTimeFromNow(publishedAt)}
) : ( -
+
{t('workflow.common.autoSaved')} · {Boolean(draftUpdatedAt) && formatTimeFromNow(draftUpdatedAt!)}
)} @@ -187,20 +182,37 @@ const AppPublisher = ({ { published ? t('workflow.common.published') - : publishedAt ? t('workflow.common.update') : t('workflow.common.publish') + : ( +
+ {t('workflow.common.publishUpdate')} +
+ {PUBLISH_SHORTCUT.map(key => ( + + {key} + + ))} +
+
+ ) } ) }
-
- }>{t('workflow.common.runApp')} +
+ } + > + {t('workflow.common.runApp')} + {appDetail?.mode === 'workflow' ? ( } + icon={} > {t('workflow.common.batchRunApp')} @@ -226,7 +238,13 @@ const AppPublisher = ({ > {t('workflow.common.openInExplore')} - }>{t('workflow.common.accessAPIReference')} + } + > + {t('workflow.common.accessAPIReference')} + {appDetail?.mode === 'workflow' && ( & { icon?: React.ReactNode @@ -14,15 +14,15 @@ const SuggestedAction = ({ icon, link, disabled, children, className, ...props } target='_blank' rel='noreferrer' className={classNames( - 'flex justify-start items-center gap-2 h-[34px] px-2.5 bg-background-section-burn rounded-lg transition-colors text-text-secondary [&:not(:first-child)]:mt-1', - disabled ? 'shadow-xs opacity-30 cursor-not-allowed' : 'hover:bg-state-accent-hover hover:text-text-accent cursor-pointer', + 'flex justify-start items-center gap-2 py-2 px-2.5 bg-background-section-burn rounded-lg transition-colors [&:not(:first-child)]:mt-1', + disabled ? 'shadow-xs opacity-30 cursor-not-allowed' : 'text-text-secondary hover:bg-state-accent-hover hover:text-text-accent cursor-pointer', className, )} {...props} >
{icon}
-
{children}
- +
{children}
+ ) diff --git a/web/app/components/app/app-publisher/version-info-modal.tsx b/web/app/components/app/app-publisher/version-info-modal.tsx new file mode 100644 index 00000000000000..960560499f41e7 --- /dev/null +++ b/web/app/components/app/app-publisher/version-info-modal.tsx @@ -0,0 +1,112 @@ +import React, { type FC, useCallback, useState } from 'react' +import Modal from '@/app/components/base/modal' +import type { VersionHistory } from '@/types/workflow' +import { useTranslation } from 'react-i18next' +import { RiCloseLine } from '@remixicon/react' +import Input from '../../base/input' +import Textarea from '../../base/textarea' +import Button from '../../base/button' +import Toast from '@/app/components/base/toast' + +type VersionInfoModalProps = { + isOpen: boolean + versionInfo: VersionHistory + onClose: () => void + onPublish: (params: { title: string; releaseNotes: string }) => void +} + +const TITLE_MAX_LENGTH = 15 +const RELEASE_NOTES_MAX_LENGTH = 100 + +const VersionInfoModal: FC = ({ + isOpen, + versionInfo, + onClose, + onPublish, +}) => { + const { t } = useTranslation() + const [title, setTitle] = useState(versionInfo.marked_name || '') + const [releaseNotes, setReleaseNotes] = useState(versionInfo.marked_comment || '') + const [titleError, setTitleError] = useState(false) + const [releaseNotesError, setReleaseNotesError] = useState(false) + + const handlePublish = () => { + if (title.length > TITLE_MAX_LENGTH) { + setTitleError(true) + Toast.notify({ + type: 'error', + message: t('workflow.versionHistory.editField.titleLengthLimit', { limit: TITLE_MAX_LENGTH }), + }) + return + } + else { + titleError && setTitleError(false) + } + + if (releaseNotes.length > RELEASE_NOTES_MAX_LENGTH) { + setReleaseNotesError(true) + Toast.notify({ + type: 'error', + message: t('workflow.versionHistory.editField.releaseNotesLengthLimit', { limit: TITLE_MAX_LENGTH }), + }) + return + } + else { + releaseNotesError && setReleaseNotesError(false) + } + + onPublish({ title, releaseNotes }) + onClose() + } + + const handleTitleChange = useCallback((e: React.ChangeEvent) => { + setTitle(e.target.value) + }, []) + + const handleDescriptionChange = useCallback((e: React.ChangeEvent) => { + setReleaseNotes(e.target.value) + }, []) + + return +
+
+ {versionInfo.marked_name ? t('workflow.versionHistory.editVersionInfo') : t('workflow.versionHistory.nameThisVersion')} +
+
+ +
+
+
+
+
+ {t('workflow.versionHistory.editField.title')} +
+ +
+
+
+ {t('workflow.versionHistory.editField.releaseNotes')} +
+