diff --git a/web/app/components/rag-pipeline/components/rag-pipeline-header/index.tsx b/web/app/components/rag-pipeline/components/rag-pipeline-header/index.tsx
index 49656bd25a..87bd3e7adf 100644
--- a/web/app/components/rag-pipeline/components/rag-pipeline-header/index.tsx
+++ b/web/app/components/rag-pipeline/components/rag-pipeline-header/index.tsx
@@ -2,6 +2,7 @@ import { useMemo } from 'react'
import type { HeaderProps } from '@/app/components/workflow/header'
import Header from '@/app/components/workflow/header'
import InputFieldButton from './input-field-button'
+import Publisher from './publisher'
const RagPipelineHeader = () => {
const headerProps: HeaderProps = useMemo(() => {
@@ -9,6 +10,7 @@ const RagPipelineHeader = () => {
normal: {
components: {
left: ,
+ middle: ,
},
},
}
diff --git a/web/app/components/rag-pipeline/components/rag-pipeline-header/publisher/index.tsx b/web/app/components/rag-pipeline/components/rag-pipeline-header/publisher/index.tsx
new file mode 100644
index 0000000000..69da6e154e
--- /dev/null
+++ b/web/app/components/rag-pipeline/components/rag-pipeline-header/publisher/index.tsx
@@ -0,0 +1,35 @@
+import { useTranslation } from 'react-i18next'
+import { RiArrowDownSLine } from '@remixicon/react'
+import Button from '@/app/components/base/button'
+import {
+ PortalToFollowElem,
+ PortalToFollowElemContent,
+ PortalToFollowElemTrigger,
+} from '@/app/components/base/portal-to-follow-elem'
+import Popup from './popup'
+
+const Publisher = () => {
+ const { t } = useTranslation()
+
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Publisher
diff --git a/web/app/components/rag-pipeline/components/rag-pipeline-header/publisher/popup.tsx b/web/app/components/rag-pipeline/components/rag-pipeline-header/publisher/popup.tsx
new file mode 100644
index 0000000000..91042068df
--- /dev/null
+++ b/web/app/components/rag-pipeline/components/rag-pipeline-header/publisher/popup.tsx
@@ -0,0 +1,111 @@
+import {
+ useCallback,
+ useState,
+} from 'react'
+import {
+ RiArrowRightUpLine,
+ RiHammerLine,
+ RiPlayCircleLine,
+ RiTerminalBoxLine,
+} from '@remixicon/react'
+import { useTranslation } from 'react-i18next'
+import { useStore } from '@/app/components/workflow/store'
+import Button from '@/app/components/base/button'
+import { useFormatTimeFromNow } from '@/app/components/workflow/hooks'
+import Divider from '@/app/components/base/divider'
+
+const PUBLISH_SHORTCUT = ['⌘', '⇧', 'P']
+
+const Popup = () => {
+ const { t } = useTranslation()
+ const [published, setPublished] = useState(false)
+ const publishedAt = useStore(s => s.publishedAt)
+ const draftUpdatedAt = useStore(s => s.draftUpdatedAt)
+ const { formatTimeFromNow } = useFormatTimeFromNow()
+
+ const handlePublish = useCallback(async () => {
+ try {
+ setPublished(true)
+ }
+ catch {
+ setPublished(false)
+ }
+ }, [])
+
+ return (
+
+
+
+ {publishedAt ? t('workflow.common.latestPublished') : t('workflow.common.currentDraftUnpublished')}
+
+ {
+ publishedAt
+ ? (
+
+
+ {t('workflow.common.publishedAt')} {formatTimeFromNow(publishedAt)}
+
+
+ )
+ : (
+
+ {t('workflow.common.autoSaved')} · {Boolean(draftUpdatedAt) && formatTimeFromNow(draftUpdatedAt!)}
+
+ )
+ }
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default Popup