diff --git a/web/app/(commonLayout)/integrations/layout.tsx b/web/app/(commonLayout)/integrations/layout.tsx
new file mode 100644
index 0000000000..a6cd0ad11e
--- /dev/null
+++ b/web/app/(commonLayout)/integrations/layout.tsx
@@ -0,0 +1,12 @@
+'use client'
+
+import type { PropsWithChildren } from 'react'
+import { useTranslation } from 'react-i18next'
+import useDocumentTitle from '@/hooks/use-document-title'
+
+export default function IntegrationsLayout({ children }: PropsWithChildren) {
+ const { t } = useTranslation()
+ useDocumentTitle(t('mainNav.integrations', { ns: 'common' }))
+
+ return children
+}
diff --git a/web/app/(commonLayout)/page.tsx b/web/app/(commonLayout)/page.tsx
index 0cc61f1131..7242b21b36 100644
--- a/web/app/(commonLayout)/page.tsx
+++ b/web/app/(commonLayout)/page.tsx
@@ -1,7 +1,14 @@
+'use client'
+
import * as React from 'react'
+import { useTranslation } from 'react-i18next'
import AppList from '@/app/components/explore/app-list'
+import useDocumentTitle from '@/hooks/use-document-title'
const Home = () => {
+ const { t } = useTranslation()
+ useDocumentTitle(t('mainNav.home', { ns: 'common' }))
+
return
}
diff --git a/web/app/components/plugins/plugin-page/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-page/__tests__/index.spec.tsx
index 59d041f50c..a6ef8b11dd 100644
--- a/web/app/components/plugins/plugin-page/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/plugin-page/__tests__/index.spec.tsx
@@ -5,6 +5,7 @@ import { useQueryState } from 'nuqs'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { renderWithSystemFeatures } from '@/__tests__/utils/mock-system-features'
+import useDocumentTitle from '@/hooks/use-document-title'
import { usePluginInstallation } from '@/hooks/use-query-params'
// Import mocked modules for assertions
import { fetchBundleInfoFromMarketPlace, fetchManifestFromMarketPlace } from '@/service/plugins'
@@ -184,6 +185,20 @@ describe('PluginPage Component', () => {
expect(screen.getByText(/requestAPlugin/i)).toBeInTheDocument()
})
+ it('should use plugins as document title on plugins tab', () => {
+ vi.mocked(useQueryState).mockReturnValue(['plugins', vi.fn()])
+
+ render()
+ expect(useDocumentTitle).toHaveBeenCalledWith('plugin.metadata.title')
+ })
+
+ it('should use marketplace as document title when exploring marketplace', () => {
+ vi.mocked(useQueryState).mockReturnValue(['discover', vi.fn()])
+
+ render()
+ expect(useDocumentTitle).toHaveBeenCalledWith('common.mainNav.marketplace')
+ })
+
it('should render TabSlider', () => {
render()
// TabSlider renders tab options
diff --git a/web/app/components/plugins/plugin-page/index.tsx b/web/app/components/plugins/plugin-page/index.tsx
index acd25836ab..427cf997a3 100644
--- a/web/app/components/plugins/plugin-page/index.tsx
+++ b/web/app/components/plugins/plugin-page/index.tsx
@@ -57,7 +57,6 @@ const PluginPage = ({
}: PluginPageProps) => {
const { t } = useTranslation()
const docLink = useDocLink()
- useDocumentTitle(t('metadata.title', { ns: 'plugin' }))
// Use nuqs hook for installation state
const [{ packageId, bundleInfo }, setInstallState] = usePluginInstallation()
@@ -132,6 +131,9 @@ const PluginPage = ({
const values = Object.values(PLUGIN_TYPE_SEARCH_MAP)
return activeTab === PLUGIN_PAGE_TABS_MAP.marketplace || values.includes(activeTab)
}, [activeTab])
+ useDocumentTitle(isExploringMarketplace
+ ? t('mainNav.marketplace', { ns: 'common' })
+ : t('metadata.title', { ns: 'plugin' }))
const handleFileChange = (file: File | null) => {
if (!file || !file.name.endsWith('.difypkg')) {
diff --git a/web/app/components/tools/provider/__tests__/custom-create-card.spec.tsx b/web/app/components/tools/provider/__tests__/custom-create-card.spec.tsx
index d241bc8e96..c83af11761 100644
--- a/web/app/components/tools/provider/__tests__/custom-create-card.spec.tsx
+++ b/web/app/components/tools/provider/__tests__/custom-create-card.spec.tsx
@@ -136,7 +136,7 @@ describe('CustomCreateCard', () => {
render()
const docLink = screen.getByText('tools.swaggerAPIAsToolTip').closest('a')
- expect(docLink).toHaveAttribute('href', 'https://docs.dify.ai/en/use-dify/nodes/tools')
+ expect(docLink).toHaveAttribute('href', 'https://docs.dify.ai/en/use-dify/workspace/tools#custom-tool')
expect(docLink).toHaveAttribute('target', '_blank')
expect(docLink).toHaveAttribute('rel', 'noopener noreferrer')
})
diff --git a/web/app/components/tools/provider/custom-create-card.tsx b/web/app/components/tools/provider/custom-create-card.tsx
index fb9d870b76..f44b130711 100644
--- a/web/app/components/tools/provider/custom-create-card.tsx
+++ b/web/app/components/tools/provider/custom-create-card.tsx
@@ -1,24 +1,23 @@
'use client'
import type { CustomCollectionBackend } from '../types'
import { toast } from '@langgenius/dify-ui/toast'
-import { useMemo, useState } from 'react'
+import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
import { useAppContext } from '@/context/app-context'
-import { useDocLink } from '@/context/i18n'
import { createCustomCollection } from '@/service/tools'
import CreateEntryCard from './create-entry-card'
+const CUSTOM_TOOL_DOC_URL = 'https://docs.dify.ai/en/use-dify/workspace/tools#custom-tool'
+
type Props = {
onRefreshData: () => void
}
const Contribute = ({ onRefreshData }: Props) => {
const { t } = useTranslation()
- const docLink = useDocLink()
const { isCurrentWorkspaceManager } = useAppContext()
- const linkUrl = useMemo(() => docLink('/use-dify/nodes/tools'), [docLink])
const [isShowEditCollectionToolModal, setIsShowEditCustomCollectionModal] = useState(false)
const doCreateCustomToolCollection = async (data: CustomCollectionBackend) => {
await createCustomCollection(data)
@@ -33,7 +32,7 @@ const Contribute = ({ onRefreshData }: Props) => {
setIsShowEditCustomCollectionModal(true)}
/>
)}