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 1457d638c4e..fc4dee32896 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 d2e52812821..20e5400e568 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 ffc2d0bd312..8a760217593 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 b5eabfeeccd..6f62b53e2d4 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 a67f8216a3a..24bc5d2d589 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 31d9150135f..f44605984d3 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 7e5c4ed1de8..04150901fc5 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 0cd2e281065..c7790411af4 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 0c67b010d89..34db09bb320 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 37c65556408..5ab7130ad03 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 f912cf1960e..920836d4674 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 fdf2547374d..76262326469 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 318435d63de..602f5c7407c 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 913778e91df..62381ab9ba7 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 8e447d0c172..6a4a0b0eb17 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 99fcb93a4ed..41bb4299051 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 722953747e9..c5a526418ae 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 c9bbbf80433..6409a7aedbf 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 ec1cbf501fe..b0d6eadafdb 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 036f3e98c30..b7800457681 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 7fe974c96ed..223eccb699a 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 ca792318fa2..e111c940824 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 1b343d814a1..20277ca50cb 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 13780eec6c9..9c731a081ac 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 81b4f2474e3..f07b2932c90 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 b0b042b2a5b..44c4fc8f462 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 0d7b705d9e3..2140afe1dd2 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 6c9bd14d1e4..6d478de9087 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 71827c4e0de..fece5598e1d 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 c1a662df5d1..9975c81b3ef 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 77dae81a01b..c7391abe3db 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 428a475da98..0d087e27c29 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 a2d28606a17..12a294b4eca 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 59b62d0bfdb..c9455c98eb9 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 ad26711311b..5ab21f6302a 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 ed97742fdd9..490893d43f0 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 9b0df231bde..4bdaac895b3 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 508745aaeb8..4346a2d0af6 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 97c8937442a..283600fa698 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 a1373486263..c37ff20ba7c 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 beb6a3cf71b..61b37a0a1d1 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 2fce096cd5f..d56833fd369 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 1d17b23b435..07b6e71fa6b 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 d21297fc932..a942c402ed6 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 1b0357bc6ae..12e87a73799 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 5fbaefade79..a25d7705182 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 e16c00acd0d..07dd0fca3dd 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 38525993fa2..d75e66f8d0e 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 d3146d7baad..f35986dbb0e 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 f62f8a4134d..f3eed424f42 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 d852e4d0b88..efb665197ab 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 637d17c3c38..bf86a1f833e 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 0f92982cf2c..0faf43bfd10 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 60f0bf3b280..0c5c1e4a407 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 8303681d90c..42be3d46e43 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 538dce09d0b..080fa0f1079 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 fe267f52c4a..49cd44160c0 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 6184bcad9fa..26f10b7a1d5 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 77981639cda..73e87ec12ba 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 e7ac493bd2d..b4dac4b58ee 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 ef735b5e76c..ed9e93e8b33 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 (