From d4f5a113ed0f2db298ca2d701036d30ff4dbba03 Mon Sep 17 00:00:00 2001 From: Bowen Liang Date: Wed, 21 Jan 2026 15:07:32 +0800 Subject: [PATCH 01/17] chore(web): refactor next.config.js to next.config.ts (#31331) --- web/{next.config.js => next.config.ts} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename web/{next.config.js => next.config.ts} (88%) diff --git a/web/next.config.js b/web/next.config.ts similarity index 88% rename from web/next.config.js rename to web/next.config.ts index 1457d638c4..fc4dee3289 100644 --- a/web/next.config.js +++ b/web/next.config.ts @@ -1,3 +1,4 @@ +import type { NextConfig } from 'next' import process from 'node:process' import withBundleAnalyzerInit from '@next/bundle-analyzer' import createMDX from '@next/mdx' @@ -24,10 +25,9 @@ const withBundleAnalyzer = withBundleAnalyzerInit({ const hasSetWebPrefix = process.env.NEXT_PUBLIC_WEB_PREFIX const port = process.env.PORT || 3000 const locImageURLs = !hasSetWebPrefix ? [new URL(`http://localhost:${port}/**`), new URL(`http://127.0.0.1:${port}/**`)] : [] -const remoteImageURLs = [hasSetWebPrefix ? new URL(`${process.env.NEXT_PUBLIC_WEB_PREFIX}/**`) : '', ...locImageURLs].filter(item => !!item) +const remoteImageURLs = ([hasSetWebPrefix ? new URL(`${process.env.NEXT_PUBLIC_WEB_PREFIX}/**`) : '', ...locImageURLs].filter(item => !!item)) as URL[] -/** @type {import('next').NextConfig} */ -const nextConfig = { +const nextConfig: NextConfig = { basePath: process.env.NEXT_PUBLIC_BASE_PATH || '', serverExternalPackages: ['esbuild-wasm'], transpilePackages: ['echarts', 'zrender'], @@ -42,7 +42,7 @@ const nextConfig = { // https://nextjs.org/docs/messages/next-image-unconfigured-host images: { remotePatterns: remoteImageURLs.map(remoteImageURL => ({ - protocol: remoteImageURL.protocol.replace(':', ''), + protocol: remoteImageURL.protocol.replace(':', '') as 'http' | 'https', hostname: remoteImageURL.hostname, port: remoteImageURL.port, pathname: remoteImageURL.pathname, From ed0e068a47f90c1e5ce8118f1a43307eafa7c253 Mon Sep 17 00:00:00 2001 From: Coding On Star <447357187@qq.com> Date: Wed, 21 Jan 2026 15:47:49 +0800 Subject: [PATCH 02/17] fix(i18n): update model provider tip to only mention OpenAI in English, Japanese, and Simplified Chinese translations (#31339) Co-authored-by: CodingOnStar --- web/i18n/en-US/common.json | 2 +- web/i18n/ja-JP/common.json | 2 +- web/i18n/zh-Hans/common.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/i18n/en-US/common.json b/web/i18n/en-US/common.json index d2e5281282..20e5400e56 100644 --- a/web/i18n/en-US/common.json +++ b/web/i18n/en-US/common.json @@ -350,7 +350,7 @@ "modelProvider.card.quota": "QUOTA", "modelProvider.card.quotaExhausted": "Quota exhausted", "modelProvider.card.removeKey": "Remove API Key", - "modelProvider.card.tip": "Message Credits supports models from OpenAI, Anthropic, Gemini, xAI, DeepSeek and Tongyi. Priority will be given to the paid quota. The free quota will be used after the paid quota is exhausted.", + "modelProvider.card.tip": "Message Credits supports models from OpenAI. Priority will be given to the paid quota. The free quota will be used after the paid quota is exhausted.", "modelProvider.card.tokens": "Tokens", "modelProvider.collapse": "Collapse", "modelProvider.config": "Config", diff --git a/web/i18n/ja-JP/common.json b/web/i18n/ja-JP/common.json index ffc2d0bd31..8a76021759 100644 --- a/web/i18n/ja-JP/common.json +++ b/web/i18n/ja-JP/common.json @@ -350,7 +350,7 @@ "modelProvider.card.quota": "クォータ", "modelProvider.card.quotaExhausted": "クォータが使い果たされました", "modelProvider.card.removeKey": "API キーを削除", - "modelProvider.card.tip": "メッセージ枠はOpenAI、Anthropic、Gemini、xAI、DeepSeek、Tongyiのモデルを使用することをサポートしています。無料枠は有料枠が使い果たされた後に消費されます。", + "modelProvider.card.tip": "メッセージ枠はOpenAIのモデルを使用することをサポートしています。無料枠は有料枠が使い果たされた後に消費されます。", "modelProvider.card.tokens": "トークン", "modelProvider.collapse": "折り畳み", "modelProvider.config": "設定", diff --git a/web/i18n/zh-Hans/common.json b/web/i18n/zh-Hans/common.json index b5eabfeecc..6f62b53e2d 100644 --- a/web/i18n/zh-Hans/common.json +++ b/web/i18n/zh-Hans/common.json @@ -350,7 +350,7 @@ "modelProvider.card.quota": "额度", "modelProvider.card.quotaExhausted": "配额已用完", "modelProvider.card.removeKey": "删除 API 密钥", - "modelProvider.card.tip": "消息额度支持使用 OpenAI、Anthropic、Gemini、xAI、深度求索、通义 的模型;免费额度会在付费额度用尽后才会消耗。", + "modelProvider.card.tip": "消息额度支持使用 OpenAI 的模型;免费额度会在付费额度用尽后才会消耗。", "modelProvider.card.tokens": "Tokens", "modelProvider.collapse": "收起", "modelProvider.config": "配置", From 146ee4d3e9a9d000e993d05bb0dba1eaa00c9b36 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:15:58 +0800 Subject: [PATCH 03/17] chore(i18n): sync translations with en-US (#31332) Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com> --- web/i18n/ar-TN/billing.json | 1 + web/i18n/de-DE/billing.json | 1 + web/i18n/es-ES/billing.json | 1 + web/i18n/fa-IR/billing.json | 1 + web/i18n/fr-FR/billing.json | 1 + web/i18n/hi-IN/billing.json | 1 + web/i18n/id-ID/billing.json | 1 + web/i18n/it-IT/billing.json | 1 + web/i18n/ko-KR/billing.json | 1 + web/i18n/pl-PL/billing.json | 1 + web/i18n/pt-BR/billing.json | 1 + web/i18n/ro-RO/billing.json | 1 + web/i18n/ru-RU/billing.json | 1 + web/i18n/sl-SI/billing.json | 1 + web/i18n/th-TH/billing.json | 1 + web/i18n/tr-TR/billing.json | 1 + web/i18n/uk-UA/billing.json | 1 + web/i18n/vi-VN/billing.json | 1 + web/i18n/zh-Hant/billing.json | 1 + 19 files changed, 19 insertions(+) diff --git a/web/i18n/ar-TN/billing.json b/web/i18n/ar-TN/billing.json index a67f8216a3..24bc5d2d58 100644 --- a/web/i18n/ar-TN/billing.json +++ b/web/i18n/ar-TN/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "حصة رفع المستندات", "usagePage.perMonth": "شهريًا", "usagePage.resetsIn": "يتم إعادة التعيين في {{count,number}} أيام", + "usagePage.storageThresholdTooltip": "يتم عرض الاستخدام التفصيلي بمجرد أن تتجاوز مساحة التخزين 50 ميجابايت.", "usagePage.teamMembers": "أعضاء الفريق", "usagePage.triggerEvents": "أحداث المشغل", "usagePage.vectorSpace": "تخزين بيانات المعرفة", diff --git a/web/i18n/de-DE/billing.json b/web/i18n/de-DE/billing.json index 31d9150135..f44605984d 100644 --- a/web/i18n/de-DE/billing.json +++ b/web/i18n/de-DE/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Dokumenten-Upload-Quota", "usagePage.perMonth": "pro Monat", "usagePage.resetsIn": "Setzt in {{count,number}} Tagen zurück", + "usagePage.storageThresholdTooltip": "Die detaillierte Nutzung wird angezeigt, sobald der Speicher 50 MB überschreitet.", "usagePage.teamMembers": "Teammitglieder", "usagePage.triggerEvents": "Auslöser-Ereignisse", "usagePage.vectorSpace": "Wissensdatenbank", diff --git a/web/i18n/es-ES/billing.json b/web/i18n/es-ES/billing.json index 7e5c4ed1de..04150901fc 100644 --- a/web/i18n/es-ES/billing.json +++ b/web/i18n/es-ES/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Cuota de carga de documentos", "usagePage.perMonth": "por mes", "usagePage.resetsIn": "Se reinicia en {{count,number}} días", + "usagePage.storageThresholdTooltip": "El uso detallado se muestra una vez que el almacenamiento supera los 50 MB.", "usagePage.teamMembers": "Miembros del equipo", "usagePage.triggerEvents": "Eventos desencadenantes", "usagePage.vectorSpace": "Almacenamiento de Datos de Conocimiento", diff --git a/web/i18n/fa-IR/billing.json b/web/i18n/fa-IR/billing.json index 0cd2e28106..c7790411af 100644 --- a/web/i18n/fa-IR/billing.json +++ b/web/i18n/fa-IR/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "حجم بارگذاری اسناد", "usagePage.perMonth": "در ماه", "usagePage.resetsIn": "در {{count,number}} روز بازنشانی می‌شود", + "usagePage.storageThresholdTooltip": "جزئیات استفاده زمانی نمایش داده می‌شود که فضای ذخیره‌سازی از 50 مگابایت بیشتر شود.", "usagePage.teamMembers": "اعضای تیم", "usagePage.triggerEvents": "رویدادهای محرک", "usagePage.vectorSpace": "ذخیره‌سازی داده‌های دانش", diff --git a/web/i18n/fr-FR/billing.json b/web/i18n/fr-FR/billing.json index 0c67b010d8..34db09bb32 100644 --- a/web/i18n/fr-FR/billing.json +++ b/web/i18n/fr-FR/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Quota de téléchargement de documents", "usagePage.perMonth": "par mois", "usagePage.resetsIn": "Réinitialisations dans {{count,number}} jours", + "usagePage.storageThresholdTooltip": "L'utilisation détaillée est affichée lorsque le stockage dépasse 50 Mo.", "usagePage.teamMembers": "Membres de l'équipe", "usagePage.triggerEvents": "Événements déclencheurs", "usagePage.vectorSpace": "Stockage de données de connaissance", diff --git a/web/i18n/hi-IN/billing.json b/web/i18n/hi-IN/billing.json index 37c6555640..5ab7130ad0 100644 --- a/web/i18n/hi-IN/billing.json +++ b/web/i18n/hi-IN/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "दस्तावेज़ अपलोड कोटा", "usagePage.perMonth": "प्रति माह", "usagePage.resetsIn": "{{count,number}} दिनों में रीसेट होता है", + "usagePage.storageThresholdTooltip": "स्टोरेज 50 MB से अधिक होने पर विस्तृत उपयोग दिखाया जाता है।", "usagePage.teamMembers": "टीम के सदस्य", "usagePage.triggerEvents": "उत्तेजक घटनाएँ", "usagePage.vectorSpace": "ज्ञान डेटा भंडारण", diff --git a/web/i18n/id-ID/billing.json b/web/i18n/id-ID/billing.json index f912cf1960..920836d467 100644 --- a/web/i18n/id-ID/billing.json +++ b/web/i18n/id-ID/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Kuota Unggah Dokumen", "usagePage.perMonth": "per bulan", "usagePage.resetsIn": "Diatur ulang dalam {{count,number}} hari", + "usagePage.storageThresholdTooltip": "Penggunaan terperinci ditampilkan setelah penyimpanan melebihi 50 MB.", "usagePage.teamMembers": "Anggota Tim", "usagePage.triggerEvents": "Pemicu Acara", "usagePage.vectorSpace": "Penyimpanan Data Pengetahuan", diff --git a/web/i18n/it-IT/billing.json b/web/i18n/it-IT/billing.json index fdf2547374..7626232646 100644 --- a/web/i18n/it-IT/billing.json +++ b/web/i18n/it-IT/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Quota di Caricamento Documenti", "usagePage.perMonth": "al mese", "usagePage.resetsIn": "Si resetta tra {{count,number}} giorni", + "usagePage.storageThresholdTooltip": "L'utilizzo dettagliato viene visualizzato quando lo spazio di archiviazione supera i 50 MB.", "usagePage.teamMembers": "Membri del team", "usagePage.triggerEvents": "Eventi di attivazione", "usagePage.vectorSpace": "Archiviazione dei dati conoscitivi", diff --git a/web/i18n/ko-KR/billing.json b/web/i18n/ko-KR/billing.json index 318435d63d..602f5c7407 100644 --- a/web/i18n/ko-KR/billing.json +++ b/web/i18n/ko-KR/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "문서 업로드 한도", "usagePage.perMonth": "월별", "usagePage.resetsIn": "{{count,number}}일 후 초기화", + "usagePage.storageThresholdTooltip": "저장 공간이 50 MB를 초과하면 세부 사용량이 표시됩니다.", "usagePage.teamMembers": "팀원들", "usagePage.triggerEvents": "트리거 이벤트", "usagePage.vectorSpace": "지식 데이터 저장소", diff --git a/web/i18n/pl-PL/billing.json b/web/i18n/pl-PL/billing.json index 913778e91d..62381ab9ba 100644 --- a/web/i18n/pl-PL/billing.json +++ b/web/i18n/pl-PL/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Limit przesyłania dokumentów", "usagePage.perMonth": "miesięcznie", "usagePage.resetsIn": "Resetuje się za {{count,number}} dni", + "usagePage.storageThresholdTooltip": "Szczegółowe użycie jest wyświetlane, gdy przestrzeń dyskowa przekracza 50 MB.", "usagePage.teamMembers": "Członkowie zespołu", "usagePage.triggerEvents": "Wydarzenia wyzwalające", "usagePage.vectorSpace": "Magazynowanie danych wiedzy", diff --git a/web/i18n/pt-BR/billing.json b/web/i18n/pt-BR/billing.json index 8e447d0c17..6a4a0b0eb1 100644 --- a/web/i18n/pt-BR/billing.json +++ b/web/i18n/pt-BR/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Cota de Upload de Documentos", "usagePage.perMonth": "por mês", "usagePage.resetsIn": "Reinicia em {{count,number}} dias", + "usagePage.storageThresholdTooltip": "O uso detalhado é exibido quando o armazenamento excede 50 MB.", "usagePage.teamMembers": "Membros da equipe", "usagePage.triggerEvents": "Eventos de Gatilho", "usagePage.vectorSpace": "Armazenamento de Dados do Conhecimento", diff --git a/web/i18n/ro-RO/billing.json b/web/i18n/ro-RO/billing.json index 99fcb93a4e..41bb429905 100644 --- a/web/i18n/ro-RO/billing.json +++ b/web/i18n/ro-RO/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Cota de încărcare a documentelor", "usagePage.perMonth": "pe lună", "usagePage.resetsIn": "Se resetează în {{count,number}} zile", + "usagePage.storageThresholdTooltip": "Utilizarea detaliată este afișată odată ce spațiul de stocare depășește 50 MB.", "usagePage.teamMembers": "Membrii echipei", "usagePage.triggerEvents": "Evenimente declanșatoare", "usagePage.vectorSpace": "Stocarea datelor de cunoștințe", diff --git a/web/i18n/ru-RU/billing.json b/web/i18n/ru-RU/billing.json index 722953747e..c5a526418a 100644 --- a/web/i18n/ru-RU/billing.json +++ b/web/i18n/ru-RU/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Квота на загрузку документов", "usagePage.perMonth": "в месяц", "usagePage.resetsIn": "Сброс через {{count,number}} дней", + "usagePage.storageThresholdTooltip": "Подробные данные об использовании отображаются после превышения 50 МБ хранилища.", "usagePage.teamMembers": "Члены команды", "usagePage.triggerEvents": "Триггерные события", "usagePage.vectorSpace": "Хранилище данных знаний", diff --git a/web/i18n/sl-SI/billing.json b/web/i18n/sl-SI/billing.json index c9bbbf8043..6409a7aedb 100644 --- a/web/i18n/sl-SI/billing.json +++ b/web/i18n/sl-SI/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Kvota za nalaganje dokumentov", "usagePage.perMonth": "na mesec", "usagePage.resetsIn": "Ponastavitve čez {{count,number}} dni", + "usagePage.storageThresholdTooltip": "Podrobna uporaba se prikaže, ko shramba preseže 50 MB.", "usagePage.teamMembers": "Člani ekipe", "usagePage.triggerEvents": "Sprožilni dogodki", "usagePage.vectorSpace": "Shranjevanje podatkov znanja", diff --git a/web/i18n/th-TH/billing.json b/web/i18n/th-TH/billing.json index ec1cbf501f..b0d6eadafd 100644 --- a/web/i18n/th-TH/billing.json +++ b/web/i18n/th-TH/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "โควต้าการอัปโหลดเอกสาร", "usagePage.perMonth": "ต่อเดือน", "usagePage.resetsIn": "รีเซ็ตในอีก {{count,number}} วัน", + "usagePage.storageThresholdTooltip": "รายละเอียดการใช้งานจะแสดงเมื่อพื้นที่จัดเก็บเกิน 50 MB", "usagePage.teamMembers": "สมาชิกในทีม", "usagePage.triggerEvents": "เหตุการณ์กระตุ้น", "usagePage.vectorSpace": "การจัดเก็บข้อมูลความรู้", diff --git a/web/i18n/tr-TR/billing.json b/web/i18n/tr-TR/billing.json index 036f3e98c3..b780045768 100644 --- a/web/i18n/tr-TR/billing.json +++ b/web/i18n/tr-TR/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Belgeler Yükleme Kotası", "usagePage.perMonth": "ayda", "usagePage.resetsIn": "{{count,number}} gün içinde sıfırlanır", + "usagePage.storageThresholdTooltip": "Depolama alanı 50 MB'yi aştığında ayrıntılı kullanım gösterilir.", "usagePage.teamMembers": "Ekip Üyeleri", "usagePage.triggerEvents": "Tetikleyici Olaylar", "usagePage.vectorSpace": "Bilgi Veri Depolama", diff --git a/web/i18n/uk-UA/billing.json b/web/i18n/uk-UA/billing.json index 7fe974c96e..223eccb699 100644 --- a/web/i18n/uk-UA/billing.json +++ b/web/i18n/uk-UA/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Квота на завантаження документів", "usagePage.perMonth": "на місяць", "usagePage.resetsIn": "Скидання через {{count,number}} днів", + "usagePage.storageThresholdTooltip": "Детальне використання відображається після перевищення 50 МБ сховища.", "usagePage.teamMembers": "Члени команди", "usagePage.triggerEvents": "Тригерні події", "usagePage.vectorSpace": "Сховище даних знань", diff --git a/web/i18n/vi-VN/billing.json b/web/i18n/vi-VN/billing.json index ca792318fa..e111c94082 100644 --- a/web/i18n/vi-VN/billing.json +++ b/web/i18n/vi-VN/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "Hạn ngạch tải lên tài liệu", "usagePage.perMonth": "mỗi tháng", "usagePage.resetsIn": "Đặt lại sau {{count,number}} ngày", + "usagePage.storageThresholdTooltip": "Thông tin sử dụng chi tiết sẽ được hiển thị khi dung lượng lưu trữ vượt quá 50 MB.", "usagePage.teamMembers": "Các thành viên trong nhóm", "usagePage.triggerEvents": "Các sự kiện kích hoạt", "usagePage.vectorSpace": "Lưu trữ dữ liệu kiến thức", diff --git a/web/i18n/zh-Hant/billing.json b/web/i18n/zh-Hant/billing.json index 1b343d814a..20277ca50c 100644 --- a/web/i18n/zh-Hant/billing.json +++ b/web/i18n/zh-Hant/billing.json @@ -172,6 +172,7 @@ "usagePage.documentsUploadQuota": "文件上傳配額", "usagePage.perMonth": "每月", "usagePage.resetsIn": "{{count,number}} 天後重置", + "usagePage.storageThresholdTooltip": "儲存空間超過 50 MB 後,將顯示詳細使用情況。", "usagePage.teamMembers": "團隊成員", "usagePage.triggerEvents": "觸發事件", "usagePage.vectorSpace": "知識數據儲存", From 061feebd87099f0bc0129d7e61d720cf88a6f96e Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:31:48 +0800 Subject: [PATCH 04/17] fix: check and update doc links (#30849) Co-authored-by: Riskey <36894937+RiskeyL@users.noreply.github.com> --- web/README.md | 2 +- .../[appId]/overview/card-view.tsx | 17 +- .../conversation-history/history-panel.tsx | 11 - .../settings-modal/retrieval-section.spec.tsx | 9 +- .../settings-modal/retrieval-section.tsx | 5 +- .../tools/external-data-tool-modal.tsx | 2 +- .../components/app/create-app-modal/index.tsx | 18 - web/app/components/app/overview/app-card.tsx | 2 +- .../app/overview/customize/index.tsx | 2 +- .../app/overview/settings/index.tsx | 12 - .../components/app/overview/trigger-card.tsx | 2 +- .../base/features/new-feature-panel/index.tsx | 11 - .../moderation/moderation-setting-modal.tsx | 2 +- .../datasets/create/step-three/index.spec.tsx | 2 +- .../datasets/create/step-three/index.tsx | 2 +- .../components/indexing-mode-section.tsx | 2 +- .../documents/components/documents-header.tsx | 2 +- .../data-source/online-documents/index.tsx | 2 +- .../data-source/online-drive/index.spec.tsx | 2 +- .../data-source/online-drive/index.tsx | 2 +- .../data-source/website-crawl/index.tsx | 2 +- .../processing/index.spec.tsx | 2 +- .../create-from-pipeline/processing/index.tsx | 2 +- .../external-api/external-api-modal/Form.tsx | 2 +- .../external-api-panel/index.spec.tsx | 2 +- .../external-api/external-api-panel/index.tsx | 2 +- .../create/InfoPanel.tsx | 4 +- .../create/index.spec.tsx | 2 +- .../external-knowledge-base/create/index.tsx | 2 +- .../hit-testing/modify-retrieval-modal.tsx | 5 +- .../datasets/no-linked-apps-panel.tsx | 2 +- .../datasets/settings/form/index.tsx | 7 +- .../header/account-dropdown/index.tsx | 2 +- .../api-based-extension-page/empty.tsx | 2 +- .../api-based-extension-page/modal.tsx | 2 +- .../plugin-detail-panel/endpoint-list.tsx | 2 +- .../plugins/plugin-page/debug-info.tsx | 7 +- .../components/plugins/plugin-page/index.tsx | 7 +- web/app/components/plugins/utils.ts | 13 - .../rag-pipeline-header/publisher/popup.tsx | 4 +- .../hooks/use-available-nodes-meta-data.ts | 15 +- web/app/components/tools/mcp/create-card.tsx | 14 +- .../components/tools/mcp/mcp-service-card.tsx | 2 +- .../tools/provider/custom-create-card.tsx | 22 +- .../workflow-onboarding-modal/index.tsx | 11 - .../hooks/use-available-nodes-meta-data.ts | 3 +- .../nodes/_base/components/agent-strategy.tsx | 5 +- .../components/error-handle/default-value.tsx | 11 - .../error-handle/fail-branch-card.tsx | 2 +- .../variable/var-reference-popup.tsx | 14 +- .../chunk-structure/instruction/index.tsx | 2 +- .../components/retrieval-setting/index.tsx | 4 +- .../json-schema-config.tsx | 14 +- .../panel/chat-variable-panel/index.tsx | 13 - web/app/components/workflow/run/node.tsx | 2 +- web/app/components/workflow/run/status.tsx | 2 +- .../workflow/variable-inspect/empty.tsx | 4 +- .../education-apply/education-apply-page.tsx | 2 +- .../education-apply/expire-notice-modal.tsx | 2 +- .../education-apply/verify-state-modal.tsx | 2 +- web/app/install/installForm.tsx | 5 +- web/app/signin/invite-settings/page.tsx | 5 +- web/app/signin/one-more-step.tsx | 6 +- web/constants/link.ts | 1 + web/context/i18n.ts | 19 +- web/eslint.config.mjs | 2 +- web/hooks/use-api-access-url.ts | 15 +- web/i18n-config/language.ts | 9 +- web/package.json | 1 + web/scripts/gen-doc-paths.ts | 433 ++++++++++++++++++ web/types/doc-paths.ts | 316 +++++++++++++ 71 files changed, 858 insertions(+), 282 deletions(-) create mode 100644 web/constants/link.ts create mode 100644 web/scripts/gen-doc-paths.ts create mode 100644 web/types/doc-paths.ts diff --git a/web/README.md b/web/README.md index 13780eec6c..9c731a081a 100644 --- a/web/README.md +++ b/web/README.md @@ -138,7 +138,7 @@ This will help you determine the testing strategy. See [web/testing/testing.md]( ## Documentation -Visit to view the full documentation. +Visit to view the full documentation. ## Community diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx index 81b4f2474e..f07b2932c9 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx @@ -5,7 +5,6 @@ import type { BlockEnum } from '@/app/components/workflow/types' import type { UpdateAppSiteCodeResponse } from '@/models/app' import type { App } from '@/types/app' import type { I18nKeysByPrefix } from '@/types/i18n' -import * as React from 'react' import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' @@ -17,7 +16,6 @@ import { ToastContext } from '@/app/components/base/toast' import MCPServiceCard from '@/app/components/tools/mcp/mcp-service-card' import { isTriggerNode } from '@/app/components/workflow/types' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' -import { useDocLink } from '@/context/i18n' import { fetchAppDetail, updateAppSiteAccessToken, @@ -36,7 +34,6 @@ export type ICardViewProps = { const CardView: FC = ({ appId, isInPanel, className }) => { const { t } = useTranslation() - const docLink = useDocLink() const { notify } = useContext(ToastContext) const appDetail = useAppStore(state => state.appDetail) const setAppDetail = useAppStore(state => state.setAppDetail) @@ -59,25 +56,13 @@ const CardView: FC = ({ appId, isInPanel, className }) => { const shouldRenderAppCards = !isWorkflowApp || hasTriggerNode === false const disableAppCards = !shouldRenderAppCards - const triggerDocUrl = docLink('/guides/workflow/node/start') const buildTriggerModeMessage = useCallback((featureName: string) => (
{t('overview.disableTooltip.triggerMode', { ns: 'appOverview', feature: featureName })}
- { - event.stopPropagation() - }} - > - {t('overview.appInfo.enableTooltip.learnMore', { ns: 'appOverview' })} -
- ), [t, triggerDocUrl]) + ), [t]) const disableWebAppTooltip = disableAppCards ? buildTriggerModeMessage(t('overview.appInfo.title', { ns: 'appOverview' })) diff --git a/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx b/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx index b0b042b2a5..44c4fc8f46 100644 --- a/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx +++ b/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx @@ -5,7 +5,6 @@ import { useTranslation } from 'react-i18next' import Panel from '@/app/components/app/configuration/base/feature-panel' import OperationBtn from '@/app/components/app/configuration/base/operation-btn' import { MessageClockCircle } from '@/app/components/base/icons/src/vender/solid/general' -import { useDocLink } from '@/context/i18n' type Props = { showWarning: boolean @@ -17,8 +16,6 @@ const HistoryPanel: FC = ({ onShowEditModal, }) => { const { t } = useTranslation() - const docLink = useDocLink() - return ( = ({
{t('feature.conversationHistory.tip', { ns: 'appDebug' })} - - {t('feature.conversationHistory.learnMore', { ns: 'appDebug' })} -
)} diff --git a/web/app/components/app/configuration/dataset-config/settings-modal/retrieval-section.spec.tsx b/web/app/components/app/configuration/dataset-config/settings-modal/retrieval-section.spec.tsx index 0d7b705d9e..2140afe1dd 100644 --- a/web/app/components/app/configuration/dataset-config/settings-modal/retrieval-section.spec.tsx +++ b/web/app/components/app/configuration/dataset-config/settings-modal/retrieval-section.spec.tsx @@ -1,5 +1,6 @@ import type { DataSet } from '@/models/datasets' import type { RetrievalConfig } from '@/types/app' +import type { DocPathWithoutLang } from '@/types/doc-paths' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { IndexingType } from '@/app/components/datasets/create/step-two' @@ -237,15 +238,15 @@ describe('RetrievalSection', () => { retrievalConfig={retrievalConfig} showMultiModalTip onRetrievalConfigChange={vi.fn()} - docLink={docLink} + docLink={docLink as unknown as (path?: DocPathWithoutLang) => string} />, ) // Assert expect(screen.getByText('dataset.retrieval.semantic_search.title')).toBeInTheDocument() const learnMoreLink = screen.getByRole('link', { name: 'datasetSettings.form.retrievalSetting.learnMore' }) - expect(learnMoreLink).toHaveAttribute('href', 'https://docs.example/guides/knowledge-base/create-knowledge-and-upload-documents/setting-indexing-methods#setting-the-retrieval-setting') - expect(docLink).toHaveBeenCalledWith('/guides/knowledge-base/create-knowledge-and-upload-documents/setting-indexing-methods#setting-the-retrieval-setting') + expect(learnMoreLink).toHaveAttribute('href', 'https://docs.example/use-dify/knowledge/create-knowledge/setting-indexing-methods') + expect(docLink).toHaveBeenCalledWith('/use-dify/knowledge/create-knowledge/setting-indexing-methods') }) it('propagates retrieval config changes for economical indexing', async () => { @@ -263,7 +264,7 @@ describe('RetrievalSection', () => { retrievalConfig={createRetrievalConfig()} showMultiModalTip={false} onRetrievalConfigChange={handleRetrievalChange} - docLink={path => path} + docLink={path => path || ''} />, ) const [topKIncrement] = screen.getAllByLabelText('increment') diff --git a/web/app/components/app/configuration/dataset-config/settings-modal/retrieval-section.tsx b/web/app/components/app/configuration/dataset-config/settings-modal/retrieval-section.tsx index 6c9bd14d1e..6d478de908 100644 --- a/web/app/components/app/configuration/dataset-config/settings-modal/retrieval-section.tsx +++ b/web/app/components/app/configuration/dataset-config/settings-modal/retrieval-section.tsx @@ -1,6 +1,7 @@ import type { FC } from 'react' import type { DataSet } from '@/models/datasets' import type { RetrievalConfig } from '@/types/app' +import type { DocPathWithoutLang } from '@/types/doc-paths' import { RiCloseLine } from '@remixicon/react' import Divider from '@/app/components/base/divider' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' @@ -84,7 +85,7 @@ type InternalRetrievalSectionProps = CommonSectionProps & { retrievalConfig: RetrievalConfig showMultiModalTip: boolean onRetrievalConfigChange: (value: RetrievalConfig) => void - docLink: (path: string) => string + docLink: (path?: DocPathWithoutLang) => string } const InternalRetrievalSection: FC = ({ @@ -102,7 +103,7 @@ const InternalRetrievalSection: FC = ({
{t('form.retrievalSetting.title', { ns: 'datasetSettings' })}
diff --git a/web/app/components/app/configuration/tools/external-data-tool-modal.tsx b/web/app/components/app/configuration/tools/external-data-tool-modal.tsx index 71827c4e0d..fece5598e1 100644 --- a/web/app/components/app/configuration/tools/external-data-tool-modal.tsx +++ b/web/app/components/app/configuration/tools/external-data-tool-modal.tsx @@ -240,7 +240,7 @@ const ExternalDataToolModal: FC = ({ ) diff --git a/web/app/components/app/overview/app-card.tsx b/web/app/components/app/overview/app-card.tsx index c1a662df5d..9975c81b3e 100644 --- a/web/app/components/app/overview/app-card.tsx +++ b/web/app/components/app/overview/app-card.tsx @@ -245,7 +245,7 @@ function AppCard({
window.open(docLink('/guides/workflow/node/user-input'), '_blank')} + onClick={() => window.open(docLink('/use-dify/nodes/user-input'), '_blank')} > {t('overview.appInfo.enableTooltip.learnMore', { ns: 'appOverview' })}
diff --git a/web/app/components/app/overview/customize/index.tsx b/web/app/components/app/overview/customize/index.tsx index 77dae81a01..c7391abe3d 100644 --- a/web/app/components/app/overview/customize/index.tsx +++ b/web/app/components/app/overview/customize/index.tsx @@ -118,7 +118,7 @@ const CustomizeModal: FC = ({ className="mt-2" onClick={() => window.open( - docLink('/guides/application-publishing/developing-with-apis'), + docLink('/use-dify/publish/developing-with-apis'), '_blank', )} > diff --git a/web/app/components/app/overview/settings/index.tsx b/web/app/components/app/overview/settings/index.tsx index 428a475da9..0d087e27c2 100644 --- a/web/app/components/app/overview/settings/index.tsx +++ b/web/app/components/app/overview/settings/index.tsx @@ -23,7 +23,6 @@ import Textarea from '@/app/components/base/textarea' import { useToastContext } from '@/app/components/base/toast' import Tooltip from '@/app/components/base/tooltip' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' -import { useDocLink } from '@/context/i18n' import { useModalContext } from '@/context/modal-context' import { useProviderContext } from '@/context/provider-context' import { languages } from '@/i18n-config/language' @@ -100,7 +99,6 @@ const SettingsModal: FC = ({ const [language, setLanguage] = useState(default_language) const [saveLoading, setSaveLoading] = useState(false) const { t } = useTranslation() - const docLink = useDocLink() const [showAppIconPicker, setShowAppIconPicker] = useState(false) const [appIcon, setAppIcon] = useState( @@ -240,16 +238,6 @@ const SettingsModal: FC = ({
{t(`${prefixSettings}.modalTip`, { ns: 'appOverview' })} - - {t('operation.learnMore', { ns: 'common' })} -
{/* form body */} diff --git a/web/app/components/app/overview/trigger-card.tsx b/web/app/components/app/overview/trigger-card.tsx index a2d28606a1..12a294b4ec 100644 --- a/web/app/components/app/overview/trigger-card.tsx +++ b/web/app/components/app/overview/trigger-card.tsx @@ -208,7 +208,7 @@ function TriggerCard({ appInfo, onToggleResult }: ITriggerCardProps) { {t('overview.triggerInfo.triggerStatusDescription', { ns: 'appOverview' })} {' '} { const { t } = useTranslation() - const docLink = useDocLink() const { data: speech2textDefaultModel } = useDefaultModel(ModelTypeEnum.speech2text) const { data: text2speechDefaultModel } = useDefaultModel(ModelTypeEnum.tts) @@ -76,14 +73,6 @@ const NewFeaturePanel = ({
diff --git a/web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx b/web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx index 59b62d0bfd..c9455c98eb 100644 --- a/web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx +++ b/web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx @@ -319,7 +319,7 @@ const ModerationSettingModal: FC = ({
{t('apiBasedExtension.selector.title', { ns: 'common' })}
{ // Assert const link = screen.getByText('datasetPipeline.addDocuments.stepThree.learnMore') - expect(link).toHaveAttribute('href', 'https://docs.dify.ai/en-US/guides/knowledge-base/integrate-knowledge-within-application') + expect(link).toHaveAttribute('href', 'https://docs.dify.ai/en-US/use-dify/knowledge/integrate-knowledge-within-application') expect(link).toHaveAttribute('target', '_blank') expect(link).toHaveAttribute('rel', 'noreferrer noopener') }) diff --git a/web/app/components/datasets/create/step-three/index.tsx b/web/app/components/datasets/create/step-three/index.tsx index ad26711311..5ab21f6302 100644 --- a/web/app/components/datasets/create/step-three/index.tsx +++ b/web/app/components/datasets/create/step-three/index.tsx @@ -87,7 +87,7 @@ const StepThree = ({ datasetId, datasetName, indexingType, creationCache, retrie
{t('stepThree.sideTipTitle', { ns: 'datasetCreation' })}
{t('stepThree.sideTipContent', { ns: 'datasetCreation' })}
= ({ {t('form.retrievalSetting.learnMore', { ns: 'datasetSettings' })} diff --git a/web/app/components/datasets/documents/components/documents-header.tsx b/web/app/components/datasets/documents/components/documents-header.tsx index ed97742fdd..490893d43f 100644 --- a/web/app/components/datasets/documents/components/documents-header.tsx +++ b/web/app/components/datasets/documents/components/documents-header.tsx @@ -121,7 +121,7 @@ const DocumentsHeader: FC = ({ className="flex items-center text-text-accent" target="_blank" rel="noopener noreferrer" - href={docLink('/guides/knowledge-base/integrate-knowledge-within-application')} + href={docLink('/use-dify/knowledge/integrate-knowledge-within-application')} > {t('list.learnMore', { ns: 'datasetDocuments' })} diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx index 9b0df231bd..4bdaac895b 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx @@ -138,7 +138,7 @@ const OnlineDocuments = ({
{ render() // Assert - expect(mockDocLink).toHaveBeenCalledWith('/guides/knowledge-base/knowledge-pipeline/authorize-data-source') + expect(mockDocLink).toHaveBeenCalledWith('/use-dify/knowledge/knowledge-pipeline/authorize-data-source') }) }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx index 508745aaeb..4346a2d0af 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx @@ -196,7 +196,7 @@ const OnlineDrive = ({
{ // Assert const link = screen.getByRole('link', { name: 'datasetPipeline.addDocuments.stepThree.learnMore' }) - expect(link).toHaveAttribute('href', 'https://docs.dify.ai/en-US/guides/knowledge-base/integrate-knowledge-within-application') + expect(link).toHaveAttribute('href', 'https://docs.dify.ai/en-US/use-dify/knowledge/knowledge-pipeline/authorize-data-source') expect(link).toHaveAttribute('target', '_blank') expect(link).toHaveAttribute('rel', 'noreferrer noopener') }) diff --git a/web/app/components/datasets/documents/create-from-pipeline/processing/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/processing/index.tsx index 97c8937442..283600fa69 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/processing/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/processing/index.tsx @@ -44,7 +44,7 @@ const Processing = ({
{t('stepThree.sideTipTitle', { ns: 'datasetCreation' })}
{t('stepThree.sideTipContent', { ns: 'datasetCreation' })}
= React.memo(({ {variable === 'endpoint' && ( { render() const docLink = screen.getByText('dataset.externalAPIPanelDocumentation') expect(docLink).toBeInTheDocument() - expect(docLink.closest('a')).toHaveAttribute('href', 'https://docs.example.com/guides/knowledge-base/connect-external-knowledge-base') + expect(docLink.closest('a')).toHaveAttribute('href', 'https://docs.example.com/use-dify/knowledge/connect-external-knowledge-base') }) it('should render create button', () => { diff --git a/web/app/components/datasets/external-api/external-api-panel/index.tsx b/web/app/components/datasets/external-api/external-api-panel/index.tsx index a137348626..c37ff20ba7 100644 --- a/web/app/components/datasets/external-api/external-api-panel/index.tsx +++ b/web/app/components/datasets/external-api/external-api-panel/index.tsx @@ -54,7 +54,7 @@ const ExternalAPIPanel: React.FC = ({ onClose }) => {
{t('externalAPIPanelDescription', { ns: 'dataset' })}
diff --git a/web/app/components/datasets/external-knowledge-base/create/InfoPanel.tsx b/web/app/components/datasets/external-knowledge-base/create/InfoPanel.tsx index beb6a3cf71..61b37a0a1d 100644 --- a/web/app/components/datasets/external-knowledge-base/create/InfoPanel.tsx +++ b/web/app/components/datasets/external-knowledge-base/create/InfoPanel.tsx @@ -18,14 +18,14 @@ const InfoPanel = () => { {t('connectDatasetIntro.content.front', { ns: 'dataset' })} - + {t('connectDatasetIntro.content.link', { ns: 'dataset' })} {t('connectDatasetIntro.content.end', { ns: 'dataset' })} diff --git a/web/app/components/datasets/external-knowledge-base/create/index.spec.tsx b/web/app/components/datasets/external-knowledge-base/create/index.spec.tsx index 2fce096cd5..d56833fd36 100644 --- a/web/app/components/datasets/external-knowledge-base/create/index.spec.tsx +++ b/web/app/components/datasets/external-knowledge-base/create/index.spec.tsx @@ -146,7 +146,7 @@ describe('ExternalKnowledgeBaseCreate', () => { renderComponent() const docLink = screen.getByText('dataset.connectHelper.helper4') - expect(docLink).toHaveAttribute('href', 'https://docs.dify.ai/en/guides/knowledge-base/connect-external-knowledge-base') + expect(docLink).toHaveAttribute('href', 'https://docs.dify.ai/en/use-dify/knowledge/connect-external-knowledge-base') expect(docLink).toHaveAttribute('target', '_blank') expect(docLink).toHaveAttribute('rel', 'noopener noreferrer') }) diff --git a/web/app/components/datasets/external-knowledge-base/create/index.tsx b/web/app/components/datasets/external-knowledge-base/create/index.tsx index 1d17b23b43..07b6e71fa6 100644 --- a/web/app/components/datasets/external-knowledge-base/create/index.tsx +++ b/web/app/components/datasets/external-knowledge-base/create/index.tsx @@ -61,7 +61,7 @@ const ExternalKnowledgeBaseCreate: React.FC = {t('connectHelper.helper1', { ns: 'dataset' })} {t('connectHelper.helper2', { ns: 'dataset' })} {t('connectHelper.helper3', { ns: 'dataset' })} - + {t('connectHelper.helper4', { ns: 'dataset' })} diff --git a/web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx b/web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx index d21297fc93..a942c402ed 100644 --- a/web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx +++ b/web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx @@ -96,10 +96,7 @@ const ModifyRetrievalModal: FC = ({ {t('form.retrievalSetting.learnMore', { ns: 'datasetSettings' })} diff --git a/web/app/components/datasets/no-linked-apps-panel.tsx b/web/app/components/datasets/no-linked-apps-panel.tsx index 1b0357bc6a..12e87a7379 100644 --- a/web/app/components/datasets/no-linked-apps-panel.tsx +++ b/web/app/components/datasets/no-linked-apps-panel.tsx @@ -15,7 +15,7 @@ const NoLinkedAppsPanel = () => {
{t('datasetMenus.emptyTip', { ns: 'common' })}
diff --git a/web/app/components/datasets/settings/form/index.tsx b/web/app/components/datasets/settings/form/index.tsx index 5fbaefade7..a25d770518 100644 --- a/web/app/components/datasets/settings/form/index.tsx +++ b/web/app/components/datasets/settings/form/index.tsx @@ -281,7 +281,7 @@ const Form = () => { {t('form.chunkStructure.learnMore', { ns: 'datasetSettings' })} @@ -421,10 +421,7 @@ const Form = () => { {t('form.retrievalSetting.learnMore', { ns: 'datasetSettings' })} diff --git a/web/app/components/header/account-dropdown/index.tsx b/web/app/components/header/account-dropdown/index.tsx index e16c00acd0..07dd0fca3d 100644 --- a/web/app/components/header/account-dropdown/index.tsx +++ b/web/app/components/header/account-dropdown/index.tsx @@ -137,7 +137,7 @@ export default function AppSelector() { diff --git a/web/app/components/header/account-setting/api-based-extension-page/empty.tsx b/web/app/components/header/account-setting/api-based-extension-page/empty.tsx index 38525993fa..d75e66f8d0 100644 --- a/web/app/components/header/account-setting/api-based-extension-page/empty.tsx +++ b/web/app/components/header/account-setting/api-based-extension-page/empty.tsx @@ -17,7 +17,7 @@ const Empty = () => {
{t('apiBasedExtension.title', { ns: 'common' })}
diff --git a/web/app/components/header/account-setting/api-based-extension-page/modal.tsx b/web/app/components/header/account-setting/api-based-extension-page/modal.tsx index d3146d7baa..f35986dbb0 100644 --- a/web/app/components/header/account-setting/api-based-extension-page/modal.tsx +++ b/web/app/components/header/account-setting/api-based-extension-page/modal.tsx @@ -102,7 +102,7 @@ const ApiBasedExtensionModal: FC = ({
{t('detailPanel.endpointsTip', { ns: 'plugin' })}
diff --git a/web/app/components/plugins/plugin-page/debug-info.tsx b/web/app/components/plugins/plugin-page/debug-info.tsx index f62f8a4134..f3eed424f4 100644 --- a/web/app/components/plugins/plugin-page/debug-info.tsx +++ b/web/app/components/plugins/plugin-page/debug-info.tsx @@ -8,8 +8,7 @@ import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Tooltip from '@/app/components/base/tooltip' -import { getDocsUrl } from '@/app/components/plugins/utils' -import { useLocale } from '@/context/i18n' +import { useDocLink } from '@/context/i18n' import { useDebugKey } from '@/service/use-plugins' import KeyValueItem from '../base/key-value-item' @@ -17,7 +16,7 @@ const i18nPrefix = 'debugInfo' const DebugInfo: FC = () => { const { t } = useTranslation() - const locale = useLocale() + const docLink = useDocLink() const { data: info, isLoading } = useDebugKey() // info.key likes 4580bdb7-b878-471c-a8a4-bfd760263a53 mask the middle part using *. @@ -34,7 +33,7 @@ const DebugInfo: FC = () => { <>
{t(`${i18nPrefix}.title`, { ns: 'plugin' })} - + {t(`${i18nPrefix}.viewDocs`, { ns: 'plugin' })} diff --git a/web/app/components/plugins/plugin-page/index.tsx b/web/app/components/plugins/plugin-page/index.tsx index d852e4d0b8..efb665197a 100644 --- a/web/app/components/plugins/plugin-page/index.tsx +++ b/web/app/components/plugins/plugin-page/index.tsx @@ -15,10 +15,9 @@ import Button from '@/app/components/base/button' import TabSlider from '@/app/components/base/tab-slider' import Tooltip from '@/app/components/base/tooltip' import ReferenceSettingModal from '@/app/components/plugins/reference-setting-modal' -import { getDocsUrl } from '@/app/components/plugins/utils' import { MARKETPLACE_API_PREFIX, SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config' import { useGlobalPublicStore } from '@/context/global-public-context' -import { useLocale } from '@/context/i18n' +import { useDocLink } from '@/context/i18n' import useDocumentTitle from '@/hooks/use-document-title' import { usePluginInstallation } from '@/hooks/use-query-params' import { fetchBundleInfoFromMarketPlace, fetchManifestFromMarketPlace } from '@/service/plugins' @@ -47,7 +46,7 @@ const PluginPage = ({ marketplace, }: PluginPageProps) => { const { t } = useTranslation() - const locale = useLocale() + const docLink = useDocLink() useDocumentTitle(t('metadata.title', { ns: 'plugin' })) // Use nuqs hook for installation state @@ -175,7 +174,7 @@ const PluginPage = ({
window.open(docLink('/guides/workflow/node/user-input'), '_blank')} + onClick={() => window.open(docLink('/use-dify/nodes/user-input'), '_blank')} > {t('overview.appInfo.enableTooltip.learnMore', { ns: 'appOverview' })}
diff --git a/web/app/components/tools/provider/custom-create-card.tsx b/web/app/components/tools/provider/custom-create-card.tsx index 637d17c3c3..bf86a1f833 100644 --- a/web/app/components/tools/provider/custom-create-card.tsx +++ b/web/app/components/tools/provider/custom-create-card.tsx @@ -2,16 +2,12 @@ import type { CustomCollectionBackend } from '../types' import { RiAddCircleFill, - RiArrowRightUpLine, - RiBookOpenLine, } from '@remixicon/react' -import { useMemo, useState } from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal' import { useAppContext } from '@/context/app-context' -import { useDocLink, useLocale } from '@/context/i18n' -import { getLanguage } from '@/i18n-config/language' import { createCustomCollection } from '@/service/tools' type Props = { @@ -20,17 +16,8 @@ type Props = { const Contribute = ({ onRefreshData }: Props) => { const { t } = useTranslation() - const locale = useLocale() - const language = getLanguage(locale) const { isCurrentWorkspaceManager } = useAppContext() - const docLink = useDocLink() - const linkUrl = useMemo(() => { - return docLink('/guides/tools#how-to-create-custom-tools', { - 'zh-Hans': '/guides/tools#ru-he-chuang-jian-zi-ding-yi-gong-ju', - }) - }, [language]) - const [isShowEditCollectionToolModal, setIsShowEditCustomCollectionModal] = useState(false) const doCreateCustomToolCollection = async (data: CustomCollectionBackend) => { await createCustomCollection(data) @@ -54,13 +41,6 @@ const Contribute = ({ onRefreshData }: Props) => {
{t('createCustomTool', { ns: 'tools' })}
-
)} {isShowEditCollectionToolModal && ( diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/index.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/index.tsx index 0f92982cf2..0faf43bfd1 100644 --- a/web/app/components/workflow-app/components/workflow-onboarding-modal/index.tsx +++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/index.tsx @@ -8,7 +8,6 @@ import { import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' import { BlockEnum } from '@/app/components/workflow/types' -import { useDocLink } from '@/context/i18n' import StartNodeSelectionPanel from './start-node-selection-panel' type WorkflowOnboardingModalProps = { @@ -23,7 +22,6 @@ const WorkflowOnboardingModal: FC = ({ onSelectStartNode, }) => { const { t } = useTranslation() - const docLink = useDocLink() const handleSelectUserInput = useCallback(() => { onSelectStartNode(BlockEnum.Start) @@ -63,15 +61,6 @@ const WorkflowOnboardingModal: FC = ({
{t('onboarding.description', { ns: 'workflow' })} {' '} - - {t('onboarding.learnMore', { ns: 'workflow' })} - - {' '} {t('onboarding.aboutStartNode', { ns: 'workflow' })}
diff --git a/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts b/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts index 60f0bf3b28..0c5c1e4a40 100644 --- a/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts +++ b/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts @@ -1,4 +1,5 @@ import type { AvailableNodesMetaData } from '@/app/components/workflow/hooks-store/store' +import type { DocPathWithoutLang } from '@/types/doc-paths' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { WORKFLOW_COMMON_NODES } from '@/app/components/workflow/constants/node' @@ -44,7 +45,7 @@ export const useAvailableNodesMetaData = () => { const { metaData } = node const title = t(`blocks.${metaData.type}`, { ns: 'workflow' }) const description = t(`blocksAbout.${metaData.type}`, { ns: 'workflow' }) - const helpLinkPath = `guides/workflow/node/${metaData.helpLinkUri}` + const helpLinkPath = `/use-dify/nodes/${metaData.helpLinkUri}` as DocPathWithoutLang return { ...node, metaData: { diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx index 8303681d90..42be3d46e4 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -251,10 +251,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => { {' '}
diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/default-value.tsx b/web/app/components/workflow/nodes/_base/components/error-handle/default-value.tsx index 538dce09d0..080fa0f107 100644 --- a/web/app/components/workflow/nodes/_base/components/error-handle/default-value.tsx +++ b/web/app/components/workflow/nodes/_base/components/error-handle/default-value.tsx @@ -5,7 +5,6 @@ import Input from '@/app/components/base/input' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import { VarType } from '@/app/components/workflow/types' -import { useDocLink } from '@/context/i18n' type DefaultValueProps = { forms: DefaultValueForm[] @@ -16,7 +15,6 @@ const DefaultValue = ({ onFormChange, }: DefaultValueProps) => { const { t } = useTranslation() - const docLink = useDocLink() const getFormChangeHandler = useCallback(({ key, type }: DefaultValueForm) => { return (payload: any) => { let value @@ -35,15 +33,6 @@ const DefaultValue = ({
{t('nodes.common.errorHandle.defaultValue.desc', { ns: 'workflow' })}   - - {t('common.learnMore', { ns: 'workflow' })} -
{ diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/fail-branch-card.tsx b/web/app/components/workflow/nodes/_base/components/error-handle/fail-branch-card.tsx index fe267f52c4..49cd44160c 100644 --- a/web/app/components/workflow/nodes/_base/components/error-handle/fail-branch-card.tsx +++ b/web/app/components/workflow/nodes/_base/components/error-handle/fail-branch-card.tsx @@ -19,7 +19,7 @@ const FailBranchCard = () => { {t('nodes.common.errorHandle.failBranch.customizeTip', { ns: 'workflow' })}   diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx index 6184bcad9f..26f10b7a1d 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx @@ -6,7 +6,6 @@ import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import ListEmpty from '@/app/components/base/list-empty' import { useStore } from '@/app/components/workflow/store' -import { useDocLink } from '@/context/i18n' import VarReferenceVars from './var-reference-vars' type Props = { @@ -31,7 +30,7 @@ const VarReferencePopup: FC = ({ const pipelineId = useStore(s => s.pipelineId) const showManageRagInputFields = useMemo(() => !!pipelineId, [pipelineId]) const setShowInputFieldPanel = useStore(s => s.setShowInputFieldPanel) - const docLink = useDocLink() + // max-h-[300px] overflow-y-auto todo: use portal to handle long list return (
= ({ description={( )} /> diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/index.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/index.tsx index 77981639cd..73e87ec12b 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/index.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/index.tsx @@ -31,7 +31,7 @@ const Instruction = ({

{t('nodes.knowledgeBase.chunkStructureTip.message', { ns: 'workflow' })}

{ const { t } = useTranslation() + const docLink = useDocLink() const { options, hybridSearchModeOptions, @@ -61,7 +63,7 @@ const RetrievalSetting = ({ title: t('form.retrievalSetting.title', { ns: 'datasetSettings' }), subTitle: ( diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx index e7ac493bd2..b4dac4b58e 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx @@ -1,14 +1,12 @@ import type { FC } from 'react' import type { SchemaRoot } from '../../types' -import { RiBracesLine, RiCloseLine, RiExternalLinkLine, RiTimelineView } from '@remixicon/react' -import * as React from 'react' +import { RiBracesLine, RiCloseLine, RiTimelineView } from '@remixicon/react' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' import Toast from '@/app/components/base/toast' import { JSON_SCHEMA_MAX_DEPTH } from '@/config' -import { useDocLink } from '@/context/i18n' import { SegmentedControl } from '../../../../../base/segmented-control' import { Type } from '../../types' import { @@ -55,7 +53,6 @@ const JsonSchemaConfig: FC = ({ onClose, }) => { const { t } = useTranslation() - const docLink = useDocLink() const [currentTab, setCurrentTab] = useState(SchemaView.VisualEditor) const [jsonSchema, setJsonSchema] = useState(defaultSchema || DEFAULT_SCHEMA) const [json, setJson] = useState(() => JSON.stringify(jsonSchema, null, 2)) @@ -253,15 +250,6 @@ const JsonSchemaConfig: FC = ({
{/* Footer */}
- - {t('nodes.llm.jsonSchema.doc', { ns: 'workflow' })} - -
, + MarkdownForm: ({ children }: PropsWithChildren) =>
{children}
, + Paragraph: ({ children }: PropsWithChildren) =>

{children}

, + PluginImg: ({ alt }: { alt?: string }) => {alt}, + PluginParagraph: ({ children }: PropsWithChildren) =>

{children}

, + ScriptBlock: () => null, + ThinkBlock: ({ children }: PropsWithChildren) =>
{children}
, + VideoBlock: ({ children }: PropsWithChildren) =>
{children}
, +})) + +vi.mock('@/app/components/base/markdown-blocks/code-block', () => ({ + default: ({ children }: PropsWithChildren) => {children}, +})) + +describe('ReactMarkdownWrapper', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + describe('Strikethrough rendering', () => { + it('should NOT render single tilde as strikethrough', () => { + // Arrange - single tilde should be rendered as literal text + const content = 'Range: 0.3~8mm' + + // Act + render() + + // Assert - check that ~ is rendered as text, not as strikethrough (del element) + // The content should contain the tilde as literal text + expect(screen.getByText(/0\.3~8mm/)).toBeInTheDocument() + expect(document.querySelector('del')).toBeNull() + }) + + it('should render double tildes as strikethrough', () => { + // Arrange - double tildes should create strikethrough + const content = 'This is ~~strikethrough~~ text' + + // Act + render() + + // Assert - del element should be present for double tildes + const delElement = document.querySelector('del') + expect(delElement).not.toBeNull() + expect(delElement?.textContent).toBe('strikethrough') + }) + + it('should handle mixed content with single and double tildes correctly', () => { + // Arrange - real-world example from issue #31391 + const content = 'PCB thickness: 0.3~8mm and ~~removed feature~~ text' + + // Act + render() + + // Assert + // Only double tildes should create strikethrough + const delElements = document.querySelectorAll('del') + expect(delElements).toHaveLength(1) + expect(delElements[0].textContent).toBe('removed feature') + + // Single tilde should remain as literal text + expect(screen.getByText(/0\.3~8mm/)).toBeInTheDocument() + }) + }) + + describe('Basic rendering', () => { + it('should render plain text content', () => { + // Arrange + const content = 'Hello World' + + // Act + render() + + // Assert + expect(screen.getByText('Hello World')).toBeInTheDocument() + }) + + it('should render bold text', () => { + // Arrange + const content = '**bold text**' + + // Act + render() + + // Assert + expect(screen.getByText('bold text')).toBeInTheDocument() + expect(document.querySelector('strong')).not.toBeNull() + }) + + it('should render italic text', () => { + // Arrange + const content = '*italic text*' + + // Act + render() + + // Assert + expect(screen.getByText('italic text')).toBeInTheDocument() + expect(document.querySelector('em')).not.toBeNull() + }) + }) +}) diff --git a/web/app/components/base/markdown/react-markdown-wrapper.tsx b/web/app/components/base/markdown/react-markdown-wrapper.tsx index ef735b5e76..ed9e93e8b3 100644 --- a/web/app/components/base/markdown/react-markdown-wrapper.tsx +++ b/web/app/components/base/markdown/react-markdown-wrapper.tsx @@ -30,7 +30,7 @@ export const ReactMarkdownWrapper: FC = (props) => { return (