@@ -97,7 +99,7 @@
-أسهل طريقة لبدء تشغيل خادم Dify هي تشغيل ملف [docker-compose.yml](docker/docker-compose.yaml) الخاص بنا. قبل تشغيل أمر التثبيت، تأكد من تثبيت [Docker](https://docs.docker.com/get-docker/) و [Docker Compose](https://docs.docker.com/compose/install/) على جهازك:
+أسهل طريقة لبدء تشغيل خادم Dify هي تشغيل ملف [docker-compose.yml](../../docker/docker-compose.yaml) الخاص بنا. قبل تشغيل أمر التثبيت، تأكد من تثبيت [Docker](https://docs.docker.com/get-docker/) و [Docker Compose](https://docs.docker.com/compose/install/) على جهازك:
```bash
cd docker
@@ -111,7 +113,7 @@ docker compose up -d
## الخطوات التالية
-إذا كنت بحاجة إلى تخصيص الإعدادات، فيرجى الرجوع إلى التعليقات في ملف [.env.example](docker/.env.example) وتحديث القيم المقابلة في ملف `.env`. بالإضافة إلى ذلك، قد تحتاج إلى إجراء تعديلات على ملف `docker-compose.yaml` نفسه، مثل تغيير إصدارات الصور أو تعيينات المنافذ أو نقاط تحميل وحدات التخزين، بناءً على بيئة النشر ومتطلباتك الخاصة. بعد إجراء أي تغييرات، يرجى إعادة تشغيل `docker-compose up -d`. يمكنك العثور على قائمة كاملة بمتغيرات البيئة المتاحة [هنا](https://docs.dify.ai/getting-started/install-self-hosted/environments).
+إذا كنت بحاجة إلى تخصيص الإعدادات، فيرجى الرجوع إلى التعليقات في ملف [.env.example](../../docker/.env.example) وتحديث القيم المقابلة في ملف `.env`. بالإضافة إلى ذلك، قد تحتاج إلى إجراء تعديلات على ملف `docker-compose.yaml` نفسه، مثل تغيير إصدارات الصور أو تعيينات المنافذ أو نقاط تحميل وحدات التخزين، بناءً على بيئة النشر ومتطلباتك الخاصة. بعد إجراء أي تغييرات، يرجى إعادة تشغيل `docker-compose up -d`. يمكنك العثور على قائمة كاملة بمتغيرات البيئة المتاحة [هنا](https://docs.dify.ai/getting-started/install-self-hosted/environments).
يوجد مجتمع خاص بـ [Helm Charts](https://helm.sh/) وملفات YAML التي تسمح بتنفيذ Dify على Kubernetes للنظام من الإيجابيات العلوية.
@@ -185,12 +187,4 @@ docker compose up -d
## الرخصة
-هذا المستودع متاح تحت [رخصة البرنامج الحر Dify](../LICENSE)، والتي تعتبر بشكل أساسي Apache 2.0 مع بعض القيود الإضافية.
-
-## الكشف عن الأمان
-
-لحماية خصوصيتك، يرجى تجنب نشر مشكلات الأمان على GitHub. بدلاً من ذلك، أرسل أسئلتك إلى
وسنقدم لك إجابة أكثر تفصيلاً.
-
-## الرخصة
-
-هذا المستودع متاح تحت [رخصة البرنامج الحر Dify](../LICENSE)، والتي تعتبر بشكل أساسي Apache 2.0 مع بعض القيود الإضافية.
+هذا المستودع متاح تحت [رخصة البرنامج الحر Dify](../../LICENSE)، والتي تعتبر بشكل أساسي Apache 2.0 مع بعض القيود الإضافية.
diff --git a/README/README_BN.md b/docs/bn-BD/README.md
similarity index 85%
rename from README/README_BN.md
rename to docs/bn-BD/README.md
index b0a64a6cfe..318853a8de 100644
--- a/README/README_BN.md
+++ b/docs/bn-BD/README.md
@@ -1,4 +1,4 @@
-
+
📌 ডিফাই ওয়ার্কফ্লো ফাইল আপলোড পরিচিতি: গুগল নোটবুক-এলএম পডকাস্ট পুনর্নির্মাণ
@@ -39,18 +39,19 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
ডিফাই একটি ওপেন-সোর্স LLM অ্যাপ ডেভেলপমেন্ট প্ল্যাটফর্ম। এটি ইন্টুইটিভ ইন্টারফেস, এজেন্টিক AI ওয়ার্কফ্লো, RAG পাইপলাইন, এজেন্ট ক্যাপাবিলিটি, মডেল ম্যানেজমেন্ট, মনিটরিং সুবিধা এবং আরও অনেক কিছু একত্রিত করে, যা দ্রুত প্রোটোটাইপ থেকে প্রোডাকশন পর্যন্ত নিয়ে যেতে সহায়তা করে।
@@ -64,7 +65,7 @@
-ডিফাই সার্ভার চালু করার সবচেয়ে সহজ উপায় [docker compose](docker/docker-compose.yaml) মাধ্যমে। নিম্নলিখিত কমান্ডগুলো ব্যবহার করে ডিফাই চালানোর আগে, নিশ্চিত করুন যে আপনার মেশিনে [Docker](https://docs.docker.com/get-docker/) এবং [Docker Compose](https://docs.docker.com/compose/install/) ইনস্টল করা আছে :
+ডিফাই সার্ভার চালু করার সবচেয়ে সহজ উপায় [docker compose](../../docker/docker-compose.yaml) মাধ্যমে। নিম্নলিখিত কমান্ডগুলো ব্যবহার করে ডিফাই চালানোর আগে, নিশ্চিত করুন যে আপনার মেশিনে [Docker](https://docs.docker.com/get-docker/) এবং [Docker Compose](https://docs.docker.com/compose/install/) ইনস্টল করা আছে :
```bash
cd dify
@@ -128,7 +129,7 @@ GitHub-এ ডিফাইকে স্টার দিয়ে রাখুন
## Advanced Setup
-যদি আপনার কনফিগারেশনটি কাস্টমাইজ করার প্রয়োজন হয়, তাহলে অনুগ্রহ করে আমাদের [.env.example](docker/.env.example) ফাইল দেখুন এবং আপনার `.env` ফাইলে সংশ্লিষ্ট মানগুলি আপডেট করুন। এছাড়াও, আপনার নির্দিষ্ট এনভায়রনমেন্ট এবং প্রয়োজনীয়তার উপর ভিত্তি করে আপনাকে `docker-compose.yaml` ফাইলে সমন্বয় করতে হতে পারে, যেমন ইমেজ ভার্সন পরিবর্তন করা, পোর্ট ম্যাপিং করা, অথবা ভলিউম মাউন্ট করা।
+যদি আপনার কনফিগারেশনটি কাস্টমাইজ করার প্রয়োজন হয়, তাহলে অনুগ্রহ করে আমাদের [.env.example](../../docker/.env.example) ফাইল দেখুন এবং আপনার `.env` ফাইলে সংশ্লিষ্ট মানগুলি আপডেট করুন। এছাড়াও, আপনার নির্দিষ্ট এনভায়রনমেন্ট এবং প্রয়োজনীয়তার উপর ভিত্তি করে আপনাকে `docker-compose.yaml` ফাইলে সমন্বয় করতে হতে পারে, যেমন ইমেজ ভার্সন পরিবর্তন করা, পোর্ট ম্যাপিং করা, অথবা ভলিউম মাউন্ট করা।
যেকোনো পরিবর্তন করার পর, অনুগ্রহ করে `docker-compose up -d` পুনরায় চালান। ভেরিয়েবলের সম্পূর্ণ তালিকা [এখানে] (https://docs.dify.ai/getting-started/install-self-hosted/environments) খুঁজে পেতে পারেন।
যদি আপনি একটি হাইলি এভেইলেবল সেটআপ কনফিগার করতে চান, তাহলে কমিউনিটি [Helm Charts](https://helm.sh/) এবং YAML ফাইল রয়েছে যা Dify কে Kubernetes-এ ডিপ্লয় করার প্রক্রিয়া বর্ণনা করে।
@@ -175,7 +176,7 @@ GitHub-এ ডিফাইকে স্টার দিয়ে রাখুন
## Contributing
-যারা কোড অবদান রাখতে চান, তাদের জন্য আমাদের [অবদান নির্দেশিকা] দেখুন (https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md)।
+যারা কোড অবদান রাখতে চান, তাদের জন্য আমাদের [অবদান নির্দেশিকা](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) দেখুন।
একই সাথে, সোশ্যাল মিডিয়া এবং ইভেন্ট এবং কনফারেন্সে এটি শেয়ার করে Dify কে সমর্থন করুন।
> আমরা ম্যান্ডারিন বা ইংরেজি ছাড়া অন্য ভাষায় Dify অনুবাদ করতে সাহায্য করার জন্য অবদানকারীদের খুঁজছি। আপনি যদি সাহায্য করতে আগ্রহী হন, তাহলে আরও তথ্যের জন্য [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) দেখুন এবং আমাদের [ডিসকর্ড কমিউনিটি সার্ভার](https://discord.gg/8Tpq4AcN9c) এর `গ্লোবাল-ইউজারস` চ্যানেলে আমাদের একটি মন্তব্য করুন।
@@ -203,4 +204,4 @@ GitHub-এ ডিফাইকে স্টার দিয়ে রাখুন
## লাইসেন্স
-এই রিপোজিটরিটি [ডিফাই ওপেন সোর্স লাইসেন্স](../LICENSE) এর অধিনে , যা মূলত অ্যাপাচি ২.০, তবে কিছু অতিরিক্ত বিধিনিষেধ রয়েছে।
+এই রিপোজিটরিটি [ডিফাই ওপেন সোর্স লাইসেন্স](../../LICENSE) এর অধিনে , যা মূলত অ্যাপাচি ২.০, তবে কিছু অতিরিক্ত বিধিনিষেধ রয়েছে।
diff --git a/CONTRIBUTING/CONTRIBUTING_DE.md b/docs/de-DE/CONTRIBUTING.md
similarity index 96%
rename from CONTRIBUTING/CONTRIBUTING_DE.md
rename to docs/de-DE/CONTRIBUTING.md
index c9e52c4fd7..db12006b30 100644
--- a/CONTRIBUTING/CONTRIBUTING_DE.md
+++ b/docs/de-DE/CONTRIBUTING.md
@@ -6,7 +6,7 @@ Wir müssen wendig sein und schnell liefern, aber wir möchten auch sicherstelle
Dieser Leitfaden ist, wie Dify selbst, in ständiger Entwicklung. Wir sind dankbar für Ihr Verständnis, falls er manchmal hinter dem eigentlichen Projekt zurückbleibt, und begrüßen jedes Feedback zur Verbesserung.
-Bitte nehmen Sie sich einen Moment Zeit, um unsere [Lizenz- und Mitwirkungsvereinbarung](../LICENSE) zu lesen. Die Community hält sich außerdem an den [Verhaltenskodex](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
+Bitte nehmen Sie sich einen Moment Zeit, um unsere [Lizenz- und Mitwirkungsvereinbarung](../../LICENSE) zu lesen. Die Community hält sich außerdem an den [Verhaltenskodex](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
## Bevor Sie loslegen
diff --git a/README/README_DE.md b/docs/de-DE/README.md
similarity index 80%
rename from README/README_DE.md
rename to docs/de-DE/README.md
index d1a5837ab4..8907d914d3 100644
--- a/README/README_DE.md
+++ b/docs/de-DE/README.md
@@ -1,4 +1,4 @@
-
+
📌 Einführung in Dify Workflow File Upload: Google NotebookLM Podcast nachbilden
@@ -39,18 +39,19 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
Dify ist eine Open-Source-Plattform zur Entwicklung von LLM-Anwendungen. Ihre intuitive Benutzeroberfläche vereint agentenbasierte KI-Workflows, RAG-Pipelines, Agentenfunktionen, Modellverwaltung, Überwachungsfunktionen und mehr, sodass Sie schnell von einem Prototyp in die Produktion übergehen können.
@@ -64,7 +65,7 @@ Dify ist eine Open-Source-Plattform zur Entwicklung von LLM-Anwendungen. Ihre in
-Der einfachste Weg, den Dify-Server zu starten, ist über [docker compose](docker/docker-compose.yaml). Stellen Sie vor dem Ausführen von Dify mit den folgenden Befehlen sicher, dass [Docker](https://docs.docker.com/get-docker/) und [Docker Compose](https://docs.docker.com/compose/install/) auf Ihrem System installiert sind:
+Der einfachste Weg, den Dify-Server zu starten, ist über [docker compose](../../docker/docker-compose.yaml). Stellen Sie vor dem Ausführen von Dify mit den folgenden Befehlen sicher, dass [Docker](https://docs.docker.com/get-docker/) und [Docker Compose](https://docs.docker.com/compose/install/) auf Ihrem System installiert sind:
```bash
cd dify
@@ -127,7 +128,7 @@ Star Dify auf GitHub und lassen Sie sich sofort über neue Releases benachrichti
## Erweiterte Einstellungen
-Falls Sie die Konfiguration anpassen müssen, lesen Sie bitte die Kommentare in unserer [.env.example](docker/.env.example)-Datei und aktualisieren Sie die entsprechenden Werte in Ihrer `.env`-Datei. Zusätzlich müssen Sie eventuell Anpassungen an der `docker-compose.yaml`-Datei vornehmen, wie zum Beispiel das Ändern von Image-Versionen, Portzuordnungen oder Volumen-Mounts, je nach Ihrer spezifischen Einsatzumgebung und Ihren Anforderungen. Nachdem Sie Änderungen vorgenommen haben, starten Sie `docker-compose up -d` erneut. Eine vollständige Liste der verfügbaren Umgebungsvariablen finden Sie [hier](https://docs.dify.ai/getting-started/install-self-hosted/environments).
+Falls Sie die Konfiguration anpassen müssen, lesen Sie bitte die Kommentare in unserer [.env.example](../../docker/.env.example)-Datei und aktualisieren Sie die entsprechenden Werte in Ihrer `.env`-Datei. Zusätzlich müssen Sie eventuell Anpassungen an der `docker-compose.yaml`-Datei vornehmen, wie zum Beispiel das Ändern von Image-Versionen, Portzuordnungen oder Volumen-Mounts, je nach Ihrer spezifischen Einsatzumgebung und Ihren Anforderungen. Nachdem Sie Änderungen vorgenommen haben, starten Sie `docker-compose up -d` erneut. Eine vollständige Liste der verfügbaren Umgebungsvariablen finden Sie [hier](https://docs.dify.ai/getting-started/install-self-hosted/environments).
Falls Sie eine hochverfügbare Konfiguration einrichten möchten, gibt es von der Community bereitgestellte [Helm Charts](https://helm.sh/) und YAML-Dateien, die es ermöglichen, Dify auf Kubernetes bereitzustellen.
@@ -173,14 +174,14 @@ Stellen Sie Dify mit einem Klick in AKS bereit, indem Sie [Azure Devops Pipeline
## Contributing
-Falls Sie Code beitragen möchten, lesen Sie bitte unseren [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING/CONTRIBUTING_DE.md). Gleichzeitig bitten wir Sie, Dify zu unterstützen, indem Sie es in den sozialen Medien teilen und auf Veranstaltungen und Konferenzen präsentieren.
+Falls Sie Code beitragen möchten, lesen Sie bitte unseren [Contribution Guide](./CONTRIBUTING.md). Gleichzeitig bitten wir Sie, Dify zu unterstützen, indem Sie es in den sozialen Medien teilen und auf Veranstaltungen und Konferenzen präsentieren.
> Wir suchen Mitwirkende, die dabei helfen, Dify in weitere Sprachen zu übersetzen – außer Mandarin oder Englisch. Wenn Sie Interesse an einer Mitarbeit haben, lesen Sie bitte die [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) für weitere Informationen und hinterlassen Sie einen Kommentar im `global-users`-Kanal unseres [Discord Community Servers](https://discord.gg/8Tpq4AcN9c).
## Gemeinschaft & Kontakt
- [GitHub Discussion](https://github.com/langgenius/dify/discussions). Am besten geeignet für: den Austausch von Feedback und das Stellen von Fragen.
-- [GitHub Issues](https://github.com/langgenius/dify/issues). Am besten für: Fehler, auf die Sie bei der Verwendung von Dify.AI stoßen, und Funktionsvorschläge. Siehe unseren [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
+- [GitHub Issues](https://github.com/langgenius/dify/issues). Am besten für: Fehler, auf die Sie bei der Verwendung von Dify.AI stoßen, und Funktionsvorschläge. Siehe unseren [Contribution Guide](./CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Am besten geeignet für: den Austausch von Bewerbungen und den Austausch mit der Community.
- [X(Twitter)](https://twitter.com/dify_ai). Am besten geeignet für: den Austausch von Bewerbungen und den Austausch mit der Community.
@@ -200,4 +201,4 @@ Um Ihre Privatsphäre zu schützen, vermeiden Sie es bitte, Sicherheitsprobleme
## Lizenz
-Dieses Repository steht unter der [Dify Open Source License](../LICENSE), die im Wesentlichen Apache 2.0 mit einigen zusätzlichen Einschränkungen ist.
+Dieses Repository steht unter der [Dify Open Source License](../../LICENSE), die im Wesentlichen Apache 2.0 mit einigen zusätzlichen Einschränkungen ist.
diff --git a/CONTRIBUTING/CONTRIBUTING_ES.md b/docs/es-ES/CONTRIBUTING.md
similarity index 96%
rename from CONTRIBUTING/CONTRIBUTING_ES.md
rename to docs/es-ES/CONTRIBUTING.md
index 764c678fb2..6cd80651c4 100644
--- a/CONTRIBUTING/CONTRIBUTING_ES.md
+++ b/docs/es-ES/CONTRIBUTING.md
@@ -6,7 +6,7 @@ Necesitamos ser ágiles y enviar rápidamente dado donde estamos, pero también
Esta guía, como Dify mismo, es un trabajo en constante progreso. Agradecemos mucho tu comprensión si a veces se queda atrás del proyecto real, y damos la bienvenida a cualquier comentario para que podamos mejorar.
-En términos de licencia, por favor tómate un minuto para leer nuestro breve [Acuerdo de Licencia y Colaborador](../LICENSE). La comunidad también se adhiere al [código de conducta](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
+En términos de licencia, por favor tómate un minuto para leer nuestro breve [Acuerdo de Licencia y Colaborador](../../LICENSE). La comunidad también se adhiere al [código de conducta](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
## Antes de empezar
diff --git a/README/README_ES.md b/docs/es-ES/README.md
similarity index 79%
rename from README/README_ES.md
rename to docs/es-ES/README.md
index 60f0a06868..b005691fea 100644
--- a/README/README_ES.md
+++ b/docs/es-ES/README.md
@@ -1,4 +1,4 @@
-
+
Dify Cloud ·
@@ -35,17 +35,19 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
#
@@ -108,7 +110,7 @@ Dale estrella a Dify en GitHub y serás notificado instantáneamente de las nuev
-La forma más fácil de iniciar el servidor de Dify es ejecutar nuestro archivo [docker-compose.yml](docker/docker-compose.yaml). Antes de ejecutar el comando de instalación, asegúrate de que [Docker](https://docs.docker.com/get-docker/) y [Docker Compose](https://docs.docker.com/compose/install/) estén instalados en tu máquina:
+La forma más fácil de iniciar el servidor de Dify es ejecutar nuestro archivo [docker-compose.yml](../../docker/docker-compose.yaml). Antes de ejecutar el comando de instalación, asegúrate de que [Docker](https://docs.docker.com/get-docker/) y [Docker Compose](https://docs.docker.com/compose/install/) estén instalados en tu máquina:
```bash
cd docker
@@ -122,7 +124,7 @@ Después de ejecutarlo, puedes acceder al panel de control de Dify en tu navegad
## Próximos pasos
-Si necesita personalizar la configuración, consulte los comentarios en nuestro archivo [.env.example](docker/.env.example) y actualice los valores correspondientes en su archivo `.env`. Además, es posible que deba realizar ajustes en el propio archivo `docker-compose.yaml`, como cambiar las versiones de las imágenes, las asignaciones de puertos o los montajes de volúmenes, según su entorno de implementación y requisitos específicos. Después de realizar cualquier cambio, vuelva a ejecutar `docker-compose up -d`. Puede encontrar la lista completa de variables de entorno disponibles [aquí](https://docs.dify.ai/getting-started/install-self-hosted/environments).
+Si necesita personalizar la configuración, consulte los comentarios en nuestro archivo [.env.example](../../docker/.env.example) y actualice los valores correspondientes en su archivo `.env`. Además, es posible que deba realizar ajustes en el propio archivo `docker-compose.yaml`, como cambiar las versiones de las imágenes, las asignaciones de puertos o los montajes de volúmenes, según su entorno de implementación y requisitos específicos. Después de realizar cualquier cambio, vuelva a ejecutar `docker-compose up -d`. Puede encontrar la lista completa de variables de entorno disponibles [aquí](https://docs.dify.ai/getting-started/install-self-hosted/environments).
. Después de realizar los cambios, ejecuta `docker-compose up -d` nuevamente. Puedes ver la lista completa de variables de entorno [aquí](https://docs.dify.ai/getting-started/install-self-hosted/environments).
@@ -170,7 +172,7 @@ Implementa Dify en AKS con un clic usando [Azure Devops Pipeline Helm Chart by @
## Contribuir
-Para aquellos que deseen contribuir con código, consulten nuestra [Guía de contribución](https://github.com/langgenius/dify/blob/main/CONTRIBUTING/CONTRIBUTING_ES.md).
+Para aquellos que deseen contribuir con código, consulten nuestra [Guía de contribución](./CONTRIBUTING.md).
Al mismo tiempo, considera apoyar a Dify compartiéndolo en redes sociales y en eventos y conferencias.
> Estamos buscando colaboradores para ayudar con la traducción de Dify a idiomas que no sean el mandarín o el inglés. Si estás interesado en ayudar, consulta el [README de i18n](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) para obtener más información y déjanos un comentario en el canal `global-users` de nuestro [Servidor de Comunidad en Discord](https://discord.gg/8Tpq4AcN9c).
@@ -184,7 +186,7 @@ Al mismo tiempo, considera apoyar a Dify compartiéndolo en redes sociales y en
## Comunidad y Contacto
- [Discusión en GitHub](https://github.com/langgenius/dify/discussions). Lo mejor para: compartir comentarios y hacer preguntas.
-- [Reporte de problemas en GitHub](https://github.com/langgenius/dify/issues). Lo mejor para: errores que encuentres usando Dify.AI y propuestas de características. Consulta nuestra [Guía de contribución](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
+- [Reporte de problemas en GitHub](https://github.com/langgenius/dify/issues). Lo mejor para: errores que encuentres usando Dify.AI y propuestas de características. Consulta nuestra [Guía de contribución](./CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Lo mejor para: compartir tus aplicaciones y pasar el rato con la comunidad.
- [X(Twitter)](https://twitter.com/dify_ai). Lo mejor para: compartir tus aplicaciones y pasar el rato con la comunidad.
@@ -198,12 +200,4 @@ Para proteger tu privacidad, evita publicar problemas de seguridad en GitHub. En
## Licencia
-Este repositorio está disponible bajo la [Licencia de Código Abierto de Dify](../LICENSE), que es esencialmente Apache 2.0 con algunas restricciones adicionales.
-
-## Divulgación de Seguridad
-
-Para proteger tu privacidad, evita publicar problemas de seguridad en GitHub. En su lugar, envía tus preguntas a security@dify.ai y te proporcionaremos una respuesta más detallada.
-
-## Licencia
-
-Este repositorio está disponible bajo la [Licencia de Código Abierto de Dify](../LICENSE), que es esencialmente Apache 2.0 con algunas restricciones adicionales.
+Este repositorio está disponible bajo la [Licencia de Código Abierto de Dify](../../LICENSE), que es esencialmente Apache 2.0 con algunas restricciones adicionales.
diff --git a/CONTRIBUTING/CONTRIBUTING_FR.md b/docs/fr-FR/CONTRIBUTING.md
similarity index 96%
rename from CONTRIBUTING/CONTRIBUTING_FR.md
rename to docs/fr-FR/CONTRIBUTING.md
index 8df491a0a0..74e44ca734 100644
--- a/CONTRIBUTING/CONTRIBUTING_FR.md
+++ b/docs/fr-FR/CONTRIBUTING.md
@@ -6,7 +6,7 @@ Nous devons être agiles et livrer rapidement compte tenu de notre position, mai
Ce guide, comme Dify lui-même, est un travail en constante évolution. Nous apprécions grandement votre compréhension si parfois il est en retard par rapport au projet réel, et nous accueillons tout commentaire pour nous aider à nous améliorer.
-En termes de licence, veuillez prendre une minute pour lire notre bref [Accord de Licence et de Contributeur](../LICENSE). La communauté adhère également au [code de conduite](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
+En termes de licence, veuillez prendre une minute pour lire notre bref [Accord de Licence et de Contributeur](../../LICENSE). La communauté adhère également au [code de conduite](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
## Avant de vous lancer
diff --git a/README/README_FR.md b/docs/fr-FR/README.md
similarity index 79%
rename from README/README_FR.md
rename to docs/fr-FR/README.md
index a782bd16f8..3aca9a9672 100644
--- a/README/README_FR.md
+++ b/docs/fr-FR/README.md
@@ -1,4 +1,4 @@
-
+
Dify Cloud ·
@@ -35,17 +35,19 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
#
@@ -108,7 +110,7 @@ Mettez une étoile à Dify sur GitHub et soyez instantanément informé des nouv
-La manière la plus simple de démarrer le serveur Dify est d'exécuter notre fichier [docker-compose.yml](docker/docker-compose.yaml). Avant d'exécuter la commande d'installation, assurez-vous que [Docker](https://docs.docker.com/get-docker/) et [Docker Compose](https://docs.docker.com/compose/install/) sont installés sur votre machine:
+La manière la plus simple de démarrer le serveur Dify est d'exécuter notre fichier [docker-compose.yml](../../docker/docker-compose.yaml). Avant d'exécuter la commande d'installation, assurez-vous que [Docker](https://docs.docker.com/get-docker/) et [Docker Compose](https://docs.docker.com/compose/install/) sont installés sur votre machine:
```bash
cd docker
@@ -122,7 +124,7 @@ Après l'exécution, vous pouvez accéder au tableau de bord Dify dans votre nav
## Prochaines étapes
-Si vous devez personnaliser la configuration, veuillez vous référer aux commentaires dans notre fichier [.env.example](docker/.env.example) et mettre à jour les valeurs correspondantes dans votre fichier `.env`. De plus, vous devrez peut-être apporter des modifications au fichier `docker-compose.yaml` lui-même, comme changer les versions d'image, les mappages de ports ou les montages de volumes, en fonction de votre environnement de déploiement et de vos exigences spécifiques. Après avoir effectué des modifications, veuillez réexécuter `docker-compose up -d`. Vous pouvez trouver la liste complète des variables d'environnement disponibles [ici](https://docs.dify.ai/getting-started/install-self-hosted/environments).
+Si vous devez personnaliser la configuration, veuillez vous référer aux commentaires dans notre fichier [.env.example](../../docker/.env.example) et mettre à jour les valeurs correspondantes dans votre fichier `.env`. De plus, vous devrez peut-être apporter des modifications au fichier `docker-compose.yaml` lui-même, comme changer les versions d'image, les mappages de ports ou les montages de volumes, en fonction de votre environnement de déploiement et de vos exigences spécifiques. Après avoir effectué des modifications, veuillez réexécuter `docker-compose up -d`. Vous pouvez trouver la liste complète des variables d'environnement disponibles [ici](https://docs.dify.ai/getting-started/install-self-hosted/environments).
Si vous souhaitez configurer une configuration haute disponibilité, la communauté fournit des [Helm Charts](https://helm.sh/) et des fichiers YAML, à travers lesquels vous pouvez déployer Dify sur Kubernetes.
@@ -168,7 +170,7 @@ Déployez Dify sur AKS en un clic en utilisant [Azure Devops Pipeline Helm Chart
## Contribuer
-Pour ceux qui souhaitent contribuer du code, consultez notre [Guide de contribution](https://github.com/langgenius/dify/blob/main/CONTRIBUTING/CONTRIBUTING_FR.md).
+Pour ceux qui souhaitent contribuer du code, consultez notre [Guide de contribution](./CONTRIBUTING.md).
Dans le même temps, veuillez envisager de soutenir Dify en le partageant sur les réseaux sociaux et lors d'événements et de conférences.
> Nous recherchons des contributeurs pour aider à traduire Dify dans des langues autres que le mandarin ou l'anglais. Si vous êtes intéressé à aider, veuillez consulter le [README i18n](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) pour plus d'informations, et laissez-nous un commentaire dans le canal `global-users` de notre [Serveur communautaire Discord](https://discord.gg/8Tpq4AcN9c).
@@ -182,7 +184,7 @@ Dans le même temps, veuillez envisager de soutenir Dify en le partageant sur le
## Communauté & Contact
- [Discussion GitHub](https://github.com/langgenius/dify/discussions). Meilleur pour: partager des commentaires et poser des questions.
-- [Problèmes GitHub](https://github.com/langgenius/dify/issues). Meilleur pour: les bogues que vous rencontrez en utilisant Dify.AI et les propositions de fonctionnalités. Consultez notre [Guide de contribution](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
+- [Problèmes GitHub](https://github.com/langgenius/dify/issues). Meilleur pour: les bogues que vous rencontrez en utilisant Dify.AI et les propositions de fonctionnalités. Consultez notre [Guide de contribution](./CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Meilleur pour: partager vos applications et passer du temps avec la communauté.
- [X(Twitter)](https://twitter.com/dify_ai). Meilleur pour: partager vos applications et passer du temps avec la communauté.
@@ -196,12 +198,4 @@ Pour protéger votre vie privée, veuillez éviter de publier des problèmes de
## Licence
-Ce référentiel est disponible sous la [Licence open source Dify](../LICENSE), qui est essentiellement l'Apache 2.0 avec quelques restrictions supplémentaires.
-
-## Divulgation de sécurité
-
-Pour protéger votre vie privée, veuillez éviter de publier des problèmes de sécurité sur GitHub. Au lieu de cela, envoyez vos questions à security@dify.ai et nous vous fournirons une réponse plus détaillée.
-
-## Licence
-
-Ce référentiel est disponible sous la [Licence open source Dify](../LICENSE), qui est essentiellement l'Apache 2.0 avec quelques restrictions supplémentaires.
+Ce référentiel est disponible sous la [Licence open source Dify](../../LICENSE), qui est essentiellement l'Apache 2.0 avec quelques restrictions supplémentaires.
diff --git a/CONTRIBUTING/CONTRIBUTING_JA.md b/docs/ja-JP/CONTRIBUTING.md
similarity index 96%
rename from CONTRIBUTING/CONTRIBUTING_JA.md
rename to docs/ja-JP/CONTRIBUTING.md
index dd3d6cbfc5..4ee7d8c963 100644
--- a/CONTRIBUTING/CONTRIBUTING_JA.md
+++ b/docs/ja-JP/CONTRIBUTING.md
@@ -6,7 +6,7 @@ Difyに貢献しようとお考えですか?素晴らしいですね。私た
このガイドは、Dify自体と同様に、常に進化し続けています。実際のプロジェクトの進行状況と多少のずれが生じる場合もございますが、ご理解いただけますと幸いです。改善のためのフィードバックも歓迎いたします。
-ライセンスについては、[ライセンスと貢献者同意書](../LICENSE)をご一読ください。また、コミュニティは[行動規範](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)に従っています。
+ライセンスについては、[ライセンスと貢献者同意書](../../LICENSE)をご一読ください。また、コミュニティは[行動規範](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)に従っています。
## 始める前に
diff --git a/README/README_JA.md b/docs/ja-JP/README.md
similarity index 79%
rename from README/README_JA.md
rename to docs/ja-JP/README.md
index 23cd0e692b..66831285d6 100644
--- a/README/README_JA.md
+++ b/docs/ja-JP/README.md
@@ -1,4 +1,4 @@
-
+
Dify Cloud ·
@@ -35,17 +35,19 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
#
@@ -109,7 +111,7 @@ GitHub上でDifyにスターを付けることで、Difyに関する新しいニ
-Difyサーバーを起動する最も簡単な方法は、[docker-compose.yml](docker/docker-compose.yaml)ファイルを実行することです。インストールコマンドを実行する前に、マシンに[Docker](https://docs.docker.com/get-docker/)と[Docker Compose](https://docs.docker.com/compose/install/)がインストールされていることを確認してください。
+Difyサーバーを起動する最も簡単な方法は、[docker-compose.yml](../../docker/docker-compose.yaml)ファイルを実行することです。インストールコマンドを実行する前に、マシンに[Docker](https://docs.docker.com/get-docker/)と[Docker Compose](https://docs.docker.com/compose/install/)がインストールされていることを確認してください。
```bash
cd docker
@@ -123,7 +125,7 @@ docker compose up -d
## 次のステップ
-設定をカスタマイズする必要がある場合は、[.env.example](docker/.env.example) ファイルのコメントを参照し、`.env` ファイルの対応する値を更新してください。さらに、デプロイ環境や要件に応じて、`docker-compose.yaml` ファイル自体を調整する必要がある場合があります。たとえば、イメージのバージョン、ポートのマッピング、ボリュームのマウントなどを変更します。変更を加えた後は、`docker-compose up -d` を再実行してください。利用可能な環境変数の全一覧は、[こちら](https://docs.dify.ai/getting-started/install-self-hosted/environments)で確認できます。
+設定をカスタマイズする必要がある場合は、[.env.example](../../docker/.env.example) ファイルのコメントを参照し、`.env` ファイルの対応する値を更新してください。さらに、デプロイ環境や要件に応じて、`docker-compose.yaml` ファイル自体を調整する必要がある場合があります。たとえば、イメージのバージョン、ポートのマッピング、ボリュームのマウントなどを変更します。変更を加えた後は、`docker-compose up -d` を再実行してください。利用可能な環境変数の全一覧は、[こちら](https://docs.dify.ai/getting-started/install-self-hosted/environments)で確認できます。
高可用性設定を設定する必要がある場合、コミュニティは[Helm Charts](https://helm.sh/)とYAMLファイルにより、DifyをKubernetesにデプロイすることができます。
@@ -169,7 +171,7 @@ docker compose up -d
## 貢献
-コードに貢献したい方は、[Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING/CONTRIBUTING_JA.md)を参照してください。
+コードに貢献したい方は、[Contribution Guide](./CONTRIBUTING.md)を参照してください。
同時に、DifyをSNSやイベント、カンファレンスで共有してサポートしていただけると幸いです。
> Difyを英語または中国語以外の言語に翻訳してくれる貢献者を募集しています。興味がある場合は、詳細については[i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md)を参照してください。また、[Discordコミュニティサーバー](https://discord.gg/8Tpq4AcN9c)の`global-users`チャンネルにコメントを残してください。
@@ -183,10 +185,10 @@ docker compose up -d
## コミュニティ & お問い合わせ
- [GitHub Discussion](https://github.com/langgenius/dify/discussions). 主に: フィードバックの共有や質問。
-- [GitHub Issues](https://github.com/langgenius/dify/issues). 主に: Dify.AIを使用する際に発生するエラーや問題については、[貢献ガイド](../CONTRIBUTING/CONTRIBUTING_JA.md)を参照してください
+- [GitHub Issues](https://github.com/langgenius/dify/issues). 主に: Dify.AIを使用する際に発生するエラーや問題については、[貢献ガイド](./CONTRIBUTING.md)を参照してください
- [Discord](https://discord.gg/FngNHpbcY7). 主に: アプリケーションの共有やコミュニティとの交流。
- [X(Twitter)](https://twitter.com/dify_ai). 主に: アプリケーションの共有やコミュニティとの交流。
## ライセンス
-このリポジトリは、Dify Open Source License にいくつかの追加制限を加えた[Difyオープンソースライセンス](../LICENSE)の下で利用可能です。
+このリポジトリは、Dify Open Source License にいくつかの追加制限を加えた[Difyオープンソースライセンス](../../LICENSE)の下で利用可能です。
diff --git a/CONTRIBUTING/CONTRIBUTING_KR.md b/docs/ko-KR/CONTRIBUTING.md
similarity index 96%
rename from CONTRIBUTING/CONTRIBUTING_KR.md
rename to docs/ko-KR/CONTRIBUTING.md
index f94d5bfbc9..9c171c3561 100644
--- a/CONTRIBUTING/CONTRIBUTING_KR.md
+++ b/docs/ko-KR/CONTRIBUTING.md
@@ -6,7 +6,7 @@ Dify에 기여하려고 하시는군요 - 정말 멋집니다, 당신이 무엇
이 가이드는 Dify 자체와 마찬가지로 끊임없이 진행 중인 작업입니다. 때로는 실제 프로젝트보다 뒤처질 수 있다는 점을 이해해 주시면 감사하겠으며, 개선을 위한 피드백은 언제든지 환영합니다.
-라이센스 측면에서, 간략한 [라이센스 및 기여자 동의서](../LICENSE)를 읽어보는 시간을 가져주세요. 커뮤니티는 또한 [행동 강령](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)을 준수합니다.
+라이센스 측면에서, 간략한 [라이센스 및 기여자 동의서](../../LICENSE)를 읽어보는 시간을 가져주세요. 커뮤니티는 또한 [행동 강령](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)을 준수합니다.
## 시작하기 전에
diff --git a/README/README_KR.md b/docs/ko-KR/README.md
similarity index 79%
rename from README/README_KR.md
rename to docs/ko-KR/README.md
index e1a2a82677..ec67bc90ed 100644
--- a/README/README_KR.md
+++ b/docs/ko-KR/README.md
@@ -1,4 +1,4 @@
-
+
Dify 클라우드 ·
@@ -35,17 +35,19 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
Dify는 오픈 소스 LLM 앱 개발 플랫폼입니다. 직관적인 인터페이스를 통해 AI 워크플로우, RAG 파이프라인, 에이전트 기능, 모델 관리, 관찰 기능 등을 결합하여 프로토타입에서 프로덕션까지 빠르게 전환할 수 있습니다. 주요 기능 목록은 다음과 같습니다:
@@ -102,7 +104,7 @@ GitHub에서 Dify에 별표를 찍어 새로운 릴리스를 즉시 알림 받
-Dify 서버를 시작하는 가장 쉬운 방법은 [docker-compose.yml](docker/docker-compose.yaml) 파일을 실행하는 것입니다. 설치 명령을 실행하기 전에 [Docker](https://docs.docker.com/get-docker/) 및 [Docker Compose](https://docs.docker.com/compose/install/)가 머신에 설치되어 있는지 확인하세요.
+Dify 서버를 시작하는 가장 쉬운 방법은 [docker-compose.yml](../../docker/docker-compose.yaml) 파일을 실행하는 것입니다. 설치 명령을 실행하기 전에 [Docker](https://docs.docker.com/get-docker/) 및 [Docker Compose](https://docs.docker.com/compose/install/)가 머신에 설치되어 있는지 확인하세요.
```bash
cd docker
@@ -116,7 +118,7 @@ docker compose up -d
## 다음 단계
-구성을 사용자 정의해야 하는 경우 [.env.example](docker/.env.example) 파일의 주석을 참조하고 `.env` 파일에서 해당 값을 업데이트하십시오. 또한 특정 배포 환경 및 요구 사항에 따라 `docker-compose.yaml` 파일 자체를 조정해야 할 수도 있습니다. 예를 들어 이미지 버전, 포트 매핑 또는 볼륨 마운트를 변경합니다. 변경 한 후 `docker-compose up -d`를 다시 실행하십시오. 사용 가능한 환경 변수의 전체 목록은 [여기](https://docs.dify.ai/getting-started/install-self-hosted/environments)에서 찾을 수 있습니다.
+구성을 사용자 정의해야 하는 경우 [.env.example](../../docker/.env.example) 파일의 주석을 참조하고 `.env` 파일에서 해당 값을 업데이트하십시오. 또한 특정 배포 환경 및 요구 사항에 따라 `docker-compose.yaml` 파일 자체를 조정해야 할 수도 있습니다. 예를 들어 이미지 버전, 포트 매핑 또는 볼륨 마운트를 변경합니다. 변경 한 후 `docker-compose up -d`를 다시 실행하십시오. 사용 가능한 환경 변수의 전체 목록은 [여기](https://docs.dify.ai/getting-started/install-self-hosted/environments)에서 찾을 수 있습니다.
Dify를 Kubernetes에 배포하고 프리미엄 스케일링 설정을 구성했다는 커뮤니티가 제공하는 [Helm Charts](https://helm.sh/)와 YAML 파일이 존재합니다.
@@ -162,7 +164,7 @@ Dify를 Kubernetes에 배포하고 프리미엄 스케일링 설정을 구성했
## 기여
-코드에 기여하고 싶은 분들은 [기여 가이드](https://github.com/langgenius/dify/blob/main/CONTRIBUTING/CONTRIBUTING_KR.md)를 참조하세요.
+코드에 기여하고 싶은 분들은 [기여 가이드](./CONTRIBUTING.md)를 참조하세요.
동시에 Dify를 소셜 미디어와 행사 및 컨퍼런스에 공유하여 지원하는 것을 고려해 주시기 바랍니다.
> 우리는 Dify를 중국어나 영어 이외의 언어로 번역하는 데 도움을 줄 수 있는 기여자를 찾고 있습니다. 도움을 주고 싶으시다면 [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md)에서 더 많은 정보를 확인하시고 [Discord 커뮤니티 서버](https://discord.gg/8Tpq4AcN9c)의 `global-users` 채널에 댓글을 남겨주세요.
@@ -176,7 +178,7 @@ Dify를 Kubernetes에 배포하고 프리미엄 스케일링 설정을 구성했
## 커뮤니티 & 연락처
- [GitHub 토론](https://github.com/langgenius/dify/discussions). 피드백 공유 및 질문하기에 적합합니다.
-- [GitHub 이슈](https://github.com/langgenius/dify/issues). Dify.AI 사용 중 발견한 버그와 기능 제안에 적합합니다. [기여 가이드](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md)를 참조하세요.
+- [GitHub 이슈](https://github.com/langgenius/dify/issues). Dify.AI 사용 중 발견한 버그와 기능 제안에 적합합니다. [기여 가이드](./CONTRIBUTING.md)를 참조하세요.
- [디스코드](https://discord.gg/FngNHpbcY7). 애플리케이션 공유 및 커뮤니티와 소통하기에 적합합니다.
- [트위터](https://twitter.com/dify_ai). 애플리케이션 공유 및 커뮤니티와 소통하기에 적합합니다.
@@ -190,4 +192,4 @@ Dify를 Kubernetes에 배포하고 프리미엄 스케일링 설정을 구성했
## 라이선스
-이 저장소는 기본적으로 몇 가지 추가 제한 사항이 있는 Apache 2.0인 [Dify 오픈 소스 라이선스](../LICENSE)에 따라 사용할 수 있습니다.
+이 저장소는 기본적으로 몇 가지 추가 제한 사항이 있는 Apache 2.0인 [Dify 오픈 소스 라이선스](../../LICENSE)에 따라 사용할 수 있습니다.
diff --git a/CONTRIBUTING/CONTRIBUTING_PT.md b/docs/pt-BR/CONTRIBUTING.md
similarity index 96%
rename from CONTRIBUTING/CONTRIBUTING_PT.md
rename to docs/pt-BR/CONTRIBUTING.md
index 2aec1e2196..737b2ddce2 100644
--- a/CONTRIBUTING/CONTRIBUTING_PT.md
+++ b/docs/pt-BR/CONTRIBUTING.md
@@ -6,7 +6,7 @@ Precisamos ser ágeis e entregar rapidamente considerando onde estamos, mas tamb
Este guia, como o próprio Dify, é um trabalho em constante evolução. Agradecemos muito a sua compreensão se às vezes ele ficar atrasado em relação ao projeto real, e damos as boas-vindas a qualquer feedback para que possamos melhorar.
-Em termos de licenciamento, por favor, dedique um minuto para ler nosso breve [Acordo de Licença e Contribuidor](../LICENSE). A comunidade também adere ao [código de conduta](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
+Em termos de licenciamento, por favor, dedique um minuto para ler nosso breve [Acordo de Licença e Contribuidor](../../LICENSE). A comunidade também adere ao [código de conduta](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
## Antes de começar
diff --git a/README/README_PT.md b/docs/pt-BR/README.md
similarity index 78%
rename from README/README_PT.md
rename to docs/pt-BR/README.md
index 91132aade4..78383a3c76 100644
--- a/README/README_PT.md
+++ b/docs/pt-BR/README.md
@@ -1,4 +1,4 @@
-
+
📌 Introduzindo o Dify Workflow com Upload de Arquivo: Recrie o Podcast Google NotebookLM
@@ -39,18 +39,20 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Dify é uma plataforma de desenvolvimento de aplicativos LLM de código aberto. Sua interface intuitiva combina workflow de IA, pipeline RAG, capacidades de agente, gerenciamento de modelos, recursos de observabilidade e muito mais, permitindo que você vá rapidamente do protótipo à produção. Aqui está uma lista das principais funcionalidades:
@@ -108,7 +110,7 @@ Dê uma estrela no Dify no GitHub e seja notificado imediatamente sobre novos la
-A maneira mais fácil de iniciar o servidor Dify é executar nosso arquivo [docker-compose.yml](docker/docker-compose.yaml). Antes de rodar o comando de instalação, certifique-se de que o [Docker](https://docs.docker.com/get-docker/) e o [Docker Compose](https://docs.docker.com/compose/install/) estão instalados na sua máquina:
+A maneira mais fácil de iniciar o servidor Dify é executar nosso arquivo [docker-compose.yml](../../docker/docker-compose.yaml). Antes de rodar o comando de instalação, certifique-se de que o [Docker](https://docs.docker.com/get-docker/) e o [Docker Compose](https://docs.docker.com/compose/install/) estão instalados na sua máquina:
```bash
cd docker
@@ -122,7 +124,7 @@ Após a execução, você pode acessar o painel do Dify no navegador em [http://
## Próximos passos
-Se precisar personalizar a configuração, consulte os comentários no nosso arquivo [.env.example](docker/.env.example) e atualize os valores correspondentes no seu arquivo `.env`. Além disso, talvez seja necessário fazer ajustes no próprio arquivo `docker-compose.yaml`, como alterar versões de imagem, mapeamentos de portas ou montagens de volumes, com base no seu ambiente de implantação específico e nas suas necessidades. Após fazer quaisquer alterações, execute novamente `docker-compose up -d`. Você pode encontrar a lista completa de variáveis de ambiente disponíveis [aqui](https://docs.dify.ai/getting-started/install-self-hosted/environments).
+Se precisar personalizar a configuração, consulte os comentários no nosso arquivo [.env.example](../../docker/.env.example) e atualize os valores correspondentes no seu arquivo `.env`. Além disso, talvez seja necessário fazer ajustes no próprio arquivo `docker-compose.yaml`, como alterar versões de imagem, mapeamentos de portas ou montagens de volumes, com base no seu ambiente de implantação específico e nas suas necessidades. Após fazer quaisquer alterações, execute novamente `docker-compose up -d`. Você pode encontrar a lista completa de variáveis de ambiente disponíveis [aqui](https://docs.dify.ai/getting-started/install-self-hosted/environments).
Se deseja configurar uma instalação de alta disponibilidade, há [Helm Charts](https://helm.sh/) e arquivos YAML contribuídos pela comunidade que permitem a implantação do Dify no Kubernetes.
@@ -168,7 +170,7 @@ Implante o Dify no AKS com um clique usando [Azure Devops Pipeline Helm Chart by
## Contribuindo
-Para aqueles que desejam contribuir com código, veja nosso [Guia de Contribuição](https://github.com/langgenius/dify/blob/main/CONTRIBUTING/CONTRIBUTING_PT.md).
+Para aqueles que desejam contribuir com código, veja nosso [Guia de Contribuição](./CONTRIBUTING.md).
Ao mesmo tempo, considere apoiar o Dify compartilhando-o nas redes sociais e em eventos e conferências.
> Estamos buscando contribuidores para ajudar na tradução do Dify para idiomas além de Mandarim e Inglês. Se você tiver interesse em ajudar, consulte o [README i18n](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) para mais informações e deixe-nos um comentário no canal `global-users` em nosso [Servidor da Comunidade no Discord](https://discord.gg/8Tpq4AcN9c).
@@ -182,7 +184,7 @@ Ao mesmo tempo, considere apoiar o Dify compartilhando-o nas redes sociais e em
## Comunidade e contato
- [Discussões no GitHub](https://github.com/langgenius/dify/discussions). Melhor para: compartilhar feedback e fazer perguntas.
-- [Problemas no GitHub](https://github.com/langgenius/dify/issues). Melhor para: relatar bugs encontrados no Dify.AI e propor novos recursos. Veja nosso [Guia de Contribuição](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
+- [Problemas no GitHub](https://github.com/langgenius/dify/issues). Melhor para: relatar bugs encontrados no Dify.AI e propor novos recursos. Veja nosso [Guia de Contribuição](./CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Melhor para: compartilhar suas aplicações e interagir com a comunidade.
- [X(Twitter)](https://twitter.com/dify_ai). Melhor para: compartilhar suas aplicações e interagir com a comunidade.
@@ -196,4 +198,4 @@ Para proteger sua privacidade, evite postar problemas de segurança no GitHub. E
## Licença
-Este repositório está disponível sob a [Licença de Código Aberto Dify](../LICENSE), que é essencialmente Apache 2.0 com algumas restrições adicionais.
+Este repositório está disponível sob a [Licença de Código Aberto Dify](../../LICENSE), que é essencialmente Apache 2.0 com algumas restrições adicionais.
diff --git a/README/README_SI.md b/docs/sl-SI/README.md
similarity index 83%
rename from README/README_SI.md
rename to docs/sl-SI/README.md
index 8cd78c065c..65aedb7703 100644
--- a/README/README_SI.md
+++ b/docs/sl-SI/README.md
@@ -1,4 +1,4 @@
-
+
📌 Predstavljamo nalaganje datotek Dify Workflow: znova ustvarite Google NotebookLM Podcast
@@ -36,18 +36,20 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Dify je odprtokodna platforma za razvoj aplikacij LLM. Njegov intuitivni vmesnik združuje agentski potek dela z umetno inteligenco, cevovod RAG, zmogljivosti agentov, upravljanje modelov, funkcije opazovanja in več, kar vam omogoča hiter prehod od prototipa do proizvodnje.
@@ -169,7 +171,7 @@ Z enim klikom namestite Dify v AKS z uporabo [Azure Devops Pipeline Helm Chart b
## Prispevam
-Za tiste, ki bi radi prispevali kodo, si oglejte naš vodnik za prispevke . Hkrati vas prosimo, da podprete Dify tako, da ga delite na družbenih medijih ter na dogodkih in konferencah.
+Za tiste, ki bi radi prispevali kodo, si oglejte naš [vodnik za prispevke](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md). Hkrati vas prosimo, da podprete Dify tako, da ga delite na družbenih medijih ter na dogodkih in konferencah.
> Iščemo sodelavce za pomoč pri prevajanju Difyja v jezike, ki niso mandarinščina ali angleščina. Če želite pomagati, si oglejte i18n README za več informacij in nam pustite komentar v global-userskanalu našega strežnika skupnosti Discord .
@@ -196,4 +198,4 @@ Zaradi zaščite vaše zasebnosti se izogibajte objavljanju varnostnih vprašanj
## Licenca
-To skladišče je na voljo pod [odprtokodno licenco Dify](../LICENSE) , ki je v bistvu Apache 2.0 z nekaj dodatnimi omejitvami.
+To skladišče je na voljo pod [odprtokodno licenco Dify](../../LICENSE) , ki je v bistvu Apache 2.0 z nekaj dodatnimi omejitvami.
diff --git a/README/README_KL.md b/docs/tlh/README.md
similarity index 79%
rename from README/README_KL.md
rename to docs/tlh/README.md
index cae02f56fe..b1e3016efd 100644
--- a/README/README_KL.md
+++ b/docs/tlh/README.md
@@ -1,4 +1,4 @@
-
+
Dify Cloud ·
@@ -35,17 +35,19 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
#
@@ -108,7 +110,7 @@ Star Dify on GitHub and be instantly notified of new releases.
-The easiest way to start the Dify server is to run our [docker-compose.yml](docker/docker-compose.yaml) file. Before running the installation command, make sure that [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) are installed on your machine:
+The easiest way to start the Dify server is to run our [docker-compose.yml](../../docker/docker-compose.yaml) file. Before running the installation command, make sure that [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) are installed on your machine:
```bash
cd docker
@@ -122,7 +124,7 @@ After running, you can access the Dify dashboard in your browser at [http://loca
## Next steps
-If you need to customize the configuration, please refer to the comments in our [.env.example](docker/.env.example) file and update the corresponding values in your `.env` file. Additionally, you might need to make adjustments to the `docker-compose.yaml` file itself, such as changing image versions, port mappings, or volume mounts, based on your specific deployment environment and requirements. After making any changes, please re-run `docker-compose up -d`. You can find the full list of available environment variables [here](https://docs.dify.ai/getting-started/install-self-hosted/environments).
+If you need to customize the configuration, please refer to the comments in our [.env.example](../../docker/.env.example) file and update the corresponding values in your `.env` file. Additionally, you might need to make adjustments to the `docker-compose.yaml` file itself, such as changing image versions, port mappings, or volume mounts, based on your specific deployment environment and requirements. After making any changes, please re-run `docker-compose up -d`. You can find the full list of available environment variables [here](https://docs.dify.ai/getting-started/install-self-hosted/environments).
If you'd like to configure a highly-available setup, there are community-contributed [Helm Charts](https://helm.sh/) and YAML files which allow Dify to be deployed on Kubernetes.
@@ -181,10 +183,7 @@ At the same time, please consider supporting Dify by sharing it on social media
## Community & Contact
-- \[GitHub Discussion\](https://github.com/langgenius/dify/discussions
-
-). Best for: sharing feedback and asking questions.
-
+- [GitHub Discussion](https://github.com/langgenius/dify/discussions). Best for: sharing feedback and asking questions.
- [GitHub Issues](https://github.com/langgenius/dify/issues). Best for: bugs you encounter using Dify.AI, and feature proposals. See our [Contribution Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md).
- [Discord](https://discord.gg/FngNHpbcY7). Best for: sharing your applications and hanging out with the community.
- [X(Twitter)](https://twitter.com/dify_ai). Best for: sharing your applications and hanging out with the community.
@@ -199,4 +198,4 @@ To protect your privacy, please avoid posting security issues on GitHub. Instead
## License
-This repository is available under the [Dify Open Source License](../LICENSE), which is essentially Apache 2.0 with a few additional restrictions.
+This repository is available under the [Dify Open Source License](../../LICENSE), which is essentially Apache 2.0 with a few additional restrictions.
diff --git a/CONTRIBUTING/CONTRIBUTING_TR.md b/docs/tr-TR/CONTRIBUTING.md
similarity index 96%
rename from CONTRIBUTING/CONTRIBUTING_TR.md
rename to docs/tr-TR/CONTRIBUTING.md
index 1932a3ab34..59227d31a9 100644
--- a/CONTRIBUTING/CONTRIBUTING_TR.md
+++ b/docs/tr-TR/CONTRIBUTING.md
@@ -6,7 +6,7 @@ Bulunduğumuz noktada çevik olmamız ve hızlı hareket etmemiz gerekiyor, anca
Bu rehber, Dify'ın kendisi gibi, sürekli gelişen bir çalışmadır. Bazen gerçek projenin gerisinde kalırsa anlayışınız için çok minnettarız ve gelişmemize yardımcı olacak her türlü geri bildirimi memnuniyetle karşılıyoruz.
-Lisanslama konusunda, lütfen kısa [Lisans ve Katkıda Bulunan Anlaşmamızı](../LICENSE) okumak için bir dakikanızı ayırın. Topluluk ayrıca [davranış kurallarına](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md) da uyar.
+Lisanslama konusunda, lütfen kısa [Lisans ve Katkıda Bulunan Anlaşmamızı](../../LICENSE) okumak için bir dakikanızı ayırın. Topluluk ayrıca [davranış kurallarına](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md) da uyar.
## Başlamadan Önce
diff --git a/README/README_TR.md b/docs/tr-TR/README.md
similarity index 79%
rename from README/README_TR.md
rename to docs/tr-TR/README.md
index 9836c6be61..a044da1f4e 100644
--- a/README/README_TR.md
+++ b/docs/tr-TR/README.md
@@ -1,4 +1,4 @@
-
+
Dify Bulut ·
@@ -35,17 +35,19 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
Dify, açık kaynaklı bir LLM uygulama geliştirme platformudur. Sezgisel arayüzü, AI iş akışı, RAG pipeline'ı, ajan yetenekleri, model yönetimi, gözlemlenebilirlik özellikleri ve daha fazlasını birleştirerek, prototipten üretime hızlıca geçmenizi sağlar. İşte temel özelliklerin bir listesi:
@@ -102,7 +104,7 @@ GitHub'da Dify'a yıldız verin ve yeni sürümlerden anında haberdar olun.
> - RAM >= 4GB
-Dify sunucusunu başlatmanın en kolay yolu, [docker-compose.yml](docker/docker-compose.yaml) dosyamızı çalıştırmaktır. Kurulum komutunu çalıştırmadan önce, makinenizde [Docker](https://docs.docker.com/get-docker/) ve [Docker Compose](https://docs.docker.com/compose/install/)'un kurulu olduğundan emin olun:
+Dify sunucusunu başlatmanın en kolay yolu, [docker-compose.yml](../../docker/docker-compose.yaml) dosyamızı çalıştırmaktır. Kurulum komutunu çalıştırmadan önce, makinenizde [Docker](https://docs.docker.com/get-docker/) ve [Docker Compose](https://docs.docker.com/compose/install/)'un kurulu olduğundan emin olun:
```bash
cd docker
@@ -116,7 +118,7 @@ docker compose up -d
## Sonraki adımlar
-Yapılandırmayı özelleştirmeniz gerekiyorsa, lütfen [.env.example](docker/.env.example) dosyamızdaki yorumlara bakın ve `.env` dosyanızdaki ilgili değerleri güncelleyin. Ayrıca, spesifik dağıtım ortamınıza ve gereksinimlerinize bağlı olarak `docker-compose.yaml` dosyasının kendisinde de, imaj sürümlerini, port eşlemelerini veya hacim bağlantılarını değiştirmek gibi ayarlamalar yapmanız gerekebilir. Herhangi bir değişiklik yaptıktan sonra, lütfen `docker-compose up -d` komutunu tekrar çalıştırın. Kullanılabilir tüm ortam değişkenlerinin tam listesini [burada](https://docs.dify.ai/getting-started/install-self-hosted/environments) bulabilirsiniz.
+Yapılandırmayı özelleştirmeniz gerekiyorsa, lütfen [.env.example](../../docker/.env.example) dosyamızdaki yorumlara bakın ve `.env` dosyanızdaki ilgili değerleri güncelleyin. Ayrıca, spesifik dağıtım ortamınıza ve gereksinimlerinize bağlı olarak `docker-compose.yaml` dosyasının kendisinde de, imaj sürümlerini, port eşlemelerini veya hacim bağlantılarını değiştirmek gibi ayarlamalar yapmanız gerekebilir. Herhangi bir değişiklik yaptıktan sonra, lütfen `docker-compose up -d` komutunu tekrar çalıştırın. Kullanılabilir tüm ortam değişkenlerinin tam listesini [burada](https://docs.dify.ai/getting-started/install-self-hosted/environments) bulabilirsiniz.
Yüksek kullanılabilirliğe sahip bir kurulum yapılandırmak isterseniz, Dify'ın Kubernetes üzerine dağıtılmasına olanak tanıyan topluluk katkılı [Helm Charts](https://helm.sh/) ve YAML dosyaları mevcuttur.
@@ -161,7 +163,7 @@ Dify'ı bulut platformuna tek tıklamayla dağıtın [terraform](https://www.ter
## Katkıda Bulunma
-Kod katkısında bulunmak isteyenler için [Katkı Kılavuzumuza](https://github.com/langgenius/dify/blob/main/CONTRIBUTING/CONTRIBUTING_TR.md) bakabilirsiniz.
+Kod katkısında bulunmak isteyenler için [Katkı Kılavuzumuza](./CONTRIBUTING.md) bakabilirsiniz.
Aynı zamanda, lütfen Dify'ı sosyal medyada, etkinliklerde ve konferanslarda paylaşarak desteklemeyi düşünün.
> Dify'ı Mandarin veya İngilizce dışındaki dillere çevirmemize yardımcı olacak katkıda bulunanlara ihtiyacımız var. Yardımcı olmakla ilgileniyorsanız, lütfen daha fazla bilgi için [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) dosyasına bakın ve [Discord Topluluk Sunucumuzdaki](https://discord.gg/8Tpq4AcN9c) `global-users` kanalında bize bir yorum bırakın.
@@ -175,7 +177,7 @@ Aynı zamanda, lütfen Dify'ı sosyal medyada, etkinliklerde ve konferanslarda p
## Topluluk & iletişim
- [GitHub Tartışmaları](https://github.com/langgenius/dify/discussions). En uygun: geri bildirim paylaşmak ve soru sormak için.
-- [GitHub Sorunları](https://github.com/langgenius/dify/issues). En uygun: Dify.AI kullanırken karşılaştığınız hatalar ve özellik önerileri için. [Katkı Kılavuzumuza](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) bakın.
+- [GitHub Sorunları](https://github.com/langgenius/dify/issues). En uygun: Dify.AI kullanırken karşılaştığınız hatalar ve özellik önerileri için. [Katkı Kılavuzumuza](./CONTRIBUTING.md) bakın.
- [Discord](https://discord.gg/FngNHpbcY7). En uygun: uygulamalarınızı paylaşmak ve toplulukla vakit geçirmek için.
- [X(Twitter)](https://twitter.com/dify_ai). En uygun: uygulamalarınızı paylaşmak ve toplulukla vakit geçirmek için.
@@ -189,4 +191,4 @@ Gizliliğinizi korumak için, lütfen güvenlik sorunlarını GitHub'da paylaşm
## Lisans
-Bu depo, temel olarak Apache 2.0 lisansı ve birkaç ek kısıtlama içeren [Dify Açık Kaynak Lisansı](../LICENSE) altında kullanıma sunulmuştur.
+Bu depo, temel olarak Apache 2.0 lisansı ve birkaç ek kısıtlama içeren [Dify Açık Kaynak Lisansı](../../LICENSE) altında kullanıma sunulmuştur.
diff --git a/CONTRIBUTING/CONTRIBUTING_VI.md b/docs/vi-VN/CONTRIBUTING.md
similarity index 96%
rename from CONTRIBUTING/CONTRIBUTING_VI.md
rename to docs/vi-VN/CONTRIBUTING.md
index b9844c4869..fa1d875f83 100644
--- a/CONTRIBUTING/CONTRIBUTING_VI.md
+++ b/docs/vi-VN/CONTRIBUTING.md
@@ -6,7 +6,7 @@ Chúng tôi cần phải nhanh nhẹn và triển khai nhanh chóng, nhưng cũn
Hướng dẫn này, giống như Dify, đang được phát triển liên tục. Chúng tôi rất cảm kích sự thông cảm của bạn nếu đôi khi nó chưa theo kịp dự án thực tế, và hoan nghênh mọi phản hồi để cải thiện.
-Về giấy phép, vui lòng dành chút thời gian đọc [Thỏa thuận Cấp phép và Người đóng góp](../LICENSE) ngắn gọn của chúng tôi. Cộng đồng cũng tuân theo [quy tắc ứng xử](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
+Về giấy phép, vui lòng dành chút thời gian đọc [Thỏa thuận Cấp phép và Người đóng góp](../../LICENSE) ngắn gọn của chúng tôi. Cộng đồng cũng tuân theo [quy tắc ứng xử](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md).
## Trước khi bắt đầu
diff --git a/README/README_VI.md b/docs/vi-VN/README.md
similarity index 80%
rename from README/README_VI.md
rename to docs/vi-VN/README.md
index 22d74eb31d..847641da12 100644
--- a/README/README_VI.md
+++ b/docs/vi-VN/README.md
@@ -1,4 +1,4 @@
-
+
Dify Cloud ·
@@ -35,17 +35,19 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
Dify là một nền tảng phát triển ứng dụng LLM mã nguồn mở. Giao diện trực quan kết hợp quy trình làm việc AI, mô hình RAG, khả năng tác nhân, quản lý mô hình, tính năng quan sát và hơn thế nữa, cho phép bạn nhanh chóng chuyển từ nguyên mẫu sang sản phẩm. Đây là danh sách các tính năng cốt lõi:
@@ -103,7 +105,7 @@ Yêu thích Dify trên GitHub và được thông báo ngay lập tức về cá
-Cách dễ nhất để khởi động máy chủ Dify là chạy tệp [docker-compose.yml](docker/docker-compose.yaml) của chúng tôi. Trước khi chạy lệnh cài đặt, hãy đảm bảo rằng [Docker](https://docs.docker.com/get-docker/) và [Docker Compose](https://docs.docker.com/compose/install/) đã được cài đặt trên máy của bạn:
+Cách dễ nhất để khởi động máy chủ Dify là chạy tệp [docker-compose.yml](../../docker/docker-compose.yaml) của chúng tôi. Trước khi chạy lệnh cài đặt, hãy đảm bảo rằng [Docker](https://docs.docker.com/get-docker/) và [Docker Compose](https://docs.docker.com/compose/install/) đã được cài đặt trên máy của bạn:
```bash
cd docker
@@ -117,7 +119,7 @@ Sau khi chạy, bạn có thể truy cập bảng điều khiển Dify trong tr
## Các bước tiếp theo
-Nếu bạn cần tùy chỉnh cấu hình, vui lòng tham khảo các nhận xét trong tệp [.env.example](docker/.env.example) của chúng tôi và cập nhật các giá trị tương ứng trong tệp `.env` của bạn. Ngoài ra, bạn có thể cần điều chỉnh tệp `docker-compose.yaml`, chẳng hạn như thay đổi phiên bản hình ảnh, ánh xạ cổng hoặc gắn kết khối lượng, dựa trên môi trường triển khai cụ thể và yêu cầu của bạn. Sau khi thực hiện bất kỳ thay đổi nào, vui lòng chạy lại `docker-compose up -d`. Bạn có thể tìm thấy danh sách đầy đủ các biến môi trường có sẵn [tại đây](https://docs.dify.ai/getting-started/install-self-hosted/environments).
+Nếu bạn cần tùy chỉnh cấu hình, vui lòng tham khảo các nhận xét trong tệp [.env.example](../../docker/.env.example) của chúng tôi và cập nhật các giá trị tương ứng trong tệp `.env` của bạn. Ngoài ra, bạn có thể cần điều chỉnh tệp `docker-compose.yaml`, chẳng hạn như thay đổi phiên bản hình ảnh, ánh xạ cổng hoặc gắn kết khối lượng, dựa trên môi trường triển khai cụ thể và yêu cầu của bạn. Sau khi thực hiện bất kỳ thay đổi nào, vui lòng chạy lại `docker-compose up -d`. Bạn có thể tìm thấy danh sách đầy đủ các biến môi trường có sẵn [tại đây](https://docs.dify.ai/getting-started/install-self-hosted/environments).
Nếu bạn muốn cấu hình một cài đặt có độ sẵn sàng cao, có các [Helm Charts](https://helm.sh/) và tệp YAML do cộng đồng đóng góp cho phép Dify được triển khai trên Kubernetes.
@@ -162,7 +164,7 @@ Triển khai Dify lên AKS chỉ với một cú nhấp chuột bằng [Azure De
## Đóng góp
-Đối với những người muốn đóng góp mã, xem [Hướng dẫn Đóng góp](https://github.com/langgenius/dify/blob/main/CONTRIBUTING/CONTRIBUTING_VI.md) của chúng tôi.
+Đối với những người muốn đóng góp mã, xem [Hướng dẫn Đóng góp](./CONTRIBUTING.md) của chúng tôi.
Đồng thời, vui lòng xem xét hỗ trợ Dify bằng cách chia sẻ nó trên mạng xã hội và tại các sự kiện và hội nghị.
> Chúng tôi đang tìm kiếm người đóng góp để giúp dịch Dify sang các ngôn ngữ khác ngoài tiếng Trung hoặc tiếng Anh. Nếu bạn quan tâm đến việc giúp đỡ, vui lòng xem [README i18n](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) để biết thêm thông tin và để lại bình luận cho chúng tôi trong kênh `global-users` của [Máy chủ Cộng đồng Discord](https://discord.gg/8Tpq4AcN9c) của chúng tôi.
@@ -176,7 +178,7 @@ Triển khai Dify lên AKS chỉ với một cú nhấp chuột bằng [Azure De
## Cộng đồng & liên hệ
- [Thảo luận GitHub](https://github.com/langgenius/dify/discussions). Tốt nhất cho: chia sẻ phản hồi và đặt câu hỏi.
-- [Vấn đề GitHub](https://github.com/langgenius/dify/issues). Tốt nhất cho: lỗi bạn gặp phải khi sử dụng Dify.AI và đề xuất tính năng. Xem [Hướng dẫn Đóng góp](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) của chúng tôi.
+- [Vấn đề GitHub](https://github.com/langgenius/dify/issues). Tốt nhất cho: lỗi bạn gặp phải khi sử dụng Dify.AI và đề xuất tính năng. Xem [Hướng dẫn Đóng góp](./CONTRIBUTING.md) của chúng tôi.
- [Discord](https://discord.gg/FngNHpbcY7). Tốt nhất cho: chia sẻ ứng dụng của bạn và giao lưu với cộng đồng.
- [X(Twitter)](https://twitter.com/dify_ai). Tốt nhất cho: chia sẻ ứng dụng của bạn và giao lưu với cộng đồng.
@@ -190,4 +192,4 @@ Triển khai Dify lên AKS chỉ với một cú nhấp chuột bằng [Azure De
## Giấy phép
-Kho lưu trữ này có sẵn theo [Giấy phép Mã nguồn Mở Dify](../LICENSE), về cơ bản là Apache 2.0 với một vài hạn chế bổ sung.
+Kho lưu trữ này có sẵn theo [Giấy phép Mã nguồn Mở Dify](../../LICENSE), về cơ bản là Apache 2.0 với một vài hạn chế bổ sung.
diff --git a/CONTRIBUTING/CONTRIBUTING_CN.md b/docs/zh-CN/CONTRIBUTING.md
similarity index 96%
rename from CONTRIBUTING/CONTRIBUTING_CN.md
rename to docs/zh-CN/CONTRIBUTING.md
index 8c52d8939c..5b71467804 100644
--- a/CONTRIBUTING/CONTRIBUTING_CN.md
+++ b/docs/zh-CN/CONTRIBUTING.md
@@ -6,7 +6,7 @@
本指南和 Dify 一样在不断完善中。如果有任何滞后于项目实际情况的地方,恳请谅解,我们也欢迎任何改进建议。
-关于许可证,请花一分钟阅读我们简短的[许可和贡献者协议](../LICENSE)。同时也请遵循社区[行为准则](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)。
+关于许可证,请花一分钟阅读我们简短的[许可和贡献者协议](../../LICENSE)。同时也请遵循社区[行为准则](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)。
## 开始之前
diff --git a/README/README_CN.md b/docs/zh-CN/README.md
similarity index 79%
rename from README/README_CN.md
rename to docs/zh-CN/README.md
index 9501992bd2..202b99a6b1 100644
--- a/README/README_CN.md
+++ b/docs/zh-CN/README.md
@@ -1,4 +1,4 @@
-
+
Dify 云服务 ·
@@ -35,17 +35,19 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
#
@@ -111,7 +113,7 @@ Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI
### 快速启动
-启动 Dify 服务器的最简单方法是运行我们的 [docker-compose.yml](docker/docker-compose.yaml) 文件。在运行安装命令之前,请确保您的机器上安装了 [Docker](https://docs.docker.com/get-docker/) 和 [Docker Compose](https://docs.docker.com/compose/install/):
+启动 Dify 服务器的最简单方法是运行我们的 [docker-compose.yml](../../docker/docker-compose.yaml) 文件。在运行安装命令之前,请确保您的机器上安装了 [Docker](https://docs.docker.com/get-docker/) 和 [Docker Compose](https://docs.docker.com/compose/install/):
```bash
cd docker
@@ -123,7 +125,7 @@ docker compose up -d
### 自定义配置
-如果您需要自定义配置,请参考 [.env.example](docker/.env.example) 文件中的注释,并更新 `.env` 文件中对应的值。此外,您可能需要根据您的具体部署环境和需求对 `docker-compose.yaml` 文件本身进行调整,例如更改镜像版本、端口映射或卷挂载。完成任何更改后,请重新运行 `docker-compose up -d`。您可以在[此处](https://docs.dify.ai/getting-started/install-self-hosted/environments)找到可用环境变量的完整列表。
+如果您需要自定义配置,请参考 [.env.example](../../docker/.env.example) 文件中的注释,并更新 `.env` 文件中对应的值。此外,您可能需要根据您的具体部署环境和需求对 `docker-compose.yaml` 文件本身进行调整,例如更改镜像版本、端口映射或卷挂载。完成任何更改后,请重新运行 `docker-compose up -d`。您可以在[此处](https://docs.dify.ai/getting-started/install-self-hosted/environments)找到可用环境变量的完整列表。
#### 使用 Helm Chart 或 Kubernetes 资源清单(YAML)部署
@@ -180,7 +182,7 @@ docker compose up -d
## Contributing
-对于那些想要贡献代码的人,请参阅我们的[贡献指南](https://github.com/langgenius/dify/blob/main/CONTRIBUTING/CONTRIBUTING_CN.md)。
+对于那些想要贡献代码的人,请参阅我们的[贡献指南](./CONTRIBUTING.md)。
同时,请考虑通过社交媒体、活动和会议来支持 Dify 的分享。
> 我们正在寻找贡献者来帮助将 Dify 翻译成除了中文和英文之外的其他语言。如果您有兴趣帮助,请参阅我们的[i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md)获取更多信息,并在我们的[Discord 社区服务器](https://discord.gg/8Tpq4AcN9c)的`global-users`频道中留言。
@@ -196,7 +198,7 @@ docker compose up -d
我们欢迎您为 Dify 做出贡献,以帮助改善 Dify。包括:提交代码、问题、新想法,或分享您基于 Dify 创建的有趣且有用的 AI 应用程序。同时,我们也欢迎您在不同的活动、会议和社交媒体上分享 Dify。
- [GitHub Discussion](https://github.com/langgenius/dify/discussions). 👉:分享您的应用程序并与社区交流。
-- [GitHub Issues](https://github.com/langgenius/dify/issues)。👉:使用 Dify.AI 时遇到的错误和问题,请参阅[贡献指南](../CONTRIBUTING.md)。
+- [GitHub Issues](https://github.com/langgenius/dify/issues)。👉:使用 Dify.AI 时遇到的错误和问题,请参阅[贡献指南](./CONTRIBUTING.md)。
- [电子邮件支持](mailto:hello@dify.ai?subject=%5BGitHub%5DQuestions%20About%20Dify)。👉:关于使用 Dify.AI 的问题。
- [Discord](https://discord.gg/FngNHpbcY7)。👉:分享您的应用程序并与社区交流。
- [X(Twitter)](https://twitter.com/dify_ai)。👉:分享您的应用程序并与社区交流。
@@ -208,4 +210,4 @@ docker compose up -d
## License
-本仓库遵循 [Dify Open Source License](../LICENSE) 开源协议,该许可证本质上是 Apache 2.0,但有一些额外的限制。
+本仓库遵循 [Dify Open Source License](../../LICENSE) 开源协议,该许可证本质上是 Apache 2.0,但有一些额外的限制。
diff --git a/CONTRIBUTING/CONTRIBUTING_TW.md b/docs/zh-TW/CONTRIBUTING.md
similarity index 96%
rename from CONTRIBUTING/CONTRIBUTING_TW.md
rename to docs/zh-TW/CONTRIBUTING.md
index 7fba220a22..1d5f02efa1 100644
--- a/CONTRIBUTING/CONTRIBUTING_TW.md
+++ b/docs/zh-TW/CONTRIBUTING.md
@@ -6,7 +6,7 @@
這份指南與 Dify 一樣,都在持續完善中。如果指南內容有落後於實際專案的情況,還請見諒,也歡迎提供改進建議。
-關於授權部分,請花點時間閱讀我們簡短的[授權和貢獻者協議](../LICENSE)。社群也需遵守[行為準則](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)。
+關於授權部分,請花點時間閱讀我們簡短的[授權和貢獻者協議](../../LICENSE)。社群也需遵守[行為準則](https://github.com/langgenius/.github/blob/main/CODE_OF_CONDUCT.md)。
## 開始之前
diff --git a/README/README_TW.md b/docs/zh-TW/README.md
similarity index 80%
rename from README/README_TW.md
rename to docs/zh-TW/README.md
index b9c0b81246..526e8d9c8c 100644
--- a/README/README_TW.md
+++ b/docs/zh-TW/README.md
@@ -1,4 +1,4 @@
-
+
📌 介紹 Dify 工作流程檔案上傳功能:重現 Google NotebookLM Podcast
@@ -39,18 +39,18 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
Dify 是一個開源的 LLM 應用程式開發平台。其直觀的界面結合了智能代理工作流程、RAG 管道、代理功能、模型管理、可觀察性功能等,讓您能夠快速從原型進展到生產環境。
@@ -64,7 +64,7 @@ Dify 是一個開源的 LLM 應用程式開發平台。其直觀的界面結合
-啟動 Dify 伺服器最簡單的方式是透過 [docker compose](docker/docker-compose.yaml)。在使用以下命令運行 Dify 之前,請確保您的機器已安裝 [Docker](https://docs.docker.com/get-docker/) 和 [Docker Compose](https://docs.docker.com/compose/install/):
+啟動 Dify 伺服器最簡單的方式是透過 [docker compose](../../docker/docker-compose.yaml)。在使用以下命令運行 Dify 之前,請確保您的機器已安裝 [Docker](https://docs.docker.com/get-docker/) 和 [Docker Compose](https://docs.docker.com/compose/install/):
```bash
cd dify
@@ -128,7 +128,7 @@ Dify 的所有功能都提供相應的 API,因此您可以輕鬆地將 Dify
## 進階設定
-如果您需要自定義配置,請參考我們的 [.env.example](docker/.env.example) 文件中的註釋,並在您的 `.env` 文件中更新相應的值。此外,根據您特定的部署環境和需求,您可能需要調整 `docker-compose.yaml` 文件本身,例如更改映像版本、端口映射或卷掛載。進行任何更改後,請重新運行 `docker-compose up -d`。您可以在[這裡](https://docs.dify.ai/getting-started/install-self-hosted/environments)找到可用環境變數的完整列表。
+如果您需要自定義配置,請參考我們的 [.env.example](../../docker/.env.example) 文件中的註釋,並在您的 `.env` 文件中更新相應的值。此外,根據您特定的部署環境和需求,您可能需要調整 `docker-compose.yaml` 文件本身,例如更改映像版本、端口映射或卷掛載。進行任何更改後,請重新運行 `docker-compose up -d`。您可以在[這裡](https://docs.dify.ai/getting-started/install-self-hosted/environments)找到可用環境變數的完整列表。
如果您想配置高可用性設置,社區貢獻的 [Helm Charts](https://helm.sh/) 和 Kubernetes 資源清單(YAML)允許在 Kubernetes 上部署 Dify。
@@ -173,7 +173,7 @@ Dify 的所有功能都提供相應的 API,因此您可以輕鬆地將 Dify
## 貢獻
-對於想要貢獻程式碼的開發者,請參閱我們的[貢獻指南](https://github.com/langgenius/dify/blob/main/CONTRIBUTING/CONTRIBUTING_TW.md)。
+對於想要貢獻程式碼的開發者,請參閱我們的[貢獻指南](./CONTRIBUTING.md)。
同時,也請考慮透過在社群媒體和各種活動與會議上分享 Dify 來支持我們。
> 我們正在尋找貢獻者協助將 Dify 翻譯成中文和英文以外的語言。如果您有興趣幫忙,請查看 [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n-config/README.md) 獲取更多資訊,並在我們的 [Discord 社群伺服器](https://discord.gg/8Tpq4AcN9c) 的 `global-users` 頻道留言給我們。
@@ -181,7 +181,7 @@ Dify 的所有功能都提供相應的 API,因此您可以輕鬆地將 Dify
## 社群與聯絡方式
- [GitHub Discussion](https://github.com/langgenius/dify/discussions):最適合分享反饋和提問。
-- [GitHub Issues](https://github.com/langgenius/dify/issues):最適合報告使用 Dify.AI 時遇到的問題和提出功能建議。請參閱我們的[貢獻指南](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md)。
+- [GitHub Issues](https://github.com/langgenius/dify/issues):最適合報告使用 Dify.AI 時遇到的問題和提出功能建議。請參閱我們的[貢獻指南](./CONTRIBUTING.md)。
- [Discord](https://discord.gg/FngNHpbcY7):最適合分享您的應用程式並與社群互動。
- [X(Twitter)](https://twitter.com/dify_ai):最適合分享您的應用程式並與社群互動。
@@ -201,4 +201,4 @@ Dify 的所有功能都提供相應的 API,因此您可以輕鬆地將 Dify
## 授權條款
-本代碼庫採用 [Dify 開源授權](../LICENSE),這基本上是 Apache 2.0 授權加上一些額外限制條款。
+本代碼庫採用 [Dify 開源授權](../../LICENSE),這基本上是 Apache 2.0 授權加上一些額外限制條款。
diff --git a/scripts/stress-test/setup/import_workflow_app.py b/scripts/stress-test/setup/import_workflow_app.py
index 86d0239e35..41a76bd29b 100755
--- a/scripts/stress-test/setup/import_workflow_app.py
+++ b/scripts/stress-test/setup/import_workflow_app.py
@@ -8,7 +8,7 @@ sys.path.append(str(Path(__file__).parent.parent))
import json
import httpx
-from common import Logger, config_helper
+from common import Logger, config_helper # type: ignore[import]
def import_workflow_app() -> None:
diff --git a/web/__tests__/goto-anything/command-selector.test.tsx b/web/__tests__/goto-anything/command-selector.test.tsx
index 1db4be31fb..6d4e045d49 100644
--- a/web/__tests__/goto-anything/command-selector.test.tsx
+++ b/web/__tests__/goto-anything/command-selector.test.tsx
@@ -16,7 +16,7 @@ jest.mock('cmdk', () => ({
Item: ({ children, onSelect, value, className }: any) => (
onSelect && onSelect()}
+ onClick={() => onSelect?.()}
data-value={value}
data-testid={`command-item-${value}`}
>
diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx
index 1ab40e31bf..246a1eb6a3 100644
--- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx
+++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx
@@ -4,6 +4,7 @@ import React, { useCallback, useRef, useState } from 'react'
import type { PopupProps } from './config-popup'
import ConfigPopup from './config-popup'
+import cn from '@/utils/classnames'
import {
PortalToFollowElem,
PortalToFollowElemContent,
@@ -45,7 +46,7 @@ const ConfigBtn: FC
= ({
offset={12}
>
-
+
{children}
diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx
index dfc8d10087..b98eb815f9 100644
--- a/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx
+++ b/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx
@@ -28,7 +28,8 @@ const CSVUploader: FC
= ({
const handleDragEnter = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
- e.target !== dragRef.current && setDragging(true)
+ if (e.target !== dragRef.current)
+ setDragging(true)
}
const handleDragOver = (e: DragEvent) => {
e.preventDefault()
@@ -37,7 +38,8 @@ const CSVUploader: FC = ({
const handleDragLeave = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
- e.target === dragRef.current && setDragging(false)
+ if (e.target === dragRef.current)
+ setDragging(false)
}
const handleDrop = (e: DragEvent) => {
e.preventDefault()
diff --git a/web/app/components/app/app-publisher/index.tsx b/web/app/components/app/app-publisher/index.tsx
index 53cceb8020..df2618b49c 100644
--- a/web/app/components/app/app-publisher/index.tsx
+++ b/web/app/components/app/app-publisher/index.tsx
@@ -348,7 +348,8 @@ const AppPublisher = ({
{
- publishedAt && handleOpenInExplore()
+ if (publishedAt)
+ handleOpenInExplore()
}}
disabled={!publishedAt || (systemFeatures.webapp_auth.enabled && !userCanAccessApp?.result)}
icon={ }
diff --git a/web/app/components/app/app-publisher/version-info-modal.tsx b/web/app/components/app/app-publisher/version-info-modal.tsx
index 4d5d3705c1..263f187736 100644
--- a/web/app/components/app/app-publisher/version-info-modal.tsx
+++ b/web/app/components/app/app-publisher/version-info-modal.tsx
@@ -40,7 +40,8 @@ const VersionInfoModal: FC = ({
return
}
else {
- titleError && setTitleError(false)
+ if (titleError)
+ setTitleError(false)
}
if (releaseNotes.length > RELEASE_NOTES_MAX_LENGTH) {
@@ -52,7 +53,8 @@ const VersionInfoModal: FC = ({
return
}
else {
- releaseNotesError && setReleaseNotesError(false)
+ if (releaseNotesError)
+ setReleaseNotesError(false)
}
onPublish({ title, releaseNotes, id: versionInfo?.id })
diff --git a/web/app/components/app/configuration/base/icons/citation.tsx b/web/app/components/app/configuration/base/icons/citation.tsx
index e69de29bb2..3aa6b0f0e1 100644
--- a/web/app/components/app/configuration/base/icons/citation.tsx
+++ b/web/app/components/app/configuration/base/icons/citation.tsx
@@ -0,0 +1,29 @@
+import type { SVGProps } from 'react'
+
+const CitationIcon = (props: SVGProps) => (
+
+
+
+
+)
+
+export default CitationIcon
diff --git a/web/app/components/app/configuration/config-var/config-modal/index.tsx b/web/app/components/app/configuration/config-var/config-modal/index.tsx
index b0f0ea8779..8a02ca8caa 100644
--- a/web/app/components/app/configuration/config-var/config-modal/index.tsx
+++ b/web/app/components/app/configuration/config-var/config-modal/index.tsx
@@ -32,6 +32,19 @@ import { TransferMethod } from '@/types/app'
import type { FileEntity } from '@/app/components/base/file-uploader/types'
const TEXT_MAX_LENGTH = 256
+const CHECKBOX_DEFAULT_TRUE_VALUE = 'true'
+const CHECKBOX_DEFAULT_FALSE_VALUE = 'false'
+
+const getCheckboxDefaultSelectValue = (value: InputVar['default']) => {
+ if (typeof value === 'boolean')
+ return value ? CHECKBOX_DEFAULT_TRUE_VALUE : CHECKBOX_DEFAULT_FALSE_VALUE
+ if (typeof value === 'string')
+ return value.toLowerCase() === CHECKBOX_DEFAULT_TRUE_VALUE ? CHECKBOX_DEFAULT_TRUE_VALUE : CHECKBOX_DEFAULT_FALSE_VALUE
+ return CHECKBOX_DEFAULT_FALSE_VALUE
+}
+
+const parseCheckboxSelectValue = (value: string) =>
+ value === CHECKBOX_DEFAULT_TRUE_VALUE
export type IConfigModalProps = {
isCreate?: boolean
@@ -66,7 +79,7 @@ const ConfigModal: FC = ({
try {
return JSON.stringify(JSON.parse(tempPayload.json_schema).properties, null, 2)
}
- catch (_e) {
+ catch {
return ''
}
}, [tempPayload.json_schema])
@@ -110,7 +123,7 @@ const ConfigModal: FC = ({
}
handlePayloadChange('json_schema')(JSON.stringify(res, null, 2))
}
- catch (_e) {
+ catch {
return null
}
}, [handlePayloadChange])
@@ -198,6 +211,8 @@ const ConfigModal: FC = ({
handlePayloadChange('variable')(e.target.value)
}, [handlePayloadChange, t])
+ const checkboxDefaultSelectValue = useMemo(() => getCheckboxDefaultSelectValue(tempPayload.default), [tempPayload.default])
+
const handleConfirm = () => {
const moreInfo = tempPayload.variable === payload?.variable
? undefined
@@ -324,6 +339,23 @@ const ConfigModal: FC = ({
)}
+ {type === InputVarType.checkbox && (
+
+ handlePayloadChange('default')(parseCheckboxSelectValue(String(item.value)))}
+ placeholder={t('appDebug.variableConfig.selectDefaultValue')}
+ allowSearch={false}
+ />
+
+ )}
+
{type === InputVarType.select && (
<>
diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx
index f1f81ebf97..20229c9717 100644
--- a/web/app/components/app/configuration/index.tsx
+++ b/web/app/components/app/configuration/index.tsx
@@ -480,7 +480,7 @@ const Configuration: FC = () => {
Toast.notify({ type: 'warning', message: `${t('common.modelProvider.parametersInvalidRemoved')}: ${Object.entries(removedDetails).map(([k, reason]) => `${k} (${reason})`).join(', ')}` })
setCompletionParams(filtered)
}
- catch (e) {
+ catch {
Toast.notify({ type: 'error', message: t('common.error') })
setCompletionParams({})
}
diff --git a/web/app/components/app/configuration/prompt-value-panel/index.tsx b/web/app/components/app/configuration/prompt-value-panel/index.tsx
index e88268ba40..43c836132f 100644
--- a/web/app/components/app/configuration/prompt-value-panel/index.tsx
+++ b/web/app/components/app/configuration/prompt-value-panel/index.tsx
@@ -192,7 +192,7 @@ const PromptValuePanel: FC = ({
onSend && onSend()}
+ onClick={() => onSend?.()}
className="w-[96px]">
{t('appDebug.inputs.run')}
@@ -203,7 +203,7 @@ const PromptValuePanel: FC = ({
onSend && onSend()}
+ onClick={() => onSend?.()}
className="w-[96px]">
{t('appDebug.inputs.run')}
diff --git a/web/app/components/app/create-from-dsl-modal/uploader.tsx b/web/app/components/app/create-from-dsl-modal/uploader.tsx
index 654c7b5952..b6644da5a4 100644
--- a/web/app/components/app/create-from-dsl-modal/uploader.tsx
+++ b/web/app/components/app/create-from-dsl-modal/uploader.tsx
@@ -38,7 +38,8 @@ const Uploader: FC = ({
const handleDragEnter = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
- e.target !== dragRef.current && setDragging(true)
+ if (e.target !== dragRef.current)
+ setDragging(true)
}
const handleDragOver = (e: DragEvent) => {
e.preventDefault()
@@ -47,7 +48,8 @@ const Uploader: FC = ({
const handleDragLeave = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
- e.target === dragRef.current && setDragging(false)
+ if (e.target === dragRef.current)
+ setDragging(false)
}
const handleDrop = (e: DragEvent) => {
e.preventDefault()
diff --git a/web/app/components/app/overview/app-chart.tsx b/web/app/components/app/overview/app-chart.tsx
index 9d9b27f230..c550f0b23f 100644
--- a/web/app/components/app/overview/app-chart.tsx
+++ b/web/app/components/app/overview/app-chart.tsx
@@ -107,7 +107,8 @@ const Chart: React.FC = ({
const { t } = useTranslation()
const statistics = chartData.data
const statisticsLen = statistics.length
- const extraDataForMarkLine = new Array(statisticsLen >= 2 ? statisticsLen - 2 : statisticsLen).fill('1')
+ const markLineLength = statisticsLen >= 2 ? statisticsLen - 2 : statisticsLen
+ const extraDataForMarkLine = Array.from({ length: markLineLength }, () => '1')
extraDataForMarkLine.push('')
extraDataForMarkLine.unshift('')
diff --git a/web/app/components/base/audio-btn/audio.ts b/web/app/components/base/audio-btn/audio.ts
index 00797d04e4..b06458cfa8 100644
--- a/web/app/components/base/audio-btn/audio.ts
+++ b/web/app/components/base/audio-btn/audio.ts
@@ -127,7 +127,7 @@ export default class AudioPlayer {
}
catch {
this.isLoadData = false
- this.callback && this.callback('error')
+ this.callback?.('error')
}
}
@@ -137,15 +137,14 @@ export default class AudioPlayer {
if (this.audioContext.state === 'suspended') {
this.audioContext.resume().then((_) => {
this.audio.play()
- this.callback && this.callback('play')
+ this.callback?.('play')
})
}
else if (this.audio.ended) {
this.audio.play()
- this.callback && this.callback('play')
+ this.callback?.('play')
}
- if (this.callback)
- this.callback('play')
+ this.callback?.('play')
}
else {
this.isLoadData = true
@@ -189,24 +188,24 @@ export default class AudioPlayer {
if (this.audio.paused) {
this.audioContext.resume().then((_) => {
this.audio.play()
- this.callback && this.callback('play')
+ this.callback?.('play')
})
}
else if (this.audio.ended) {
this.audio.play()
- this.callback && this.callback('play')
+ this.callback?.('play')
}
else if (this.audio.played) { /* empty */ }
else {
this.audio.play()
- this.callback && this.callback('play')
+ this.callback?.('play')
}
}
}
public pauseAudio() {
- this.callback && this.callback('paused')
+ this.callback?.('paused')
this.audio.pause()
this.audioContext.suspend()
}
diff --git a/web/app/components/base/chat/chat-with-history/hooks.tsx b/web/app/components/base/chat/chat-with-history/hooks.tsx
index fb3e1bb8f3..c17ab26dfe 100644
--- a/web/app/components/base/chat/chat-with-history/hooks.tsx
+++ b/web/app/components/base/chat/chat-with-history/hooks.tsx
@@ -128,7 +128,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
const localState = localStorage.getItem('webappSidebarCollapse')
return localState === 'collapsed'
}
- catch (e) {
+ catch {
// localStorage may be disabled in private browsing mode or by security settings
// fallback to default value
return false
@@ -142,7 +142,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
try {
localStorage.setItem('webappSidebarCollapse', state ? 'collapsed' : 'expanded')
}
- catch (e) {
+ catch {
// localStorage may be disabled, continue without persisting state
}
}
@@ -235,13 +235,15 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
}
}
- if(item.checkbox) {
+ if (item.checkbox) {
+ const preset = initInputs[item.checkbox.variable] === true
return {
...item.checkbox,
- default: false,
+ default: preset || item.default || item.checkbox.default,
type: 'checkbox',
}
}
+
if (item.select) {
const isInputInOptions = item.select.options.includes(initInputs[item.select.variable])
return {
diff --git a/web/app/components/base/chat/chat/answer/index.tsx b/web/app/components/base/chat/chat/answer/index.tsx
index 993a3a5519..51eb00cfc5 100644
--- a/web/app/components/base/chat/chat/answer/index.tsx
+++ b/web/app/components/base/chat/chat/answer/index.tsx
@@ -101,10 +101,14 @@ const Answer: FC = ({
}, [])
const handleSwitchSibling = useCallback((direction: 'prev' | 'next') => {
- if (direction === 'prev')
- item.prevSibling && switchSibling?.(item.prevSibling)
- else
- item.nextSibling && switchSibling?.(item.nextSibling)
+ if (direction === 'prev') {
+ if (item.prevSibling)
+ switchSibling?.(item.prevSibling)
+ }
+ else {
+ if (item.nextSibling)
+ switchSibling?.(item.nextSibling)
+ }
}, [switchSibling, item.prevSibling, item.nextSibling])
return (
diff --git a/web/app/components/base/chat/chat/question.tsx b/web/app/components/base/chat/chat/question.tsx
index 6630d9bb9d..21b604b969 100644
--- a/web/app/components/base/chat/chat/question.tsx
+++ b/web/app/components/base/chat/chat/question.tsx
@@ -73,10 +73,14 @@ const Question: FC = ({
}, [content])
const handleSwitchSibling = useCallback((direction: 'prev' | 'next') => {
- if (direction === 'prev')
- item.prevSibling && switchSibling?.(item.prevSibling)
- else
- item.nextSibling && switchSibling?.(item.nextSibling)
+ if (direction === 'prev') {
+ if (item.prevSibling)
+ switchSibling?.(item.prevSibling)
+ }
+ else {
+ if (item.nextSibling)
+ switchSibling?.(item.nextSibling)
+ }
}, [switchSibling, item.prevSibling, item.nextSibling])
const getContentWidth = () => {
diff --git a/web/app/components/base/chat/embedded-chatbot/hooks.tsx b/web/app/components/base/chat/embedded-chatbot/hooks.tsx
index 14a32860b9..aa7006db25 100644
--- a/web/app/components/base/chat/embedded-chatbot/hooks.tsx
+++ b/web/app/components/base/chat/embedded-chatbot/hooks.tsx
@@ -195,13 +195,16 @@ export const useEmbeddedChatbot = () => {
type: 'number',
}
}
+
if (item.checkbox) {
+ const preset = initInputs[item.checkbox.variable] === true
return {
...item.checkbox,
- default: false,
+ default: preset || item.default || item.checkbox.default,
type: 'checkbox',
}
}
+
if (item.select) {
const isInputInOptions = item.select.options.includes(initInputs[item.select.variable])
return {
diff --git a/web/app/components/base/date-and-time-picker/time-picker/index.spec.tsx b/web/app/components/base/date-and-time-picker/time-picker/index.spec.tsx
new file mode 100644
index 0000000000..40bc2928c8
--- /dev/null
+++ b/web/app/components/base/date-and-time-picker/time-picker/index.spec.tsx
@@ -0,0 +1,95 @@
+import React from 'react'
+import { fireEvent, render, screen } from '@testing-library/react'
+import TimePicker from './index'
+import dayjs from '../utils/dayjs'
+import { isDayjsObject } from '../utils/dayjs'
+
+jest.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (key: string) => {
+ if (key === 'time.defaultPlaceholder') return 'Pick a time...'
+ if (key === 'time.operation.now') return 'Now'
+ if (key === 'time.operation.ok') return 'OK'
+ if (key === 'common.operation.clear') return 'Clear'
+ return key
+ },
+ }),
+}))
+
+jest.mock('@/app/components/base/portal-to-follow-elem', () => ({
+ PortalToFollowElem: ({ children }: { children: React.ReactNode }) => {children}
,
+ PortalToFollowElemTrigger: ({ children, onClick }: { children: React.ReactNode, onClick: (e: React.MouseEvent) => void }) => (
+ {children}
+ ),
+ PortalToFollowElemContent: ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
+}))
+
+jest.mock('./options', () => () =>
)
+jest.mock('./header', () => () =>
)
+
+describe('TimePicker', () => {
+ const baseProps = {
+ onChange: jest.fn(),
+ onClear: jest.fn(),
+ }
+
+ beforeEach(() => {
+ jest.clearAllMocks()
+ })
+
+ test('renders formatted value for string input (Issue #26692 regression)', () => {
+ render(
+ ,
+ )
+
+ expect(screen.getByDisplayValue('06:45 PM')).toBeInTheDocument()
+ })
+
+ test('confirms cleared value when confirming without selection', () => {
+ render(
+ ,
+ )
+
+ const input = screen.getByRole('textbox')
+ fireEvent.click(input)
+
+ const clearButton = screen.getByRole('button', { name: /clear/i })
+ fireEvent.click(clearButton)
+
+ const confirmButton = screen.getByRole('button', { name: 'OK' })
+ fireEvent.click(confirmButton)
+
+ expect(baseProps.onChange).toHaveBeenCalledTimes(1)
+ expect(baseProps.onChange).toHaveBeenCalledWith(undefined)
+ expect(baseProps.onClear).not.toHaveBeenCalled()
+ })
+
+ test('selecting current time emits timezone-aware value', () => {
+ const onChange = jest.fn()
+ render(
+ ,
+ )
+
+ const nowButton = screen.getByRole('button', { name: 'Now' })
+ fireEvent.click(nowButton)
+
+ expect(onChange).toHaveBeenCalledTimes(1)
+ const emitted = onChange.mock.calls[0][0]
+ expect(isDayjsObject(emitted)).toBe(true)
+ expect(emitted?.utcOffset()).toBe(dayjs().tz('America/New_York').utcOffset())
+ })
+})
diff --git a/web/app/components/base/date-and-time-picker/time-picker/index.tsx b/web/app/components/base/date-and-time-picker/time-picker/index.tsx
index 1fb2cfed11..f23fcf8f4e 100644
--- a/web/app/components/base/date-and-time-picker/time-picker/index.tsx
+++ b/web/app/components/base/date-and-time-picker/time-picker/index.tsx
@@ -1,6 +1,13 @@
import React, { useCallback, useEffect, useRef, useState } from 'react'
-import type { Period, TimePickerProps } from '../types'
-import dayjs, { cloneTime, getDateWithTimezone, getHourIn12Hour } from '../utils/dayjs'
+import type { Dayjs } from 'dayjs'
+import { Period } from '../types'
+import type { TimePickerProps } from '../types'
+import dayjs, {
+ getDateWithTimezone,
+ getHourIn12Hour,
+ isDayjsObject,
+ toDayjs,
+} from '../utils/dayjs'
import {
PortalToFollowElem,
PortalToFollowElemContent,
@@ -13,6 +20,11 @@ import { useTranslation } from 'react-i18next'
import { RiCloseCircleFill, RiTimeLine } from '@remixicon/react'
import cn from '@/utils/classnames'
+const to24Hour = (hour12: string, period: Period) => {
+ const normalized = Number.parseInt(hour12, 10) % 12
+ return period === Period.PM ? normalized + 12 : normalized
+}
+
const TimePicker = ({
value,
timezone,
@@ -28,7 +40,11 @@ const TimePicker = ({
const [isOpen, setIsOpen] = useState(false)
const containerRef = useRef(null)
const isInitial = useRef(true)
- const [selectedTime, setSelectedTime] = useState(() => value ? getDateWithTimezone({ timezone, date: value }) : undefined)
+
+ // Initialize selectedTime
+ const [selectedTime, setSelectedTime] = useState(() => {
+ return toDayjs(value, { timezone })
+ })
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
@@ -39,20 +55,47 @@ const TimePicker = ({
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [])
+ // Track previous values to avoid unnecessary updates
+ const prevValueRef = useRef(value)
+ const prevTimezoneRef = useRef(timezone)
+
useEffect(() => {
if (isInitial.current) {
isInitial.current = false
+ // Save initial values on first render
+ prevValueRef.current = value
+ prevTimezoneRef.current = timezone
return
}
- if (value) {
- const newValue = getDateWithTimezone({ date: value, timezone })
- setSelectedTime(newValue)
- onChange(newValue)
+
+ // Only update when timezone changes but value doesn't
+ const valueChanged = prevValueRef.current !== value
+ const timezoneChanged = prevTimezoneRef.current !== timezone
+
+ // Update reference values
+ prevValueRef.current = value
+ prevTimezoneRef.current = timezone
+
+ // Skip if neither timezone changed nor value changed
+ if (!timezoneChanged && !valueChanged) return
+
+ if (value !== undefined && value !== null) {
+ const dayjsValue = toDayjs(value, { timezone })
+ if (!dayjsValue) return
+
+ setSelectedTime(dayjsValue)
+
+ if (timezoneChanged && !valueChanged)
+ onChange(dayjsValue)
+ return
}
- else {
- setSelectedTime(prev => prev ? getDateWithTimezone({ date: prev, timezone }) : undefined)
- }
- }, [timezone])
+
+ setSelectedTime((prev) => {
+ if (!isDayjsObject(prev))
+ return undefined
+ return timezone ? getDateWithTimezone({ date: prev, timezone }) : prev
+ })
+ }, [timezone, value, onChange])
const handleClickTrigger = (e: React.MouseEvent) => {
e.stopPropagation()
@@ -61,8 +104,16 @@ const TimePicker = ({
return
}
setIsOpen(true)
- if (value)
- setSelectedTime(value)
+
+ if (value) {
+ const dayjsValue = toDayjs(value, { timezone })
+ const needsUpdate = dayjsValue && (
+ !selectedTime
+ || !isDayjsObject(selectedTime)
+ || !dayjsValue.isSame(selectedTime, 'minute')
+ )
+ if (needsUpdate) setSelectedTime(dayjsValue)
+ }
}
const handleClear = (e: React.MouseEvent) => {
@@ -73,42 +124,68 @@ const TimePicker = ({
}
const handleTimeSelect = (hour: string, minute: string, period: Period) => {
- const newTime = cloneTime(dayjs(), dayjs(`1/1/2000 ${hour}:${minute} ${period}`))
+ const periodAdjustedHour = to24Hour(hour, period)
+ const nextMinute = Number.parseInt(minute, 10)
setSelectedTime((prev) => {
- return prev ? cloneTime(prev, newTime) : newTime
+ const reference = isDayjsObject(prev)
+ ? prev
+ : (timezone ? getDateWithTimezone({ timezone }) : dayjs()).startOf('minute')
+ return reference
+ .set('hour', periodAdjustedHour)
+ .set('minute', nextMinute)
+ .set('second', 0)
+ .set('millisecond', 0)
})
}
+ const getSafeTimeObject = useCallback(() => {
+ if (isDayjsObject(selectedTime))
+ return selectedTime
+ return (timezone ? getDateWithTimezone({ timezone }) : dayjs()).startOf('day')
+ }, [selectedTime, timezone])
+
const handleSelectHour = useCallback((hour: string) => {
- const time = selectedTime || dayjs().startOf('day')
+ const time = getSafeTimeObject()
handleTimeSelect(hour, time.minute().toString().padStart(2, '0'), time.format('A') as Period)
- }, [selectedTime])
+ }, [getSafeTimeObject])
const handleSelectMinute = useCallback((minute: string) => {
- const time = selectedTime || dayjs().startOf('day')
+ const time = getSafeTimeObject()
handleTimeSelect(getHourIn12Hour(time).toString().padStart(2, '0'), minute, time.format('A') as Period)
- }, [selectedTime])
+ }, [getSafeTimeObject])
const handleSelectPeriod = useCallback((period: Period) => {
- const time = selectedTime || dayjs().startOf('day')
+ const time = getSafeTimeObject()
handleTimeSelect(getHourIn12Hour(time).toString().padStart(2, '0'), time.minute().toString().padStart(2, '0'), period)
- }, [selectedTime])
+ }, [getSafeTimeObject])
const handleSelectCurrentTime = useCallback(() => {
const newDate = getDateWithTimezone({ timezone })
setSelectedTime(newDate)
onChange(newDate)
setIsOpen(false)
- }, [onChange, timezone])
+ }, [timezone, onChange])
const handleConfirm = useCallback(() => {
- onChange(selectedTime)
+ const valueToEmit = isDayjsObject(selectedTime) ? selectedTime : undefined
+ onChange(valueToEmit)
setIsOpen(false)
- }, [onChange, selectedTime])
+ }, [selectedTime, onChange])
const timeFormat = 'hh:mm A'
- const displayValue = value?.format(timeFormat) || ''
- const placeholderDate = isOpen && selectedTime ? selectedTime.format(timeFormat) : (placeholder || t('time.defaultPlaceholder'))
+
+ const formatTimeValue = useCallback((timeValue: string | Dayjs | undefined): string => {
+ if (!timeValue) return ''
+
+ const dayjsValue = toDayjs(timeValue, { timezone })
+ return dayjsValue?.format(timeFormat) || ''
+ }, [timezone])
+
+ const displayValue = formatTimeValue(value)
+
+ const placeholderDate = isOpen && isDayjsObject(selectedTime)
+ ? selectedTime.format(timeFormat)
+ : (placeholder || t('time.defaultPlaceholder'))
const inputElem = (
diff --git a/web/app/components/base/date-and-time-picker/types.ts b/web/app/components/base/date-and-time-picker/types.ts
index 4ac01c142a..b51c2ebb01 100644
--- a/web/app/components/base/date-and-time-picker/types.ts
+++ b/web/app/components/base/date-and-time-picker/types.ts
@@ -54,7 +54,7 @@ export type TriggerParams = {
onClick: (e: React.MouseEvent) => void
}
export type TimePickerProps = {
- value: Dayjs | undefined
+ value: Dayjs | string | undefined
timezone?: string
placeholder?: string
onChange: (date: Dayjs | undefined) => void
diff --git a/web/app/components/base/date-and-time-picker/utils/dayjs.spec.ts b/web/app/components/base/date-and-time-picker/utils/dayjs.spec.ts
new file mode 100644
index 0000000000..549ab01029
--- /dev/null
+++ b/web/app/components/base/date-and-time-picker/utils/dayjs.spec.ts
@@ -0,0 +1,67 @@
+import dayjs from './dayjs'
+import {
+ getDateWithTimezone,
+ isDayjsObject,
+ toDayjs,
+} from './dayjs'
+
+describe('dayjs utilities', () => {
+ const timezone = 'UTC'
+
+ test('toDayjs parses time-only strings with timezone support', () => {
+ const result = toDayjs('18:45', { timezone })
+ expect(result).toBeDefined()
+ expect(result?.format('HH:mm')).toBe('18:45')
+ expect(result?.utcOffset()).toBe(getDateWithTimezone({ timezone }).utcOffset())
+ })
+
+ test('toDayjs parses 12-hour time strings', () => {
+ const tz = 'America/New_York'
+ const result = toDayjs('07:15 PM', { timezone: tz })
+ expect(result).toBeDefined()
+ expect(result?.format('HH:mm')).toBe('19:15')
+ expect(result?.utcOffset()).toBe(getDateWithTimezone({ timezone: tz }).utcOffset())
+ })
+
+ test('isDayjsObject detects dayjs instances', () => {
+ const date = dayjs()
+ expect(isDayjsObject(date)).toBe(true)
+ expect(isDayjsObject(getDateWithTimezone({ timezone }))).toBe(true)
+ expect(isDayjsObject('2024-01-01')).toBe(false)
+ expect(isDayjsObject({})).toBe(false)
+ })
+
+ test('toDayjs parses datetime strings in target timezone', () => {
+ const value = '2024-05-01 12:00:00'
+ const tz = 'America/New_York'
+
+ const result = toDayjs(value, { timezone: tz })
+
+ expect(result).toBeDefined()
+ expect(result?.hour()).toBe(12)
+ expect(result?.format('YYYY-MM-DD HH:mm')).toBe('2024-05-01 12:00')
+ })
+
+ test('toDayjs parses ISO datetime strings in target timezone', () => {
+ const value = '2024-05-01T14:30:00'
+ const tz = 'Europe/London'
+
+ const result = toDayjs(value, { timezone: tz })
+
+ expect(result).toBeDefined()
+ expect(result?.hour()).toBe(14)
+ expect(result?.minute()).toBe(30)
+ })
+
+ test('toDayjs handles dates without time component', () => {
+ const value = '2024-05-01'
+ const tz = 'America/Los_Angeles'
+
+ const result = toDayjs(value, { timezone: tz })
+
+ expect(result).toBeDefined()
+ expect(result?.format('YYYY-MM-DD')).toBe('2024-05-01')
+ expect(result?.hour()).toBe(0)
+ expect(result?.minute()).toBe(0)
+ })
+})
diff --git a/web/app/components/base/date-and-time-picker/utils/dayjs.ts b/web/app/components/base/date-and-time-picker/utils/dayjs.ts
index 27200e76e9..808b50247a 100644
--- a/web/app/components/base/date-and-time-picker/utils/dayjs.ts
+++ b/web/app/components/base/date-and-time-picker/utils/dayjs.ts
@@ -10,6 +10,25 @@ dayjs.extend(timezone)
export default dayjs
const monthMaps: Record = {}
+const DEFAULT_OFFSET_STR = 'UTC+0'
+const TIME_ONLY_REGEX = /^(\d{1,2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?$/
+const TIME_ONLY_12H_REGEX = /^(\d{1,2}):(\d{2})(?::(\d{2}))?\s?(AM|PM)$/i
+
+const COMMON_PARSE_FORMATS = [
+ 'YYYY-MM-DD',
+ 'YYYY/MM/DD',
+ 'DD-MM-YYYY',
+ 'DD/MM/YYYY',
+ 'MM-DD-YYYY',
+ 'MM/DD/YYYY',
+ 'YYYY-MM-DDTHH:mm:ss.SSSZ',
+ 'YYYY-MM-DDTHH:mm:ssZ',
+ 'YYYY-MM-DD HH:mm:ss',
+ 'YYYY-MM-DDTHH:mm',
+ 'YYYY-MM-DDTHH:mmZ',
+ 'YYYY-MM-DDTHH:mm:ss',
+ 'YYYY-MM-DDTHH:mm:ss.SSS',
+]
export const cloneTime = (targetDate: Dayjs, sourceDate: Dayjs) => {
return targetDate.clone()
@@ -76,21 +95,116 @@ export const getHourIn12Hour = (date: Dayjs) => {
return hour === 0 ? 12 : hour >= 12 ? hour - 12 : hour
}
-export const getDateWithTimezone = (props: { date?: Dayjs, timezone?: string }) => {
- return props.date ? dayjs.tz(props.date, props.timezone) : dayjs().tz(props.timezone)
+export const getDateWithTimezone = ({ date, timezone }: { date?: Dayjs, timezone?: string }) => {
+ if (!timezone)
+ return (date ?? dayjs()).clone()
+ return date ? dayjs.tz(date, timezone) : dayjs().tz(timezone)
}
-// Asia/Shanghai -> UTC+8
-const DEFAULT_OFFSET_STR = 'UTC+0'
export const convertTimezoneToOffsetStr = (timezone?: string) => {
if (!timezone)
return DEFAULT_OFFSET_STR
const tzItem = tz.find(item => item.value === timezone)
- if(!tzItem)
+ if (!tzItem)
return DEFAULT_OFFSET_STR
return `UTC${tzItem.name.charAt(0)}${tzItem.name.charAt(2)}`
}
+export const isDayjsObject = (value: unknown): value is Dayjs => dayjs.isDayjs(value)
+
+export type ToDayjsOptions = {
+ timezone?: string
+ format?: string
+ formats?: string[]
+}
+
+const warnParseFailure = (value: string) => {
+ if (process.env.NODE_ENV !== 'production')
+ console.warn('[TimePicker] Failed to parse time value', value)
+}
+
+const normalizeMillisecond = (value: string | undefined) => {
+ if (!value) return 0
+ if (value.length === 3) return Number(value)
+ if (value.length > 3) return Number(value.slice(0, 3))
+ return Number(value.padEnd(3, '0'))
+}
+
+const applyTimezone = (date: Dayjs, timezone?: string) => {
+ return timezone ? getDateWithTimezone({ date, timezone }) : date
+}
+
+export const toDayjs = (value: string | Dayjs | undefined, options: ToDayjsOptions = {}): Dayjs | undefined => {
+ if (!value)
+ return undefined
+
+ const { timezone: tzName, format, formats } = options
+
+ if (isDayjsObject(value))
+ return applyTimezone(value, tzName)
+
+ if (typeof value !== 'string')
+ return undefined
+
+ const trimmed = value.trim()
+
+ if (format) {
+ const parsedWithFormat = tzName
+ ? dayjs.tz(trimmed, format, tzName, true)
+ : dayjs(trimmed, format, true)
+ if (parsedWithFormat.isValid())
+ return parsedWithFormat
+ }
+
+ const timeMatch = TIME_ONLY_REGEX.exec(trimmed)
+ if (timeMatch) {
+ const base = applyTimezone(dayjs(), tzName).startOf('day')
+ const rawHour = Number(timeMatch[1])
+ const minute = Number(timeMatch[2])
+ const second = timeMatch[3] ? Number(timeMatch[3]) : 0
+ const millisecond = normalizeMillisecond(timeMatch[4])
+
+ return base
+ .set('hour', rawHour)
+ .set('minute', minute)
+ .set('second', second)
+ .set('millisecond', millisecond)
+ }
+
+ const timeMatch12h = TIME_ONLY_12H_REGEX.exec(trimmed)
+ if (timeMatch12h) {
+ const base = applyTimezone(dayjs(), tzName).startOf('day')
+ let hour = Number(timeMatch12h[1]) % 12
+ const isPM = timeMatch12h[4]?.toUpperCase() === 'PM'
+ if (isPM)
+ hour += 12
+ const minute = Number(timeMatch12h[2])
+ const second = timeMatch12h[3] ? Number(timeMatch12h[3]) : 0
+
+ return base
+ .set('hour', hour)
+ .set('minute', minute)
+ .set('second', second)
+ .set('millisecond', 0)
+ }
+
+ const candidateFormats = formats ?? COMMON_PARSE_FORMATS
+ for (const fmt of candidateFormats) {
+ const parsed = tzName
+ ? dayjs.tz(trimmed, fmt, tzName, true)
+ : dayjs(trimmed, fmt, true)
+ if (parsed.isValid())
+ return parsed
+ }
+
+ const fallbackParsed = tzName ? dayjs.tz(trimmed, tzName) : dayjs(trimmed)
+ if (fallbackParsed.isValid())
+ return fallbackParsed
+
+ warnParseFailure(value)
+ return undefined
+}
+
// Parse date with multiple format support
export const parseDateWithFormat = (dateString: string, format?: string): Dayjs | null => {
if (!dateString) return null
@@ -103,15 +217,7 @@ export const parseDateWithFormat = (dateString: string, format?: string): Dayjs
// Try common date formats
const formats = [
- 'YYYY-MM-DD', // Standard format
- 'YYYY/MM/DD', // Slash format
- 'DD-MM-YYYY', // European format
- 'DD/MM/YYYY', // European slash format
- 'MM-DD-YYYY', // US format
- 'MM/DD/YYYY', // US slash format
- 'YYYY-MM-DDTHH:mm:ss.SSSZ', // ISO format
- 'YYYY-MM-DDTHH:mm:ssZ', // ISO format (no milliseconds)
- 'YYYY-MM-DD HH:mm:ss', // Standard datetime format
+ ...COMMON_PARSE_FORMATS,
]
for (const fmt of formats) {
@@ -124,7 +230,7 @@ export const parseDateWithFormat = (dateString: string, format?: string): Dayjs
}
// Format date output with localization support
-export const formatDateForOutput = (date: Dayjs, includeTime: boolean = false, locale: string = 'en-US'): string => {
+export const formatDateForOutput = (date: Dayjs, includeTime: boolean = false, _locale: string = 'en-US'): string => {
if (!date || !date.isValid()) return ''
if (includeTime) {
diff --git a/web/app/components/base/drawer/index.tsx b/web/app/components/base/drawer/index.tsx
index 8217caae97..c35acbeac7 100644
--- a/web/app/components/base/drawer/index.tsx
+++ b/web/app/components/base/drawer/index.tsx
@@ -47,7 +47,10 @@ export default function Drawer({
!clickOutsideNotOpen && onClose()}
+ onClose={() => {
+ if (!clickOutsideNotOpen)
+ onClose()
+ }}
className={cn('fixed inset-0 z-[30] overflow-y-auto', dialogClassName)}
>
@@ -55,7 +58,8 @@ export default function Drawer({
{
- !clickOutsideNotOpen && onClose()
+ if (!clickOutsideNotOpen)
+ onClose()
}}
/>
@@ -80,11 +84,11 @@ export default function Drawer({
{
- onCancel && onCancel()
+ onCancel?.()
}}>{t('common.operation.cancel')}
{
- onOk && onOk()
+ onOk?.()
}}>{t('common.operation.save')}
)}
diff --git a/web/app/components/base/emoji-picker/index.tsx b/web/app/components/base/emoji-picker/index.tsx
index d3b20bb507..7b91c62797 100644
--- a/web/app/components/base/emoji-picker/index.tsx
+++ b/web/app/components/base/emoji-picker/index.tsx
@@ -45,7 +45,7 @@ const EmojiPicker: FC = ({
{
- onClose && onClose()
+ onClose?.()
}}>
{t('app.iconPicker.cancel')}
@@ -54,7 +54,7 @@ const EmojiPicker: FC
= ({
variant="primary"
className='w-full'
onClick={() => {
- onSelect && onSelect(selectedEmoji, selectedBackground!)
+ onSelect?.(selectedEmoji, selectedBackground!)
}}>
{t('app.iconPicker.ok')}
diff --git a/web/app/components/base/form/components/field/select.tsx b/web/app/components/base/form/components/field/select.tsx
index f12b90335b..dee047e2eb 100644
--- a/web/app/components/base/form/components/field/select.tsx
+++ b/web/app/components/base/form/components/field/select.tsx
@@ -33,7 +33,10 @@ const SelectField = ({
field.handleChange(value)}
+ onChange={(value) => {
+ field.handleChange(value)
+ onChange?.(value)
+ }}
{...selectProps}
/>
diff --git a/web/app/components/base/icons/assets/public/tracing/aliyun-icon-big.svg b/web/app/components/base/icons/assets/public/tracing/aliyun-icon-big.svg
index 210a1cd00b..d82b9bc1e4 100644
--- a/web/app/components/base/icons/assets/public/tracing/aliyun-icon-big.svg
+++ b/web/app/components/base/icons/assets/public/tracing/aliyun-icon-big.svg
@@ -1 +1 @@
-
+
\ No newline at end of file
diff --git a/web/app/components/base/icons/assets/public/tracing/aliyun-icon.svg b/web/app/components/base/icons/assets/public/tracing/aliyun-icon.svg
index 6f7645301c..cee8858471 100644
--- a/web/app/components/base/icons/assets/public/tracing/aliyun-icon.svg
+++ b/web/app/components/base/icons/assets/public/tracing/aliyun-icon.svg
@@ -1 +1 @@
-
+
\ No newline at end of file
diff --git a/web/app/components/base/icons/src/public/tracing/AliyunIcon.json b/web/app/components/base/icons/src/public/tracing/AliyunIcon.json
index 5cbb52c237..154aeff8c6 100644
--- a/web/app/components/base/icons/src/public/tracing/AliyunIcon.json
+++ b/web/app/components/base/icons/src/public/tracing/AliyunIcon.json
@@ -1,118 +1,129 @@
{
- "icon": {
- "type": "element",
- "isRootNode": true,
- "name": "svg",
- "attributes": {
- "xmlns": "http://www.w3.org/2000/svg",
- "xmlns:xlink": "http://www.w3.org/1999/xlink",
- "fill": "none",
- "version": "1.1",
- "width": "65",
- "height": "16",
- "viewBox": "0 0 65 16"
- },
- "children": [
- {
- "type": "element",
- "name": "defs",
- "children": [
- {
- "type": "element",
- "name": "clipPath",
- "attributes": {
- "id": "master_svg0_42_34281"
- },
- "children": [
- {
- "type": "element",
- "name": "rect",
- "attributes": {
- "x": "0",
- "y": "0",
- "width": "19",
- "height": "16",
- "rx": "0"
- }
- }
- ]
- }
- ]
- },
- {
- "type": "element",
- "name": "g",
- "children": [
- {
- "type": "element",
- "name": "g",
- "attributes": {
- "clip-path": "url(#master_svg0_42_34281)"
- },
- "children": [
- {
- "type": "element",
- "name": "g",
- "children": [
- {
- "type": "element",
- "name": "g",
- "children": [
- {
- "type": "element",
- "name": "path",
- "attributes": {
- "d": "M4.06862,14.6667C3.79213,14.6667,3.45463,14.5688,3.05614,14.373C2.97908,14.3351,2.92692,14.3105,2.89968,14.2992C2.33193,14.0628,1.82911,13.7294,1.39123,13.2989C0.463742,12.3871,0,11.2874,0,10C0,8.71258,0.463742,7.61293,1.39123,6.70107C2.16172,5.94358,3.06404,5.50073,4.09819,5.37252C4.23172,3.98276,4.81755,2.77756,5.85569,1.75693C7.04708,0.585642,8.4857,0,10.1716,0C11.5256,0,12.743,0.396982,13.8239,1.19095C14.8847,1.97019,15.61,2.97855,16,4.21604L14.7045,4.61063C14.4016,3.64918,13.8374,2.86532,13.0121,2.25905C12.1719,1.64191,11.2251,1.33333,10.1716,1.33333C8.8602,1.33333,7.74124,1.7888,6.81467,2.69974C5.88811,3.61067,5.42483,4.71076,5.42483,6L5.42483,6.66667L4.74673,6.66667C3.81172,6.66667,3.01288,6.99242,2.35021,7.64393C1.68754,8.2954,1.35621,9.08076,1.35621,10C1.35621,10.9192,1.68754,11.7046,2.35021,12.3561C2.66354,12.6641,3.02298,12.9026,3.42852,13.0714C3.48193,13.0937,3.55988,13.13,3.66237,13.1803C3.87004,13.2823,4.00545,13.3333,4.06862,13.3333L4.06862,14.6667Z",
- "fill-rule": "evenodd",
- "fill": "#FF6A00",
- "fill-opacity": "1"
- }
- }
- ]
- },
- {
- "type": "element",
- "name": "g",
- "children": [
- {
- "type": "element",
- "name": "path",
- "attributes": {
- "d": "M13.458613505859375,7.779393492279053C12.975613505859375,7.717463492279053,12.484813505859375,7.686503492279053,11.993983505859376,7.686503492279053C11.152583505859376,7.686503492279053,10.303403505859375,7.779393492279053,9.493183505859374,7.941943492279053C8.682953505859375,8.104503492279052,7.903893505859375,8.359943492279053,7.155983505859375,8.654083492279053C6.657383505859375,8.870823492279053,6.158783505859375,9.128843492279053,5.660181505859375,9.428153492279053C5.332974751859375,9.621673492279053,5.239486705859375,10.070633492279054,5.434253505859375,10.395743492279053L7.413073505859375,13.298533492279052C7.639003505859375,13.623603492279052,8.090863505859375,13.716463492279052,8.418073505859375,13.523003492279052C8.547913505859375,13.435263492279052,8.763453505859374,13.326893492279053,9.064693505859374,13.197863492279053C9.516553505859374,13.004333492279052,9.976203505859374,12.872733492279053,10.459223505859375,12.779863492279052C10.942243505859375,12.679263492279052,11.433053505859375,12.617333492279052,11.955023505859375,12.617333492279052L13.380683505859375,7.810353492279052L13.458613505859375,7.779393492279053ZM15.273813505859374,8.135463492279053L15.016753505859375,5.333333492279053L13.458613505859375,7.787133492279053C13.817013505859375,7.818093492279052,14.144213505859375,7.880023492279053,14.494743505859375,7.949683492279053C14.494743505859375,7.944523492279053,14.754433505859375,8.006453492279054,15.273813505859374,8.135463492279053ZM12.064083505859376,12.648273492279053L11.378523505859375,14.970463492279054L12.515943505859376,16.00003349227905L14.074083505859376,15.643933492279054L14.525943505859376,13.027603492279052C14.198743505859374,12.934663492279054,13.879283505859375,12.834063492279054,13.552083505859375,12.772133492279053C13.069083505859375,12.717933492279052,12.578283505859375,12.648273492279053,12.064083505859376,12.648273492279053ZM18.327743505859374,9.428153492279053C17.829143505859374,9.128843492279053,17.330543505859374,8.870823492279053,16.831943505859375,8.654083492279053C16.348943505859374,8.460573492279053,15.826943505859376,8.267053492279054,15.305013505859375,8.135463492279053L15.305013505859375,8.267053492279054L14.463613505859374,13.043063492279053C14.596083505859376,13.105003492279053,14.759683505859375,13.135933492279053,14.884283505859376,13.205603492279053C15.185523505859376,13.334623492279052,15.401043505859375,13.443003492279052,15.530943505859375,13.530733492279053C15.858143505859376,13.724263492279054,16.341143505859375,13.623603492279052,16.535943505859375,13.306263492279053L18.514743505859375,10.403483492279053C18.779643505859376,10.039673492279054,18.686143505859377,9.621673492279053,18.327743505859374,9.428153492279053Z",
- "fill": "#FF6A00",
- "fill-opacity": "1"
- }
- }
- ]
- }
- ]
- }
- ]
- },
- {
- "type": "element",
- "name": "g",
- "children": [
- {
- "type": "element",
- "name": "g",
- "children": [
- {
- "type": "element",
- "name": "path",
- "attributes": {
- "d": "M25.044,2.668L34.676,2.668L34.676,4.04L25.044,4.04L25.044,2.668ZM29.958,7.82Q29.258,9.066,28.355,10.41Q27.451999999999998,11.754,26.92,12.3L32.506,11.782Q31.442,10.158,30.84,9.346L32.058,8.562000000000001Q32.786,9.5,33.843,11.012Q34.9,12.524,35.516,13.546L34.214,14.526Q33.891999999999996,13.966,33.346000000000004,13.098Q32.016,13.182,29.734,13.378Q27.451999999999998,13.574,25.87,13.742L25.31,13.812L24.834,13.882L24.414,12.468Q24.708,12.37,24.862000000000002,12.265Q25.016,12.16,25.121,12.069Q25.226,11.978,25.268,11.936Q25.912,11.32,26.724,10.165Q27.536,9.01,28.208,7.82L23.854,7.82L23.854,6.434L35.866,6.434L35.866,7.82L29.958,7.82ZM42.656,7.414L42.656,8.576L41.354,8.576L41.354,1.814L42.656,1.87L42.656,7.036Q43.314,5.846,43.888000000000005,4.369Q44.462,2.892,44.714,1.6600000000000001L46.086,1.981999Q45.96,2.612,45.722,3.41L49.6,3.41L49.6,4.74L45.274,4.74Q44.616,6.56,43.706,8.128L42.656,7.414ZM38.596000000000004,2.346L39.884,2.402L39.884,8.212L38.596000000000004,8.212L38.596000000000004,2.346ZM46.184,4.964Q46.688,5.356,47.5,6.175Q48.312,6.994,48.788,7.582L47.751999999999995,8.59Q47.346000000000004,8.072,46.576,7.274Q45.806,6.476,45.204,5.902L46.184,4.964ZM48.41,9.01L48.41,12.706L49.894,12.706L49.894,13.966L37.391999999999996,13.966L37.391999999999996,12.706L38.848,12.706L38.848,9.01L48.41,9.01ZM41.676,10.256L40.164,10.256L40.164,12.706L41.676,12.706L41.676,10.256ZM42.908,12.706L44.364000000000004,12.706L44.364000000000004,10.256L42.908,10.256L42.908,12.706ZM45.582,12.706L47.108000000000004,12.706L47.108000000000004,10.256L45.582,10.256L45.582,12.706ZM54.906,7.456L55.116,8.394L54.178,8.814L54.178,12.818Q54.178,13.434,54.031,13.735Q53.884,14.036,53.534,14.162Q53.184,14.288,52.456,14.358L51.867999999999995,14.414L51.476,13.084L52.162,13.028Q52.512,13,52.652,12.958Q52.792,12.916,52.841,12.797Q52.89,12.678,52.89,12.384L52.89,9.36Q51.980000000000004,9.724,51.322,9.948L51.013999999999996,8.576Q51.798,8.324,52.89,7.876L52.89,5.524L51.42,5.524L51.42,4.166L52.89,4.166L52.89,1.7579989999999999L54.178,1.814L54.178,4.166L55.214,4.166L55.214,5.524L54.178,5.524L54.178,7.316L54.808,7.022L54.906,7.456ZM56.894,4.5440000000000005L56.894,6.098L55.564,6.098L55.564,3.256L58.686,3.256Q58.42,2.346,58.266,1.9260000000000002L59.624,1.7579989999999999Q59.848,2.276,60.142,3.256L63.25,3.256L63.25,6.098L61.962,6.098L61.962,4.5440000000000005L56.894,4.5440000000000005ZM59.008,6.322Q58.392,6.938,57.685,7.512Q56.978,8.086,55.956,8.841999999999999L55.242,7.764Q56.824,6.728,58.126,5.37L59.008,6.322ZM60.422,5.37Q61.024,5.776,62.095,6.581Q63.166,7.386,63.656,7.806L62.942,8.982Q62.368,8.45,61.332,7.652Q60.296,6.854,59.666,6.434L60.422,5.37ZM62.592,10.256L60.044,10.256L60.044,12.566L63.572,12.566L63.572,13.826L55.144,13.826L55.144,12.566L58.63,12.566L58.63,10.256L56.054,10.256L56.054,8.982L62.592,8.982L62.592,10.256Z",
- "fill": "#FF6A00",
- "fill-opacity": "1"
- }
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- },
- "name": "AliyunIcon"
+ "icon": {
+ "type": "element",
+ "isRootNode": true,
+ "name": "svg",
+ "attributes": {
+ "xmlns": "http://www.w3.org/2000/svg",
+ "xmlns:xlink": "http://www.w3.org/1999/xlink",
+ "fill": "none",
+ "version": "1.1",
+ "width": "65",
+ "height": "16",
+ "viewBox": "0 0 65 16"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "defs",
+ "attributes": {},
+ "children": [
+ {
+ "type": "element",
+ "name": "clipPath",
+ "attributes": {
+ "id": "master_svg0_42_34281"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "rect",
+ "attributes": {
+ "x": "0",
+ "y": "0",
+ "width": "19",
+ "height": "16",
+ "rx": "0"
+ },
+ "children": []
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {},
+ "children": [
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {
+ "clip-path": "url(#master_svg0_42_34281)"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {},
+ "children": [
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {},
+ "children": [
+ {
+ "type": "element",
+ "name": "path",
+ "attributes": {
+ "d": "M4.06862,14.6667C3.79213,14.6667,3.45463,14.5688,3.05614,14.373C2.97908,14.3351,2.92692,14.3105,2.89968,14.2992C2.33193,14.0628,1.82911,13.7294,1.39123,13.2989C0.463742,12.3871,0,11.2874,0,10C0,8.71258,0.463742,7.61293,1.39123,6.70107C2.16172,5.94358,3.06404,5.50073,4.09819,5.37252C4.23172,3.98276,4.81755,2.77756,5.85569,1.75693C7.04708,0.585642,8.4857,0,10.1716,0C11.5256,0,12.743,0.396982,13.8239,1.19095C14.8847,1.97019,15.61,2.97855,16,4.21604L14.7045,4.61063C14.4016,3.64918,13.8374,2.86532,13.0121,2.25905C12.1719,1.64191,11.2251,1.33333,10.1716,1.33333C8.8602,1.33333,7.74124,1.7888,6.81467,2.69974C5.88811,3.61067,5.42483,4.71076,5.42483,6L5.42483,6.66667L4.74673,6.66667C3.81172,6.66667,3.01288,6.99242,2.35021,7.64393C1.68754,8.2954,1.35621,9.08076,1.35621,10C1.35621,10.9192,1.68754,11.7046,2.35021,12.3561C2.66354,12.6641,3.02298,12.9026,3.42852,13.0714C3.48193,13.0937,3.55988,13.13,3.66237,13.1803C3.87004,13.2823,4.00545,13.3333,4.06862,13.3333L4.06862,14.6667Z",
+ "fill-rule": "evenodd",
+ "fill": "#FF6A00",
+ "fill-opacity": "1"
+ },
+ "children": []
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {},
+ "children": [
+ {
+ "type": "element",
+ "name": "path",
+ "attributes": {
+ "d": "M13.458613505859375,7.779393492279053C12.975613505859375,7.717463492279053,12.484813505859375,7.686503492279053,11.993983505859376,7.686503492279053C11.152583505859376,7.686503492279053,10.303403505859375,7.779393492279053,9.493183505859374,7.941943492279053C8.682953505859375,8.104503492279052,7.903893505859375,8.359943492279053,7.155983505859375,8.654083492279053C6.657383505859375,8.870823492279053,6.158783505859375,9.128843492279053,5.660181505859375,9.428153492279053C5.332974751859375,9.621673492279053,5.239486705859375,10.070633492279054,5.434253505859375,10.395743492279053L7.413073505859375,13.298533492279052C7.639003505859375,13.623603492279052,8.090863505859375,13.716463492279052,8.418073505859375,13.523003492279052C8.547913505859375,13.435263492279052,8.763453505859374,13.326893492279053,9.064693505859374,13.197863492279053C9.516553505859374,13.004333492279052,9.976203505859374,12.872733492279053,10.459223505859375,12.779863492279052C10.942243505859375,12.679263492279052,11.433053505859375,12.617333492279052,11.955023505859375,12.617333492279052L13.380683505859375,7.810353492279052L13.458613505859375,7.779393492279053ZM15.273813505859374,8.135463492279053L15.016753505859375,5.333333492279053L13.458613505859375,7.787133492279053C13.817013505859375,7.818093492279052,14.144213505859375,7.880023492279053,14.494743505859375,7.949683492279053C14.494743505859375,7.944523492279053,14.754433505859375,8.006453492279054,15.273813505859374,8.135463492279053ZM12.064083505859376,12.648273492279053L11.378523505859375,14.970463492279054L12.515943505859376,16.00003349227905L14.074083505859376,15.643933492279054L14.525943505859376,13.027603492279052C14.198743505859374,12.934663492279054,13.879283505859375,12.834063492279054,13.552083505859375,12.772133492279053C13.069083505859375,12.717933492279052,12.578283505859375,12.648273492279053,12.064083505859376,12.648273492279053ZM18.327743505859374,9.428153492279053C17.829143505859374,9.128843492279053,17.330543505859374,8.870823492279053,16.831943505859375,8.654083492279053C16.348943505859374,8.460573492279053,15.826943505859376,8.267053492279054,15.305013505859375,8.135463492279053L15.305013505859375,8.267053492279054L14.463613505859374,13.043063492279053C14.596083505859376,13.105003492279053,14.759683505859375,13.135933492279053,14.884283505859376,13.205603492279053C15.185523505859376,13.334623492279052,15.401043505859375,13.443003492279052,15.530943505859375,13.530733492279053C15.858143505859376,13.724263492279054,16.341143505859375,13.623603492279052,16.535943505859375,13.306263492279053L18.514743505859375,10.403483492279053C18.779643505859376,10.039673492279054,18.686143505859377,9.621673492279053,18.327743505859374,9.428153492279053Z",
+ "fill": "#FF6A00",
+ "fill-opacity": "1"
+ },
+ "children": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {},
+ "children": [
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {},
+ "children": [
+ {
+ "type": "element",
+ "name": "path",
+ "attributes": {
+ "d": "M25.044,2.668L34.676,2.668L34.676,4.04L25.044,4.04L25.044,2.668ZM29.958,7.82Q29.258,9.066,28.355,10.41Q27.451999999999998,11.754,26.92,12.3L32.506,11.782Q31.442,10.158,30.84,9.346L32.058,8.562000000000001Q32.786,9.5,33.843,11.012Q34.9,12.524,35.516,13.546L34.214,14.526Q33.891999999999996,13.966,33.346000000000004,13.098Q32.016,13.182,29.734,13.378Q27.451999999999998,13.574,25.87,13.742L25.31,13.812L24.834,13.882L24.414,12.468Q24.708,12.37,24.862000000000002,12.265Q25.016,12.16,25.121,12.069Q25.226,11.978,25.268,11.936Q25.912,11.32,26.724,10.165Q27.536,9.01,28.208,7.82L23.854,7.82L23.854,6.434L35.866,6.434L35.866,7.82L29.958,7.82ZM42.656,7.414L42.656,8.576L41.354,8.576L41.354,1.814L42.656,1.87L42.656,7.036Q43.314,5.846,43.888000000000005,4.369Q44.462,2.892,44.714,1.6600000000000001L46.086,1.981999Q45.96,2.612,45.722,3.41L49.6,3.41L49.6,4.74L45.274,4.74Q44.616,6.56,43.706,8.128L42.656,7.414ZM38.596000000000004,2.346L39.884,2.402L39.884,8.212L38.596000000000004,8.212L38.596000000000004,2.346ZM46.184,4.964Q46.688,5.356,47.5,6.175Q48.312,6.994,48.788,7.582L47.751999999999995,8.59Q47.346000000000004,8.072,46.576,7.274Q45.806,6.476,45.204,5.902L46.184,4.964ZM48.41,9.01L48.41,12.706L49.894,12.706L49.894,13.966L37.391999999999996,13.966L37.391999999999996,12.706L38.848,12.706L38.848,9.01L48.41,9.01ZM41.676,10.256L40.164,10.256L40.164,12.706L41.676,12.706L41.676,10.256ZM42.908,12.706L44.364000000000004,12.706L44.364000000000004,10.256L42.908,10.256L42.908,12.706ZM45.582,12.706L47.108000000000004,12.706L47.108000000000004,10.256L45.582,10.256L45.582,12.706ZM54.906,7.456L55.116,8.394L54.178,8.814L54.178,12.818Q54.178,13.434,54.031,13.735Q53.884,14.036,53.534,14.162Q53.184,14.288,52.456,14.358L51.867999999999995,14.414L51.476,13.084L52.162,13.028Q52.512,13,52.652,12.958Q52.792,12.916,52.841,12.797Q52.89,12.678,52.89,12.384L52.89,9.36Q51.980000000000004,9.724,51.322,9.948L51.013999999999996,8.576Q51.798,8.324,52.89,7.876L52.89,5.524L51.42,5.524L51.42,4.166L52.89,4.166L52.89,1.7579989999999999L54.178,1.814L54.178,4.166L55.214,4.166L55.214,5.524L54.178,5.524L54.178,7.316L54.808,7.022L54.906,7.456ZM56.894,4.5440000000000005L56.894,6.098L55.564,6.098L55.564,3.256L58.686,3.256Q58.42,2.346,58.266,1.9260000000000002L59.624,1.7579989999999999Q59.848,2.276,60.142,3.256L63.25,3.256L63.25,6.098L61.962,6.098L61.962,4.5440000000000005L56.894,4.5440000000000005ZM59.008,6.322Q58.392,6.938,57.685,7.512Q56.978,8.086,55.956,8.841999999999999L55.242,7.764Q56.824,6.728,58.126,5.37L59.008,6.322ZM60.422,5.37Q61.024,5.776,62.095,6.581Q63.166,7.386,63.656,7.806L62.942,8.982Q62.368,8.45,61.332,7.652Q60.296,6.854,59.666,6.434L60.422,5.37ZM62.592,10.256L60.044,10.256L60.044,12.566L63.572,12.566L63.572,13.826L55.144,13.826L55.144,12.566L58.63,12.566L58.63,10.256L56.054,10.256L56.054,8.982L62.592,8.982L62.592,10.256Z",
+ "fill": "#FF6A00",
+ "fill-opacity": "1"
+ },
+ "children": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ "name": "AliyunIcon"
}
diff --git a/web/app/components/base/icons/src/public/tracing/AliyunIconBig.json b/web/app/components/base/icons/src/public/tracing/AliyunIconBig.json
index ea60744daf..7ed5166461 100644
--- a/web/app/components/base/icons/src/public/tracing/AliyunIconBig.json
+++ b/web/app/components/base/icons/src/public/tracing/AliyunIconBig.json
@@ -1,71 +1,78 @@
{
- "icon": {
- "type": "element",
- "isRootNode": true,
- "name": "svg",
- "attributes": {
- "xmlns": "http://www.w3.org/2000/svg",
- "xmlns:xlink": "http://www.w3.org/1999/xlink",
- "fill": "none",
- "version": "1.1",
- "width": "96",
- "height": "24",
- "viewBox": "0 0 96 24"
- },
- "children": [
- {
- "type": "element",
- "name": "g",
- "children": [
- {
- "type": "element",
- "name": "g",
- "children": [
- {
- "type": "element",
- "name": "path",
- "attributes": {
- "d": "M6.10294,22C5.68819,22,5.18195,21.8532,4.58421,21.5595C4.46861,21.5027,4.39038,21.4658,4.34951,21.4488C3.49789,21.0943,2.74367,20.5941,2.08684,19.9484C0.695613,18.5806,0,16.9311,0,15C0,13.0689,0.695612,11.4194,2.08684,10.0516C3.24259,8.91537,4.59607,8.2511,6.14728,8.05878C6.34758,5.97414,7.22633,4.16634,8.78354,2.63539C10.5706,0.878463,12.7286,0,15.2573,0C17.2884,0,19.1146,0.595472,20.7358,1.78642C22.327,2.95528,23.4151,4.46783,24,6.32406L22.0568,6.91594C21.6024,5.47377,20.7561,4.29798,19.5181,3.38858C18.2579,2.46286,16.8377,2,15.2573,2C13.2903,2,11.6119,2.6832,10.222,4.04961C8.83217,5.41601,8.13725,7.06614,8.13725,9L8.13725,10L7.12009,10C5.71758,10,4.51932,10.4886,3.52532,11.4659C2.53132,12.4431,2.03431,13.6211,2.03431,15C2.03431,16.3789,2.53132,17.5569,3.52532,18.5341C3.99531,18.9962,4.53447,19.3538,5.14278,19.6071C5.2229,19.6405,5.33983,19.695,5.49356,19.7705C5.80505,19.9235,6.00818,20,6.10294,20L6.10294,22Z",
- "fill-rule": "evenodd",
- "fill": "#FF6A00",
- "fill-opacity": "1"
- }
- }
- ]
- },
- {
- "type": "element",
- "name": "g",
- "children": [
- {
- "type": "element",
- "name": "path",
- "attributes": {
- "d": "M20.18796103515625,11.66909C19.46346103515625,11.5762,18.72726103515625,11.52975,17.991011035156248,11.52975C16.728921035156247,11.52975,15.45515103515625,11.66909,14.23981103515625,11.91292C13.02447103515625,12.156749999999999,11.85588103515625,12.539909999999999,10.73402103515625,12.98113C9.98612103515625,13.306239999999999,9.23822103515625,13.69327,8.49031803515625,14.14223C7.99950790415625,14.43251,7.85927603515625,15.10595,8.15142503515625,15.59361L11.11966103515625,19.9478C11.45855103515625,20.4354,12.13634103515625,20.5747,12.627151035156249,20.2845C12.821921035156251,20.152900000000002,13.14523103515625,19.990299999999998,13.59708103515625,19.796799999999998C14.27487103515625,19.506500000000003,14.964341035156249,19.3091,15.68887103515625,19.169800000000002C16.413401035156248,19.018900000000002,17.14962103515625,18.926000000000002,17.93258103515625,18.926000000000002L20.071061035156248,11.715530000000001L20.18796103515625,11.66909ZM22.91076103515625,12.20319L22.525161035156252,8L20.18796103515625,11.6807C20.72556103515625,11.72714,21.21636103515625,11.82003,21.74216103515625,11.92453C21.74216103515625,11.91679,22.13166103515625,12.00968,22.91076103515625,12.20319ZM18.09616103515625,18.9724L17.06782103515625,22.4557L18.773961035156248,24L21.11116103515625,23.465899999999998L21.788961035156248,19.5414C21.298161035156248,19.402,20.81896103515625,19.2511,20.32816103515625,19.1582C19.60366103515625,19.076900000000002,18.86746103515625,18.9724,18.09616103515625,18.9724ZM27.49166103515625,14.14223C26.74376103515625,13.69327,25.99586103515625,13.306239999999999,25.24796103515625,12.98113C24.52346103515625,12.69086,23.74046103515625,12.40058,22.95756103515625,12.20319L22.95756103515625,12.40058L21.69546103515625,19.5646C21.89416103515625,19.6575,22.139561035156248,19.7039,22.32646103515625,19.8084C22.77836103515625,20.0019,23.101661035156248,20.1645,23.29646103515625,20.2961C23.78726103515625,20.586399999999998,24.51176103515625,20.4354,24.80396103515625,19.959400000000002L27.77216103515625,15.605229999999999C28.16946103515625,15.05951,28.02926103515625,14.43251,27.49166103515625,14.14223Z",
- "fill": "#FF6A00",
- "fill-opacity": "1"
- }
- }
- ]
- },
- {
- "type": "element",
- "name": "g",
- "children": [
- {
- "type": "element",
- "name": "path",
- "attributes": {
- "d": "M35.785,3.8624638671875L50.233000000000004,3.8624638671875L50.233000000000004,5.9204638671875L35.785,5.9204638671875L35.785,3.8624638671875ZM43.156,11.5904638671875Q42.106,13.4594638671875,40.7515,15.4754638671875Q39.397,17.4914638671875,38.599000000000004,18.3104638671875L46.978,17.5334638671875Q45.382,15.0974638671875,44.479,13.8794638671875L46.306,12.7034638671875Q47.397999999999996,14.1104638671875,48.9835,16.3784638671875Q50.569,18.6464638671875,51.492999999999995,20.1794638671875L49.54,21.6494638671875Q49.057,20.8094638671875,48.238,19.5074638671875Q46.243,19.6334638671875,42.82,19.9274638671875Q39.397,20.2214638671875,37.024,20.4734638671875L36.184,20.5784638671875L35.47,20.6834638671875L34.84,18.5624638671875Q35.281,18.4154638671875,35.512,18.2579638671875Q35.743,18.1004638671875,35.9005,17.963963867187502Q36.058,17.8274638671875,36.121,17.7644638671875Q37.087,16.840463867187502,38.305,15.1079638671875Q39.522999999999996,13.3754638671875,40.531,11.5904638671875L34,11.5904638671875L34,9.5114638671875L52.018,9.5114638671875L52.018,11.5904638671875L43.156,11.5904638671875ZM62.203,10.9814638671875L62.203,12.7244638671875L60.25,12.7244638671875L60.25,2.5814638671875L62.203,2.6654638671875L62.203,10.4144638671875Q63.19,8.6294638671875,64.051,6.4139638671875Q64.912,4.1984638671875,65.28999999999999,2.3504638671875L67.348,2.8334628671875Q67.15899999999999,3.7784638671875,66.80199999999999,4.9754638671875L72.619,4.9754638671875L72.619,6.9704638671875L66.13,6.9704638671875Q65.143,9.7004638671875,63.778,12.0524638671875L62.203,10.9814638671875ZM56.113,3.3794638671875L58.045,3.4634638671875L58.045,12.1784638671875L56.113,12.1784638671875L56.113,3.3794638671875ZM67.495,7.3064638671875Q68.251,7.8944638671875,69.469,9.1229638671875Q70.687,10.3514638671875,71.40100000000001,11.2334638671875L69.84700000000001,12.7454638671875Q69.238,11.9684638671875,68.083,10.7714638671875Q66.928,9.5744638671875,66.025,8.7134638671875L67.495,7.3064638671875ZM70.834,13.3754638671875L70.834,18.9194638671875L73.06,18.9194638671875L73.06,20.8094638671875L54.307,20.8094638671875L54.307,18.9194638671875L56.491,18.9194638671875L56.491,13.3754638671875L70.834,13.3754638671875ZM60.733000000000004,15.2444638671875L58.465,15.2444638671875L58.465,18.9194638671875L60.733000000000004,18.9194638671875L60.733000000000004,15.2444638671875ZM62.581,18.9194638671875L64.765,18.9194638671875L64.765,15.2444638671875L62.581,15.2444638671875L62.581,18.9194638671875ZM66.592,18.9194638671875L68.881,18.9194638671875L68.881,15.2444638671875L66.592,15.2444638671875L66.592,18.9194638671875ZM80.578,11.0444638671875L80.893,12.4514638671875L79.48599999999999,13.0814638671875L79.48599999999999,19.0874638671875Q79.48599999999999,20.0114638671875,79.2655,20.4629638671875Q79.045,20.9144638671875,78.52000000000001,21.1034638671875Q77.995,21.2924638671875,76.90299999999999,21.3974638671875L76.021,21.4814638671875L75.43299999999999,19.4864638671875L76.462,19.4024638671875Q76.987,19.3604638671875,77.197,19.2974638671875Q77.407,19.2344638671875,77.4805,19.0559638671875Q77.554,18.8774638671875,77.554,18.4364638671875L77.554,13.9004638671875Q76.189,14.4464638671875,75.202,14.7824638671875L74.74000000000001,12.7244638671875Q75.916,12.3464638671875,77.554,11.6744638671875L77.554,8.1464638671875L75.34899999999999,8.1464638671875L75.34899999999999,6.1094638671875L77.554,6.1094638671875L77.554,2.4974628671875L79.48599999999999,2.5814638671875L79.48599999999999,6.1094638671875L81.03999999999999,6.1094638671875L81.03999999999999,8.1464638671875L79.48599999999999,8.1464638671875L79.48599999999999,10.8344638671875L80.431,10.3934638671875L80.578,11.0444638671875ZM83.56,6.6764638671875L83.56,9.0074638671875L81.565,9.0074638671875L81.565,4.7444638671875L86.24799999999999,4.7444638671875Q85.84899999999999,3.3794638671875,85.618,2.7494638671875L87.655,2.4974628671875Q87.991,3.2744638671875,88.432,4.7444638671875L93.094,4.7444638671875L93.094,9.0074638671875L91.162,9.0074638671875L91.162,6.6764638671875L83.56,6.6764638671875ZM86.731,9.3434638671875Q85.807,10.2674638671875,84.7465,11.1284638671875Q83.686,11.9894638671875,82.15299999999999,13.1234638671875L81.082,11.5064638671875Q83.455,9.9524638671875,85.408,7.9154638671875L86.731,9.3434638671875ZM88.852,7.9154638671875Q89.755,8.5244638671875,91.3615,9.731963867187499Q92.968,10.9394638671875,93.703,11.5694638671875L92.632,13.3334638671875Q91.771,12.5354638671875,90.217,11.3384638671875Q88.663,10.1414638671875,87.718,9.5114638671875L88.852,7.9154638671875ZM92.107,15.2444638671875L88.285,15.2444638671875L88.285,18.7094638671875L93.577,18.7094638671875L93.577,20.5994638671875L80.935,20.5994638671875L80.935,18.7094638671875L86.164,18.7094638671875L86.164,15.2444638671875L82.3,15.2444638671875L82.3,13.3334638671875L92.107,13.3334638671875L92.107,15.2444638671875Z",
- "fill": "#FF6A00",
- "fill-opacity": "1"
- }
- }
- ]
- }
- ]
- }
- ]
- },
- "name": "AliyunBigIcon"
+ "icon": {
+ "type": "element",
+ "isRootNode": true,
+ "name": "svg",
+ "attributes": {
+ "xmlns": "http://www.w3.org/2000/svg",
+ "xmlns:xlink": "http://www.w3.org/1999/xlink",
+ "fill": "none",
+ "version": "1.1",
+ "width": "96",
+ "height": "24",
+ "viewBox": "0 0 96 24"
+ },
+ "children": [
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {},
+ "children": [
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {},
+ "children": [
+ {
+ "type": "element",
+ "name": "path",
+ "attributes": {
+ "d": "M6.10294,22C5.68819,22,5.18195,21.8532,4.58421,21.5595C4.46861,21.5027,4.39038,21.4658,4.34951,21.4488C3.49789,21.0943,2.74367,20.5941,2.08684,19.9484C0.695613,18.5806,0,16.9311,0,15C0,13.0689,0.695612,11.4194,2.08684,10.0516C3.24259,8.91537,4.59607,8.2511,6.14728,8.05878C6.34758,5.97414,7.22633,4.16634,8.78354,2.63539C10.5706,0.878463,12.7286,0,15.2573,0C17.2884,0,19.1146,0.595472,20.7358,1.78642C22.327,2.95528,23.4151,4.46783,24,6.32406L22.0568,6.91594C21.6024,5.47377,20.7561,4.29798,19.5181,3.38858C18.2579,2.46286,16.8377,2,15.2573,2C13.2903,2,11.6119,2.6832,10.222,4.04961C8.83217,5.41601,8.13725,7.06614,8.13725,9L8.13725,10L7.12009,10C5.71758,10,4.51932,10.4886,3.52532,11.4659C2.53132,12.4431,2.03431,13.6211,2.03431,15C2.03431,16.3789,2.53132,17.5569,3.52532,18.5341C3.99531,18.9962,4.53447,19.3538,5.14278,19.6071C5.2229,19.6405,5.33983,19.695,5.49356,19.7705C5.80505,19.9235,6.00818,20,6.10294,20L6.10294,22Z",
+ "fill-rule": "evenodd",
+ "fill": "#FF6A00",
+ "fill-opacity": "1"
+ },
+ "children": []
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {},
+ "children": [
+ {
+ "type": "element",
+ "name": "path",
+ "attributes": {
+ "d": "M20.18796103515625,11.66909C19.46346103515625,11.5762,18.72726103515625,11.52975,17.991011035156248,11.52975C16.728921035156247,11.52975,15.45515103515625,11.66909,14.23981103515625,11.91292C13.02447103515625,12.156749999999999,11.85588103515625,12.539909999999999,10.73402103515625,12.98113C9.98612103515625,13.306239999999999,9.23822103515625,13.69327,8.49031803515625,14.14223C7.99950790415625,14.43251,7.85927603515625,15.10595,8.15142503515625,15.59361L11.11966103515625,19.9478C11.45855103515625,20.4354,12.13634103515625,20.5747,12.627151035156249,20.2845C12.821921035156251,20.152900000000002,13.14523103515625,19.990299999999998,13.59708103515625,19.796799999999998C14.27487103515625,19.506500000000003,14.964341035156249,19.3091,15.68887103515625,19.169800000000002C16.413401035156248,19.018900000000002,17.14962103515625,18.926000000000002,17.93258103515625,18.926000000000002L20.071061035156248,11.715530000000001L20.18796103515625,11.66909ZM22.91076103515625,12.20319L22.525161035156252,8L20.18796103515625,11.6807C20.72556103515625,11.72714,21.21636103515625,11.82003,21.74216103515625,11.92453C21.74216103515625,11.91679,22.13166103515625,12.00968,22.91076103515625,12.20319ZM18.09616103515625,18.9724L17.06782103515625,22.4557L18.773961035156248,24L21.11116103515625,23.465899999999998L21.788961035156248,19.5414C21.298161035156248,19.402,20.81896103515625,19.2511,20.32816103515625,19.1582C19.60366103515625,19.076900000000002,18.86746103515625,18.9724,18.09616103515625,18.9724ZM27.49166103515625,14.14223C26.74376103515625,13.69327,25.99586103515625,13.306239999999999,25.24796103515625,12.98113C24.52346103515625,12.69086,23.74046103515625,12.40058,22.95756103515625,12.20319L22.95756103515625,12.40058L21.69546103515625,19.5646C21.89416103515625,19.6575,22.139561035156248,19.7039,22.32646103515625,19.8084C22.77836103515625,20.0019,23.101661035156248,20.1645,23.29646103515625,20.2961C23.78726103515625,20.586399999999998,24.51176103515625,20.4354,24.80396103515625,19.959400000000002L27.77216103515625,15.605229999999999C28.16946103515625,15.05951,28.02926103515625,14.43251,27.49166103515625,14.14223Z",
+ "fill": "#FF6A00",
+ "fill-opacity": "1"
+ },
+ "children": []
+ }
+ ]
+ },
+ {
+ "type": "element",
+ "name": "g",
+ "attributes": {},
+ "children": [
+ {
+ "type": "element",
+ "name": "path",
+ "attributes": {
+ "d": "M35.785,3.8624638671875L50.233000000000004,3.8624638671875L50.233000000000004,5.9204638671875L35.785,5.9204638671875L35.785,3.8624638671875ZM43.156,11.5904638671875Q42.106,13.4594638671875,40.7515,15.4754638671875Q39.397,17.4914638671875,38.599000000000004,18.3104638671875L46.978,17.5334638671875Q45.382,15.0974638671875,44.479,13.8794638671875L46.306,12.7034638671875Q47.397999999999996,14.1104638671875,48.9835,16.3784638671875Q50.569,18.6464638671875,51.492999999999995,20.1794638671875L49.54,21.6494638671875Q49.057,20.8094638671875,48.238,19.5074638671875Q46.243,19.6334638671875,42.82,19.9274638671875Q39.397,20.2214638671875,37.024,20.4734638671875L36.184,20.5784638671875L35.47,20.6834638671875L34.84,18.5624638671875Q35.281,18.4154638671875,35.512,18.2579638671875Q35.743,18.1004638671875,35.9005,17.963963867187502Q36.058,17.8274638671875,36.121,17.7644638671875Q37.087,16.840463867187502,38.305,15.1079638671875Q39.522999999999996,13.3754638671875,40.531,11.5904638671875L34,11.5904638671875L34,9.5114638671875L52.018,9.5114638671875L52.018,11.5904638671875L43.156,11.5904638671875ZM62.203,10.9814638671875L62.203,12.7244638671875L60.25,12.7244638671875L60.25,2.5814638671875L62.203,2.6654638671875L62.203,10.4144638671875Q63.19,8.6294638671875,64.051,6.4139638671875Q64.912,4.1984638671875,65.28999999999999,2.3504638671875L67.348,2.8334628671875Q67.15899999999999,3.7784638671875,66.80199999999999,4.9754638671875L72.619,4.9754638671875L72.619,6.9704638671875L66.13,6.9704638671875Q65.143,9.7004638671875,63.778,12.0524638671875L62.203,10.9814638671875ZM56.113,3.3794638671875L58.045,3.4634638671875L58.045,12.1784638671875L56.113,12.1784638671875L56.113,3.3794638671875ZM67.495,7.3064638671875Q68.251,7.8944638671875,69.469,9.1229638671875Q70.687,10.3514638671875,71.40100000000001,11.2334638671875L69.84700000000001,12.7454638671875Q69.238,11.9684638671875,68.083,10.7714638671875Q66.928,9.5744638671875,66.025,8.7134638671875L67.495,7.3064638671875ZM70.834,13.3754638671875L70.834,18.9194638671875L73.06,18.9194638671875L73.06,20.8094638671875L54.307,20.8094638671875L54.307,18.9194638671875L56.491,18.9194638671875L56.491,13.3754638671875L70.834,13.3754638671875ZM60.733000000000004,15.2444638671875L58.465,15.2444638671875L58.465,18.9194638671875L60.733000000000004,18.9194638671875L60.733000000000004,15.2444638671875ZM62.581,18.9194638671875L64.765,18.9194638671875L64.765,15.2444638671875L62.581,15.2444638671875L62.581,18.9194638671875ZM66.592,18.9194638671875L68.881,18.9194638671875L68.881,15.2444638671875L66.592,15.2444638671875L66.592,18.9194638671875ZM80.578,11.0444638671875L80.893,12.4514638671875L79.48599999999999,13.0814638671875L79.48599999999999,19.0874638671875Q79.48599999999999,20.0114638671875,79.2655,20.4629638671875Q79.045,20.9144638671875,78.52000000000001,21.1034638671875Q77.995,21.2924638671875,76.90299999999999,21.3974638671875L76.021,21.4814638671875L75.43299999999999,19.4864638671875L76.462,19.4024638671875Q76.987,19.3604638671875,77.197,19.2974638671875Q77.407,19.2344638671875,77.4805,19.0559638671875Q77.554,18.8774638671875,77.554,18.4364638671875L77.554,13.9004638671875Q76.189,14.4464638671875,75.202,14.7824638671875L74.74000000000001,12.7244638671875Q75.916,12.3464638671875,77.554,11.6744638671875L77.554,8.1464638671875L75.34899999999999,8.1464638671875L75.34899999999999,6.1094638671875L77.554,6.1094638671875L77.554,2.4974628671875L79.48599999999999,2.5814638671875L79.48599999999999,6.1094638671875L81.03999999999999,6.1094638671875L81.03999999999999,8.1464638671875L79.48599999999999,8.1464638671875L79.48599999999999,10.8344638671875L80.431,10.3934638671875L80.578,11.0444638671875ZM83.56,6.6764638671875L83.56,9.0074638671875L81.565,9.0074638671875L81.565,4.7444638671875L86.24799999999999,4.7444638671875Q85.84899999999999,3.3794638671875,85.618,2.7494638671875L87.655,2.4974628671875Q87.991,3.2744638671875,88.432,4.7444638671875L93.094,4.7444638671875L93.094,9.0074638671875L91.162,9.0074638671875L91.162,6.6764638671875L83.56,6.6764638671875ZM86.731,9.3434638671875Q85.807,10.2674638671875,84.7465,11.1284638671875Q83.686,11.9894638671875,82.15299999999999,13.1234638671875L81.082,11.5064638671875Q83.455,9.9524638671875,85.408,7.9154638671875L86.731,9.3434638671875ZM88.852,7.9154638671875Q89.755,8.5244638671875,91.3615,9.731963867187499Q92.968,10.9394638671875,93.703,11.5694638671875L92.632,13.3334638671875Q91.771,12.5354638671875,90.217,11.3384638671875Q88.663,10.1414638671875,87.718,9.5114638671875L88.852,7.9154638671875ZM92.107,15.2444638671875L88.285,15.2444638671875L88.285,18.7094638671875L93.577,18.7094638671875L93.577,20.5994638671875L80.935,20.5994638671875L80.935,18.7094638671875L86.164,18.7094638671875L86.164,15.2444638671875L82.3,15.2444638671875L82.3,13.3334638671875L92.107,13.3334638671875L92.107,15.2444638671875Z",
+ "fill": "#FF6A00",
+ "fill-opacity": "1"
+ },
+ "children": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ "name": "AliyunIconBig"
}
diff --git a/web/app/components/base/image-uploader/image-list.tsx b/web/app/components/base/image-uploader/image-list.tsx
index 758ffe99d5..3b5f6dee9c 100644
--- a/web/app/components/base/image-uploader/image-list.tsx
+++ b/web/app/components/base/image-uploader/image-list.tsx
@@ -62,7 +62,7 @@ const ImageList: FC = ({
{item.progress === -1 && (
onReUpload && onReUpload(item._id)}
+ onClick={() => onReUpload?.(item._id)}
/>
)}
@@ -122,7 +122,7 @@ const ImageList: FC
= ({
'rounded-2xl shadow-lg hover:bg-state-base-hover',
item.progress === -1 ? 'flex' : 'hidden group-hover:flex',
)}
- onClick={() => onRemove && onRemove(item._id)}
+ onClick={() => onRemove?.(item._id)}
>
diff --git a/web/app/components/base/image-uploader/image-preview.tsx b/web/app/components/base/image-uploader/image-preview.tsx
index e67edaa3ca..53c22e344f 100644
--- a/web/app/components/base/image-uploader/image-preview.tsx
+++ b/web/app/components/base/image-uploader/image-preview.tsx
@@ -20,7 +20,7 @@ const isBase64 = (str: string): boolean => {
try {
return btoa(atob(str)) === str
}
- catch (err) {
+ catch {
return false
}
}
diff --git a/web/app/components/base/markdown-blocks/code-block.tsx b/web/app/components/base/markdown-blocks/code-block.tsx
index 48de8bf4ab..bc41c65fd5 100644
--- a/web/app/components/base/markdown-blocks/code-block.tsx
+++ b/web/app/components/base/markdown-blocks/code-block.tsx
@@ -8,12 +8,14 @@ import {
import ActionButton from '@/app/components/base/action-button'
import CopyIcon from '@/app/components/base/copy-icon'
import SVGBtn from '@/app/components/base/svg'
-import Flowchart from '@/app/components/base/mermaid'
import { Theme } from '@/types/app'
import useTheme from '@/hooks/use-theme'
import SVGRenderer from '../svg-gallery' // Assumes svg-gallery.tsx is in /base directory
import MarkdownMusic from '@/app/components/base/markdown-blocks/music'
import ErrorBoundary from '@/app/components/base/markdown/error-boundary'
+import dynamic from 'next/dynamic'
+
+const Flowchart = dynamic(() => import('@/app/components/base/mermaid'), { ssr: false })
// Available language https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/AVAILABLE_LANGUAGES_HLJS.MD
const capitalizationLanguageNameMap: Record = {
@@ -125,7 +127,7 @@ const CodeBlock: any = memo(({ inline, className, children = '', ...props }: any
// Store event handlers in useMemo to avoid recreating them
const echartsEvents = useMemo(() => ({
- finished: (params: EChartsEventParams) => {
+ finished: (_params: EChartsEventParams) => {
// Limit finished event frequency to avoid infinite loops
finishedEventCountRef.current++
if (finishedEventCountRef.current > 3) {
diff --git a/web/app/components/base/markdown/index.tsx b/web/app/components/base/markdown/index.tsx
index bab5ac8eba..19f39d8aaa 100644
--- a/web/app/components/base/markdown/index.tsx
+++ b/web/app/components/base/markdown/index.tsx
@@ -1,25 +1,11 @@
-import ReactMarkdown from 'react-markdown'
+import dynamic from 'next/dynamic'
import 'katex/dist/katex.min.css'
-import RemarkMath from 'remark-math'
-import RemarkBreaks from 'remark-breaks'
-import RehypeKatex from 'rehype-katex'
-import RemarkGfm from 'remark-gfm'
-import RehypeRaw from 'rehype-raw'
import { flow } from 'lodash-es'
import cn from '@/utils/classnames'
-import { customUrlTransform, preprocessLaTeX, preprocessThinkTag } from './markdown-utils'
-import {
- AudioBlock,
- CodeBlock,
- Img,
- Link,
- MarkdownButton,
- MarkdownForm,
- Paragraph,
- ScriptBlock,
- ThinkBlock,
- VideoBlock,
-} from '@/app/components/base/markdown-blocks'
+import { preprocessLaTeX, preprocessThinkTag } from './markdown-utils'
+import type { ReactMarkdownWrapperProps } from './react-markdown-wrapper'
+
+const ReactMarkdown = dynamic(() => import('./react-markdown-wrapper').then(mod => mod.ReactMarkdownWrapper), { ssr: false })
/**
* @fileoverview Main Markdown rendering component.
@@ -31,9 +17,7 @@ import {
export type MarkdownProps = {
content: string
className?: string
- customDisallowedElements?: string[]
- customComponents?: Record>
-}
+} & Pick
export const Markdown = (props: MarkdownProps) => {
const { customComponents = {} } = props
@@ -44,53 +28,7 @@ export const Markdown = (props: MarkdownProps) => {
return (
- {
- return (tree: any) => {
- const iterate = (node: any) => {
- if (node.type === 'element' && node.properties?.ref)
- delete node.properties.ref
-
- if (node.type === 'element' && !/^[a-z][a-z0-9]*$/i.test(node.tagName)) {
- node.type = 'text'
- node.value = `<${node.tagName}`
- }
-
- if (node.children)
- node.children.forEach(iterate)
- }
- tree.children.forEach(iterate)
- }
- },
- ]}
- urlTransform={customUrlTransform}
- disallowedElements={['iframe', 'head', 'html', 'meta', 'link', 'style', 'body', ...(props.customDisallowedElements || [])]}
- components={{
- code: CodeBlock,
- img: Img,
- video: VideoBlock,
- audio: AudioBlock,
- a: Link,
- p: Paragraph,
- button: MarkdownButton,
- form: MarkdownForm,
- script: ScriptBlock as any,
- details: ThinkBlock,
- ...customComponents,
- }}
- >
- {/* Markdown detect has problem. */}
- {latexContent}
-
+
)
}
diff --git a/web/app/components/base/markdown/react-markdown-wrapper.tsx b/web/app/components/base/markdown/react-markdown-wrapper.tsx
new file mode 100644
index 0000000000..054b5f66cb
--- /dev/null
+++ b/web/app/components/base/markdown/react-markdown-wrapper.tsx
@@ -0,0 +1,82 @@
+import ReactMarkdown from 'react-markdown'
+import RemarkMath from 'remark-math'
+import RemarkBreaks from 'remark-breaks'
+import RehypeKatex from 'rehype-katex'
+import RemarkGfm from 'remark-gfm'
+import RehypeRaw from 'rehype-raw'
+import AudioBlock from '@/app/components/base/markdown-blocks/audio-block'
+import Img from '@/app/components/base/markdown-blocks/img'
+import Link from '@/app/components/base/markdown-blocks/link'
+import MarkdownButton from '@/app/components/base/markdown-blocks/button'
+import MarkdownForm from '@/app/components/base/markdown-blocks/form'
+import Paragraph from '@/app/components/base/markdown-blocks/paragraph'
+import ScriptBlock from '@/app/components/base/markdown-blocks/script-block'
+import ThinkBlock from '@/app/components/base/markdown-blocks/think-block'
+import VideoBlock from '@/app/components/base/markdown-blocks/video-block'
+import { customUrlTransform } from './markdown-utils'
+
+import type { FC } from 'react'
+
+import dynamic from 'next/dynamic'
+
+const CodeBlock = dynamic(() => import('@/app/components/base/markdown-blocks/code-block'), { ssr: false })
+
+export type ReactMarkdownWrapperProps = {
+ latexContent: any
+ customDisallowedElements?: string[]
+ customComponents?: Record>
+}
+
+export const ReactMarkdownWrapper: FC = (props) => {
+ const { customComponents, latexContent } = props
+
+ return (
+ {
+ return (tree: any) => {
+ const iterate = (node: any) => {
+ if (node.type === 'element' && node.properties?.ref)
+ delete node.properties.ref
+
+ if (node.type === 'element' && !/^[a-z][a-z0-9]*$/i.test(node.tagName)) {
+ node.type = 'text'
+ node.value = `<${node.tagName}`
+ }
+
+ if (node.children)
+ node.children.forEach(iterate)
+ }
+ tree.children.forEach(iterate)
+ }
+ },
+ ]}
+ urlTransform={customUrlTransform}
+ disallowedElements={['iframe', 'head', 'html', 'meta', 'link', 'style', 'body', ...(props.customDisallowedElements || [])]}
+ components={{
+ code: CodeBlock,
+ img: Img,
+ video: VideoBlock,
+ audio: AudioBlock,
+ a: Link,
+ p: Paragraph,
+ button: MarkdownButton,
+ form: MarkdownForm,
+ script: ScriptBlock as any,
+ details: ThinkBlock,
+ ...customComponents,
+ }}
+ >
+ {/* Markdown detect has problem. */}
+ {latexContent}
+
+ )
+}
diff --git a/web/app/components/base/mermaid/utils.ts b/web/app/components/base/mermaid/utils.ts
index 9d56494227..7e59869de1 100644
--- a/web/app/components/base/mermaid/utils.ts
+++ b/web/app/components/base/mermaid/utils.ts
@@ -60,7 +60,7 @@ export function svgToBase64(svgGraph: string): Promise {
reader.readAsDataURL(blob)
})
}
- catch (error) {
+ catch {
return Promise.resolve('')
}
}
diff --git a/web/app/components/base/pagination/hook.ts b/web/app/components/base/pagination/hook.ts
index 32a2af8013..9b9d86a4ef 100644
--- a/web/app/components/base/pagination/hook.ts
+++ b/web/app/components/base/pagination/hook.ts
@@ -10,9 +10,7 @@ const usePagination = ({
edgePageCount,
middlePagesSiblingCount,
}: IPaginationProps): IUsePagination => {
- const pages = new Array(totalPages)
- .fill(0)
- .map((_, i) => i + 1)
+ const pages = React.useMemo(() => Array.from({ length: totalPages }, (_, i) => i + 1), [totalPages])
const hasPreviousPage = currentPage > 1
const hasNextPage = currentPage < totalPages
diff --git a/web/app/components/base/pagination/index.tsx b/web/app/components/base/pagination/index.tsx
index 8126f663dd..e0c02df253 100644
--- a/web/app/components/base/pagination/index.tsx
+++ b/web/app/components/base/pagination/index.tsx
@@ -57,7 +57,34 @@ const CustomizedPagination: FC = ({
if (isNaN(Number.parseInt(value)))
return setInputValue('')
setInputValue(Number.parseInt(value))
- handlePaging(value)
+ }
+
+ const handleInputConfirm = () => {
+ if (inputValue !== '' && String(inputValue) !== String(current + 1)) {
+ handlePaging(String(inputValue))
+ return
+ }
+
+ if (inputValue === '')
+ setInputValue(current + 1)
+
+ setShowInput(false)
+ }
+
+ const handleInputKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') {
+ e.preventDefault()
+ handleInputConfirm()
+ }
+ else if (e.key === 'Escape') {
+ e.preventDefault()
+ setInputValue(current + 1)
+ setShowInput(false)
+ }
+ }
+
+ const handleInputBlur = () => {
+ handleInputConfirm()
}
return (
@@ -105,7 +132,8 @@ const CustomizedPagination: FC = ({
autoFocus
value={inputValue}
onChange={handleInputChange}
- onBlur={() => setShowInput(false)}
+ onKeyDown={handleInputKeyDown}
+ onBlur={handleInputBlur}
/>
)}
(null)
const onMouseEnter = (isOpen: boolean) => {
- timeOutRef.current && window.clearTimeout(timeOutRef.current)
- !isOpen && buttonRef.current?.click()
+ if (timeOutRef.current != null)
+ window.clearTimeout(timeOutRef.current)
+ if (!isOpen)
+ buttonRef.current?.click()
}
const onMouseLeave = (isOpen: boolean) => {
timeOutRef.current = window.setTimeout(() => {
- isOpen && buttonRef.current?.click()
+ if (isOpen)
+ buttonRef.current?.click()
}, timeoutDuration)
}
diff --git a/web/app/components/base/select/locale-signin.tsx b/web/app/components/base/select/locale-signin.tsx
index 2d487c4be3..2ec491f4bf 100644
--- a/web/app/components/base/select/locale-signin.tsx
+++ b/web/app/components/base/select/locale-signin.tsx
@@ -43,7 +43,7 @@ export default function LocaleSigninSelect({
className={'group flex w-full items-center rounded-lg px-3 py-2 text-sm text-text-secondary data-[active]:bg-state-base-hover'}
onClick={(evt) => {
evt.preventDefault()
- onChange && onChange(item.value)
+ onChange?.(item.value)
}}
>
{item.name}
diff --git a/web/app/components/base/select/locale.tsx b/web/app/components/base/select/locale.tsx
index cc5662f53b..2033488435 100644
--- a/web/app/components/base/select/locale.tsx
+++ b/web/app/components/base/select/locale.tsx
@@ -43,7 +43,7 @@ export default function Select({
className={'group flex w-full items-center rounded-lg px-3 py-2 text-sm text-text-secondary data-[active]:bg-state-base-hover'}
onClick={(evt) => {
evt.preventDefault()
- onChange && onChange(item.value)
+ onChange?.(item.value)
}}
>
{item.name}
diff --git a/web/app/components/base/tag-management/panel.tsx b/web/app/components/base/tag-management/panel.tsx
index 79031e9f8a..3cfb68fef0 100644
--- a/web/app/components/base/tag-management/panel.tsx
+++ b/web/app/components/base/tag-management/panel.tsx
@@ -97,10 +97,13 @@ const Panel = (props: PanelProps) => {
const removeTagIDs = value.filter(v => !selectedTagIDs.includes(v))
const selectedTags = tagList.filter(tag => selectedTagIDs.includes(tag.id))
onCacheUpdate(selectedTags)
- Promise.all([
- ...(addTagIDs.length ? [bind(addTagIDs)] : []),
- ...[removeTagIDs.length ? removeTagIDs.map(tagID => unbind(tagID)) : []],
- ]).finally(() => {
+ const operations: Promise[] = []
+ if (addTagIDs.length)
+ operations.push(bind(addTagIDs))
+ if (removeTagIDs.length)
+ operations.push(...removeTagIDs.map(tagID => unbind(tagID)))
+
+ Promise.all(operations).finally(() => {
if (onChange)
onChange()
})
diff --git a/web/app/components/base/voice-input/index.tsx b/web/app/components/base/voice-input/index.tsx
index 5a5400ad30..6587a61217 100644
--- a/web/app/components/base/voice-input/index.tsx
+++ b/web/app/components/base/voice-input/index.tsx
@@ -81,7 +81,8 @@ const VoiceInput = ({
setStartRecord(false)
setStartConvert(true)
recorder.current.stop()
- drawRecordId.current && cancelAnimationFrame(drawRecordId.current)
+ if (drawRecordId.current)
+ cancelAnimationFrame(drawRecordId.current)
drawRecordId.current = null
const canvas = canvasRef.current!
const ctx = ctxRef.current!
diff --git a/web/app/components/billing/pricing/footer.tsx b/web/app/components/billing/pricing/footer.tsx
index 4e3cdfee3d..fd713eb3da 100644
--- a/web/app/components/billing/pricing/footer.tsx
+++ b/web/app/components/billing/pricing/footer.tsx
@@ -2,19 +2,29 @@ import React from 'react'
import Link from 'next/link'
import { useTranslation } from 'react-i18next'
import { RiArrowRightUpLine } from '@remixicon/react'
+import { type Category, CategoryEnum } from '.'
+import cn from '@/utils/classnames'
type FooterProps = {
pricingPageURL: string
+ currentCategory: Category
}
const Footer = ({
pricingPageURL,
+ currentCategory,
}: FooterProps) => {
const { t } = useTranslation()
return (
-
+
+ {currentCategory === CategoryEnum.CLOUD && (
+
+ {t('billing.plansCommon.taxTip')}
+ {t('billing.plansCommon.taxTipSecond')}
+
+ )}
void
@@ -25,7 +30,7 @@ const Pricing: FC = ({
const { plan } = useProviderContext()
const { isCurrentWorkspaceManager } = useAppContext()
const [planRange, setPlanRange] = React.useState(PlanRange.monthly)
- const [currentCategory, setCurrentCategory] = useState('cloud')
+ const [currentCategory, setCurrentCategory] = useState(CategoryEnum.CLOUD)
const canPay = isCurrentWorkspaceManager
useKeyPress(['esc'], onCancel)
@@ -57,7 +62,7 @@ const Pricing: FC = ({
planRange={planRange}
canPay={canPay}
/>
-
+
diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/uploader.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/uploader.tsx
index 433b7851d7..57509b646f 100644
--- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/uploader.tsx
+++ b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/uploader.tsx
@@ -34,7 +34,8 @@ const Uploader: FC = ({
const handleDragEnter = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
- e.target !== dragRef.current && setDragging(true)
+ if (e.target !== dragRef.current)
+ setDragging(true)
}
const handleDragOver = (e: DragEvent) => {
e.preventDefault()
@@ -43,7 +44,8 @@ const Uploader: FC = ({
const handleDragLeave = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
- e.target === dragRef.current && setDragging(false)
+ if (e.target === dragRef.current)
+ setDragging(false)
}
const handleDrop = (e: DragEvent) => {
e.preventDefault()
diff --git a/web/app/components/datasets/create/file-uploader/index.tsx b/web/app/components/datasets/create/file-uploader/index.tsx
index 4dfdbc4e96..e2bbad2776 100644
--- a/web/app/components/datasets/create/file-uploader/index.tsx
+++ b/web/app/components/datasets/create/file-uploader/index.tsx
@@ -185,7 +185,8 @@ const FileUploader = ({
const handleDragEnter = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
- e.target !== dragRef.current && setDragging(true)
+ if (e.target !== dragRef.current)
+ setDragging(true)
}
const handleDragOver = (e: DragEvent) => {
e.preventDefault()
@@ -194,7 +195,8 @@ const FileUploader = ({
const handleDragLeave = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
- e.target === dragRef.current && setDragging(false)
+ if (e.target === dragRef.current)
+ setDragging(false)
}
type FileWithPath = {
relativePath?: string
diff --git a/web/app/components/datasets/create/step-two/index.tsx b/web/app/components/datasets/create/step-two/index.tsx
index 650dc9425a..4c2e129cd2 100644
--- a/web/app/components/datasets/create/step-two/index.tsx
+++ b/web/app/components/datasets/create/step-two/index.tsx
@@ -568,9 +568,9 @@ const StepTwo = ({
params,
{
onSuccess(data) {
- updateIndexingTypeCache && updateIndexingTypeCache(indexType as string)
- updateResultCache && updateResultCache(data)
- updateRetrievalMethodCache && updateRetrievalMethodCache(retrievalConfig.search_method as string)
+ updateIndexingTypeCache?.(indexType as string)
+ updateResultCache?.(data)
+ updateRetrievalMethodCache?.(retrievalConfig.search_method as string)
},
},
)
@@ -578,17 +578,18 @@ const StepTwo = ({
else {
await createDocumentMutation.mutateAsync(params, {
onSuccess(data) {
- updateIndexingTypeCache && updateIndexingTypeCache(indexType as string)
- updateResultCache && updateResultCache(data)
- updateRetrievalMethodCache && updateRetrievalMethodCache(retrievalConfig.search_method as string)
+ updateIndexingTypeCache?.(indexType as string)
+ updateResultCache?.(data)
+ updateRetrievalMethodCache?.(retrievalConfig.search_method as string)
},
})
}
if (mutateDatasetRes)
mutateDatasetRes()
invalidDatasetList()
- onStepChange && onStepChange(+1)
- isSetting && onSave && onSave()
+ onStepChange?.(+1)
+ if (isSetting)
+ onSave?.()
}
useEffect(() => {
@@ -1026,7 +1027,7 @@ const StepTwo = ({
{!isSetting
? (
-
onStepChange && onStepChange(-1)}>
+ onStepChange?.(-1)}>
{t('datasetCreation.stepTwo.previousStep')}
diff --git a/web/app/components/datasets/create/website/jina-reader/base/options-wrap.tsx b/web/app/components/datasets/create/website/jina-reader/base/options-wrap.tsx
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx
index 144decada5..ad05f0729b 100644
--- a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx
+++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx
@@ -7,7 +7,6 @@ import DocumentFileIcon from '@/app/components/datasets/common/document-file-ico
import cn from '@/utils/classnames'
import type { CustomFile as File, FileItem } from '@/models/datasets'
import { ToastContext } from '@/app/components/base/toast'
-import SimplePieChart from '@/app/components/base/simple-pie-chart'
import { upload } from '@/service/base'
import I18n from '@/context/i18n'
import { LanguagesSupported } from '@/i18n-config/language'
@@ -17,6 +16,9 @@ import useTheme from '@/hooks/use-theme'
import { useFileUploadConfig } from '@/service/use-common'
import { useDataSourceStore, useDataSourceStoreWithSelector } from '../store'
import produce from 'immer'
+import dynamic from 'next/dynamic'
+
+const SimplePieChart = dynamic(() => import('@/app/components/base/simple-pie-chart'), { ssr: false })
const FILES_NUMBER_LIMIT = 20
@@ -198,7 +200,8 @@ const LocalFile = ({
const handleDragEnter = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
- e.target !== dragRef.current && setDragging(true)
+ if (e.target !== dragRef.current)
+ setDragging(true)
}
const handleDragOver = (e: DragEvent) => {
e.preventDefault()
@@ -207,7 +210,8 @@ const LocalFile = ({
const handleDragLeave = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
- e.target === dragRef.current && setDragging(false)
+ if (e.target === dragRef.current)
+ setDragging(false)
}
const handleDrop = useCallback((e: DragEvent) => {
diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx
index 28c9ae456e..cd410c4d1e 100644
--- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx
+++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx
@@ -45,10 +45,13 @@ const CrawledResult = ({
const handleItemCheckChange = useCallback((item: CrawlResultItem) => {
return (checked: boolean) => {
- if (checked)
- isMultipleChoice ? onSelectedChange([...checkedList, item]) : onSelectedChange([item])
- else
- onSelectedChange(checkedList.filter(checkedItem => checkedItem.source_url !== item.source_url))
+ if (checked) {
+ if (isMultipleChoice)
+ onSelectedChange([...checkedList, item])
+ else
+ onSelectedChange([item])
+ }
+ else { onSelectedChange(checkedList.filter(checkedItem => checkedItem.source_url !== item.source_url)) }
}
}, [checkedList, onSelectedChange, isMultipleChoice])
diff --git a/web/app/components/datasets/documents/create-from-pipeline/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/index.tsx
index 75c31acd1b..77b77700ca 100644
--- a/web/app/components/datasets/documents/create-from-pipeline/index.tsx
+++ b/web/app/components/datasets/documents/create-from-pipeline/index.tsx
@@ -326,7 +326,10 @@ const CreateFormPipeline = () => {
}, [])
const handleSubmit = useCallback((data: Record) => {
- isPreview.current ? handlePreviewChunks(data) : handleProcess(data)
+ if (isPreview.current)
+ handlePreviewChunks(data)
+ else
+ handleProcess(data)
}, [handlePreviewChunks, handleProcess])
const handlePreviewFileChange = useCallback((file: DocumentItem) => {
diff --git a/web/app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx b/web/app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx
index aaf9ed8ffd..7e8749f0bf 100644
--- a/web/app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx
+++ b/web/app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx
@@ -99,7 +99,8 @@ const CSVUploader: FC = ({
const handleDragEnter = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
- e.target !== dragRef.current && setDragging(true)
+ if (e.target !== dragRef.current)
+ setDragging(true)
}
const handleDragOver = (e: DragEvent) => {
e.preventDefault()
@@ -108,7 +109,8 @@ const CSVUploader: FC = ({
const handleDragLeave = (e: DragEvent) => {
e.preventDefault()
e.stopPropagation()
- e.target === dragRef.current && setDragging(false)
+ if (e.target === dragRef.current)
+ setDragging(false)
}
const handleDrop = (e: DragEvent) => {
e.preventDefault()
diff --git a/web/app/components/datasets/documents/detail/completed/index.tsx b/web/app/components/datasets/documents/detail/completed/index.tsx
index 726be7519a..8fa167f976 100644
--- a/web/app/components/datasets/documents/detail/completed/index.tsx
+++ b/web/app/components/datasets/documents/detail/completed/index.tsx
@@ -284,7 +284,8 @@ const Completed: FC = ({
onSuccess: () => {
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
resetList()
- !segId && setSelectedSegmentIds([])
+ if (!segId)
+ setSelectedSegmentIds([])
},
onError: () => {
notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
@@ -438,7 +439,8 @@ const Completed: FC = ({
}
else {
resetList()
- currentPage !== totalPages && setCurrentPage(totalPages)
+ if (currentPage !== totalPages)
+ setCurrentPage(totalPages)
}
}, [segmentListData, limit, currentPage, resetList])
@@ -491,7 +493,8 @@ const Completed: FC = ({
}
else {
resetChildList()
- currentPage !== totalPages && setCurrentPage(totalPages)
+ if (currentPage !== totalPages)
+ setCurrentPage(totalPages)
}
}, [childChunkListData, limit, currentPage, resetChildList])
diff --git a/web/app/components/datasets/documents/detail/metadata/index.tsx b/web/app/components/datasets/documents/detail/metadata/index.tsx
index 4cb5fe97e9..88c359f8b2 100644
--- a/web/app/components/datasets/documents/detail/metadata/index.tsx
+++ b/web/app/components/datasets/documents/detail/metadata/index.tsx
@@ -66,7 +66,7 @@ export const FieldInfo: FC = ({
? displayedValue
: inputType === 'select'
? onUpdate && onUpdate(value as string)}
+ onSelect={({ value }) => onUpdate?.(value as string)}
items={selectOptions}
defaultValue={value}
className={s.select}
@@ -75,7 +75,7 @@ export const FieldInfo: FC = ({
/>
: inputType === 'textarea'
? onUpdate && onUpdate(e.target.value)}
+ onChange={e => onUpdate?.(e.target.value)}
value={value}
className={s.textArea}
placeholder={`${t('datasetDocuments.metadata.placeholder.add')}${label}`}
diff --git a/web/app/components/datasets/documents/detail/settings/pipeline-settings/index.tsx b/web/app/components/datasets/documents/detail/settings/pipeline-settings/index.tsx
index e69481c3ea..1ab47be445 100644
--- a/web/app/components/datasets/documents/detail/settings/pipeline-settings/index.tsx
+++ b/web/app/components/datasets/documents/detail/settings/pipeline-settings/index.tsx
@@ -148,7 +148,10 @@ const PipelineSettings = ({
}, [])
const handleSubmit = useCallback((data: Record) => {
- isPreview.current ? handlePreviewChunks(data) : handleProcess(data)
+ if (isPreview.current)
+ handlePreviewChunks(data)
+ else
+ handleProcess(data)
}, [handlePreviewChunks, handleProcess])
if (isFetchingLastRunData) {
diff --git a/web/app/components/datasets/extra-info/service-api/index.tsx b/web/app/components/datasets/extra-info/service-api/index.tsx
index b1843682ee..af7ce946ad 100644
--- a/web/app/components/datasets/extra-info/service-api/index.tsx
+++ b/web/app/components/datasets/extra-info/service-api/index.tsx
@@ -52,7 +52,7 @@ const ServiceApi = ({
/>
-
+
{
diff --git a/web/app/components/datasets/list/dataset-card/index.tsx b/web/app/components/datasets/list/dataset-card/index.tsx
index db8ee0226d..b1304e578e 100644
--- a/web/app/components/datasets/list/dataset-card/index.tsx
+++ b/web/app/components/datasets/list/dataset-card/index.tsx
@@ -157,12 +157,12 @@ const DatasetCard = ({
data-disable-nprogress={true}
onClick={(e) => {
e.preventDefault()
- isExternalProvider
- ? push(`/datasets/${dataset.id}/hitTesting`)
- // eslint-disable-next-line sonarjs/no-nested-conditional
- : isPipelineUnpublished
- ? push(`/datasets/${dataset.id}/pipeline`)
- : push(`/datasets/${dataset.id}/documents`)
+ if (isExternalProvider)
+ push(`/datasets/${dataset.id}/hitTesting`)
+ else if (isPipelineUnpublished)
+ push(`/datasets/${dataset.id}/pipeline`)
+ else
+ push(`/datasets/${dataset.id}/documents`)
}}
>
{!dataset.embedding_available && (
diff --git a/web/app/components/datasets/loading.tsx b/web/app/components/datasets/loading.tsx
index e69de29bb2..182c1f91de 100644
--- a/web/app/components/datasets/loading.tsx
+++ b/web/app/components/datasets/loading.tsx
@@ -0,0 +1,3 @@
+const DatasetsLoading = () => null
+
+export default DatasetsLoading
diff --git a/web/app/components/datasets/preview/index.tsx b/web/app/components/datasets/preview/index.tsx
index e69de29bb2..e71c440c20 100644
--- a/web/app/components/datasets/preview/index.tsx
+++ b/web/app/components/datasets/preview/index.tsx
@@ -0,0 +1,3 @@
+const DatasetPreview = () => null
+
+export default DatasetPreview
diff --git a/web/app/components/header/account-setting/collapse/index.tsx b/web/app/components/header/account-setting/collapse/index.tsx
index 2ad4a97cd1..44360df8cd 100644
--- a/web/app/components/header/account-setting/collapse/index.tsx
+++ b/web/app/components/header/account-setting/collapse/index.tsx
@@ -39,7 +39,7 @@ const Collapse = ({
{
items.map(item => (
-
onSelect && onSelect(item)}>
+
onSelect?.(item)}>
{renderItem(item)}
))
diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx
index 7c259f1a78..bdaeacb5c0 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx
@@ -276,7 +276,7 @@ function Form<
- {label[language] || label.en_US}
+ {label[language] || label.en_US}
{required && (
*
)}
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx
index 29da0ffc0c..291ba013f7 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx
@@ -49,7 +49,7 @@ const ModelLoadBalancingConfigs = ({
provider,
model,
configurationMethod,
- currentCustomConfigurationModelFixedFields,
+ currentCustomConfigurationModelFixedFields: _currentCustomConfigurationModelFixedFields,
withSwitch = false,
className,
modelCredential,
diff --git a/web/app/components/plugins/card/base/card-icon.tsx b/web/app/components/plugins/card/base/card-icon.tsx
index 7f7468ece2..b4c052c13c 100644
--- a/web/app/components/plugins/card/base/card-icon.tsx
+++ b/web/app/components/plugins/card/base/card-icon.tsx
@@ -1,6 +1,8 @@
import { RiCheckLine, RiCloseLine } from '@remixicon/react'
+import { Mcp } from '@/app/components/base/icons/src/vender/other'
import AppIcon from '@/app/components/base/app-icon'
import cn from '@/utils/classnames'
+import { shouldUseMcpIcon } from '@/utils/mcp'
const iconSizeMap = {
xs: 'w-4 h-4 text-base',
@@ -35,6 +37,7 @@ const Icon = ({
icon={src.content}
background={src.background}
className='rounded-md'
+ innerIcon={shouldUseMcpIcon(src) ? : undefined}
/>
)
diff --git a/web/app/components/plugins/hooks.ts b/web/app/components/plugins/hooks.ts
index 0af7c1a170..f22b2c4d69 100644
--- a/web/app/components/plugins/hooks.ts
+++ b/web/app/components/plugins/hooks.ts
@@ -1,3 +1,4 @@
+import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import type { TFunction } from 'i18next'
import {
@@ -14,23 +15,29 @@ export const useTags = (translateFromOut?: TFunction) => {
const { t: translation } = useTranslation()
const t = translateFromOut || translation
- const tags = tagKeys.map((tag) => {
- return {
- name: tag,
- label: t(`pluginTags.tags.${tag}`),
+ const tags = useMemo(() => {
+ return tagKeys.map((tag) => {
+ return {
+ name: tag,
+ label: t(`pluginTags.tags.${tag}`),
+ }
+ })
+ }, [t])
+
+ const tagsMap = useMemo(() => {
+ return tags.reduce((acc, tag) => {
+ acc[tag.name] = tag
+ return acc
+ }, {} as Record
)
+ }, [tags])
+
+ const getTagLabel = useMemo(() => {
+ return (name: string) => {
+ if (!tagsMap[name])
+ return name
+ return tagsMap[name].label
}
- })
-
- const tagsMap = tags.reduce((acc, tag) => {
- acc[tag.name] = tag
- return acc
- }, {} as Record)
-
- const getTagLabel = (name: string) => {
- if (!tagsMap[name])
- return name
- return tagsMap[name].label
- }
+ }, [tagsMap])
return {
tags,
@@ -48,23 +55,27 @@ export const useCategories = (translateFromOut?: TFunction) => {
const { t: translation } = useTranslation()
const t = translateFromOut || translation
- const categories = categoryKeys.map((category) => {
- if (category === 'agent-strategy') {
- return {
- name: 'agent-strategy',
- label: t('plugin.category.agents'),
+ const categories = useMemo(() => {
+ return categoryKeys.map((category) => {
+ if (category === 'agent-strategy') {
+ return {
+ name: 'agent-strategy',
+ label: t('plugin.category.agents'),
+ }
}
- }
- return {
- name: category,
- label: t(`plugin.category.${category}s`),
- }
- })
+ return {
+ name: category,
+ label: t(`plugin.category.${category}s`),
+ }
+ })
+ }, [t])
- const categoriesMap = categories.reduce((acc, category) => {
- acc[category.name] = category
- return acc
- }, {} as Record)
+ const categoriesMap = useMemo(() => {
+ return categories.reduce((acc, category) => {
+ acc[category.name] = category
+ return acc
+ }, {} as Record)
+ }, [categories])
return {
categories,
@@ -76,23 +87,27 @@ export const useSingleCategories = (translateFromOut?: TFunction) => {
const { t: translation } = useTranslation()
const t = translateFromOut || translation
- const categories = categoryKeys.map((category) => {
- if (category === 'agent-strategy') {
- return {
- name: 'agent-strategy',
- label: t('plugin.categorySingle.agent'),
+ const categories = useMemo(() => {
+ return categoryKeys.map((category) => {
+ if (category === 'agent-strategy') {
+ return {
+ name: 'agent-strategy',
+ label: t('plugin.categorySingle.agent'),
+ }
}
- }
- return {
- name: category,
- label: t(`plugin.categorySingle.${category}`),
- }
- })
+ return {
+ name: category,
+ label: t(`plugin.categorySingle.${category}`),
+ }
+ })
+ }, [t])
- const categoriesMap = categories.reduce((acc, category) => {
- acc[category.name] = category
- return acc
- }, {} as Record)
+ const categoriesMap = useMemo(() => {
+ return categories.reduce((acc, category) => {
+ acc[category.name] = category
+ return acc
+ }, {} as Record)
+ }, [categories])
return {
categories,
diff --git a/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx b/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx
index 3c79acb653..10c28507f7 100644
--- a/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx
@@ -33,7 +33,7 @@ type Props = {
}
const AppPicker: FC = ({
- scope,
+ scope: _scope,
disabled,
trigger,
placement = 'right-start',
@@ -90,7 +90,7 @@ const AppPicker: FC = ({
}
// Set up MutationObserver to watch DOM changes
- mutationObserver = new MutationObserver((mutations) => {
+ mutationObserver = new MutationObserver((_mutations) => {
if (observerTarget.current) {
setupIntersectionObserver()
mutationObserver?.disconnect()
diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx
index fb4c99e1e4..873f187e8f 100644
--- a/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx
@@ -148,7 +148,7 @@ const ModelParameterModal: FC = ({
})
}
}
- catch (e) {
+ catch {
Toast.notify({ type: 'error', message: t('common.error') })
}
}
diff --git a/web/app/components/rag-pipeline/components/panel/input-field/field-list/hooks.ts b/web/app/components/rag-pipeline/components/panel/input-field/field-list/hooks.ts
index d5c0797c9b..376894b1bb 100644
--- a/web/app/components/rag-pipeline/components/panel/input-field/field-list/hooks.ts
+++ b/web/app/components/rag-pipeline/components/panel/input-field/field-list/hooks.ts
@@ -51,7 +51,7 @@ export const useFieldList = ({
const handleListSortChange = useCallback((list: SortableItem[]) => {
const newInputFields = list.map((item) => {
- const { id, chosen, selected, ...filed } = item
+ const { id: _id, chosen: _chosen, selected: _selected, ...filed } = item
return filed
})
handleInputFieldsChange(newInputFields)
diff --git a/web/app/components/rag-pipeline/components/panel/test-run/header.tsx b/web/app/components/rag-pipeline/components/panel/test-run/header.tsx
index 16291f868b..a536f66137 100644
--- a/web/app/components/rag-pipeline/components/panel/test-run/header.tsx
+++ b/web/app/components/rag-pipeline/components/panel/test-run/header.tsx
@@ -15,7 +15,8 @@ const Header = () => {
isPreparingDataSource,
setIsPreparingDataSource,
} = workflowStore.getState()
- isPreparingDataSource && setIsPreparingDataSource?.(false)
+ if (isPreparingDataSource)
+ setIsPreparingDataSource?.(false)
handleCancelDebugAndPreviewPanel()
}, [workflowStore])
diff --git a/web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts b/web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts
index 86e44dced7..ad757f36a7 100644
--- a/web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts
+++ b/web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts
@@ -104,7 +104,7 @@ export const useNodesSyncDraft = () => {
const res = await syncWorkflowDraft(postParams)
setSyncWorkflowDraftHash(res.hash)
setDraftUpdatedAt(res.updated_at)
- callback?.onSuccess && callback.onSuccess()
+ callback?.onSuccess?.()
}
catch (error: any) {
if (error && error.json && !error.bodyUsed) {
@@ -113,10 +113,10 @@ export const useNodesSyncDraft = () => {
handleRefreshWorkflowDraft()
})
}
- callback?.onError && callback.onError()
+ callback?.onError?.()
}
finally {
- callback?.onSettled && callback.onSettled()
+ callback?.onSettled?.()
}
}
}, [getPostParams, getNodesReadOnly, workflowStore, handleRefreshWorkflowDraft])
diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx
index da5b09b065..98804c7311 100644
--- a/web/app/components/share/text-generation/index.tsx
+++ b/web/app/components/share/text-generation/index.tsx
@@ -363,7 +363,8 @@ const TextGeneration: FC = ({
(async () => {
if (!appData || !appParams)
return
- !isWorkflow && fetchSavedMessage()
+ if (!isWorkflow)
+ fetchSavedMessage()
const { app_id: appId, site: siteInfo, custom_config } = appData
setAppId(appId)
setSiteInfo(siteInfo as SiteInfo)
diff --git a/web/app/components/share/text-generation/result/index.tsx b/web/app/components/share/text-generation/result/index.tsx
index a7eb7f7591..7a4e606636 100644
--- a/web/app/components/share/text-generation/result/index.tsx
+++ b/web/app/components/share/text-generation/result/index.tsx
@@ -78,15 +78,15 @@ const Result: FC = ({
setRespondingFalse()
}, [controlStopResponding])
- const [completionRes, doSetCompletionRes] = useState('')
- const completionResRef = useRef()
- const setCompletionRes = (res: any) => {
+ const [completionRes, doSetCompletionRes] = useState('')
+ const completionResRef = useRef('')
+ const setCompletionRes = (res: string) => {
completionResRef.current = res
doSetCompletionRes(res)
}
const getCompletionRes = () => completionResRef.current
const [workflowProcessData, doSetWorkflowProcessData] = useState()
- const workflowProcessDataRef = useRef()
+ const workflowProcessDataRef = useRef(undefined)
const setWorkflowProcessData = (data: WorkflowProcess) => {
workflowProcessDataRef.current = data
doSetWorkflowProcessData(data)
@@ -126,8 +126,8 @@ const Result: FC = ({
let hasEmptyInput = ''
const requiredVars = prompt_variables?.filter(({ key, name, required, type }) => {
- if(type === 'boolean')
- return false // boolean input is not required
+ if(type === 'boolean' || type === 'checkbox')
+ return false // boolean/checkbox input is not required
const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
return res
}) || [] // compatible with old version
diff --git a/web/app/components/share/text-generation/run-once/index.tsx b/web/app/components/share/text-generation/run-once/index.tsx
index 7896776f35..4f94aa1fe8 100644
--- a/web/app/components/share/text-generation/run-once/index.tsx
+++ b/web/app/components/share/text-generation/run-once/index.tsx
@@ -51,6 +51,8 @@ const RunOnce: FC = ({
promptConfig.prompt_variables.forEach((item) => {
if (item.type === 'string' || item.type === 'paragraph')
newInputs[item.key] = ''
+ else if (item.type === 'checkbox')
+ newInputs[item.key] = false
else
newInputs[item.key] = undefined
})
@@ -77,6 +79,8 @@ const RunOnce: FC = ({
newInputs[item.key] = item.default || ''
else if (item.type === 'number')
newInputs[item.key] = item.default
+ else if (item.type === 'checkbox')
+ newInputs[item.key] = item.default || false
else if (item.type === 'file')
newInputs[item.key] = item.default
else if (item.type === 'file-list')
@@ -96,7 +100,7 @@ const RunOnce: FC = ({
{(inputs === null || inputs === undefined || Object.keys(inputs).length === 0) || !isInitialized ? null
: promptConfig.prompt_variables.map(item => (
- {item.type !== 'boolean' && (
+ {item.type !== 'checkbox' && (
{item.name}
)}
@@ -134,7 +138,7 @@ const RunOnce: FC
= ({
onChange={(e: ChangeEvent) => { handleInputsChange({ ...inputsRef.current, [item.key]: e.target.value }) }}
/>
)}
- {item.type === 'boolean' && (
+ {item.type === 'checkbox' && (
void
}
-const DEFAULT_ICON = { type: 'emoji', icon: '🧿', background: '#EFF1F5' }
+const DEFAULT_ICON = { type: 'emoji', icon: '🔗', background: '#6366F1' }
const extractFileId = (url: string) => {
const match = url.match(/files\/(.+?)\/file-preview/)
return match ? match[1] : null
@@ -208,6 +210,7 @@ const MCPModal = ({
icon={appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId}
background={appIcon.type === 'emoji' ? appIcon.background : undefined}
imageUrl={appIcon.type === 'image' ? appIcon.url : undefined}
+ innerIcon={shouldUseMcpIconForAppIcon(appIcon.type, appIcon.type === 'emoji' ? appIcon.icon : '') ? : undefined}
size='xxl'
className='relative cursor-pointer rounded-2xl'
coverElement={
diff --git a/web/app/components/tools/provider-list.tsx b/web/app/components/tools/provider-list.tsx
index 08a4aa0b5d..1679b4469b 100644
--- a/web/app/components/tools/provider-list.tsx
+++ b/web/app/components/tools/provider-list.tsx
@@ -21,6 +21,7 @@ import { useCheckInstalled, useInvalidateInstalledPluginList } from '@/service/u
import { useGlobalPublicStore } from '@/context/global-public-context'
import { ToolTypeEnum } from '../workflow/block-selector/types'
import { useMarketplace } from './marketplace/hooks'
+import { useTags } from '@/app/components/plugins/hooks'
const getToolType = (type: string) => {
switch (type) {
@@ -40,6 +41,7 @@ const ProviderList = () => {
// const searchParams = useSearchParams()
// searchParams.get('category') === 'workflow'
const { t } = useTranslation()
+ const { getTagLabel } = useTags()
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
const containerRef = useRef(null)
@@ -180,7 +182,7 @@ const ProviderList = () => {
} as any}
footer={
getTagLabel(label)) || []}
/>
}
/>
diff --git a/web/app/components/tools/utils/to-form-schema.ts b/web/app/components/tools/utils/to-form-schema.ts
index c1d17b48ef..8e85a5f9b0 100644
--- a/web/app/components/tools/utils/to-form-schema.ts
+++ b/web/app/components/tools/utils/to-form-schema.ts
@@ -45,6 +45,7 @@ export const toolCredentialToFormSchemas = (parameters: ToolCredential[]) => {
return {
...parameter,
variable: parameter.name,
+ type: toType(parameter.type),
label: parameter.label,
tooltip: parameter.help,
show_on: [],
diff --git a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts
index ab14a07574..0cdc2b2d40 100644
--- a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts
+++ b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts
@@ -162,7 +162,7 @@ export const useNodesSyncDraft = () => {
setSyncWorkflowDraftHash(res.hash)
setDraftUpdatedAt(res.updated_at)
console.log('Leader successfully synced workflow draft')
- callback?.onSuccess && callback.onSuccess()
+ callback?.onSuccess?.()
}
catch (error: any) {
console.error('Leader failed to sync workflow draft:', error)
@@ -174,10 +174,10 @@ export const useNodesSyncDraft = () => {
}
})
}
- callback?.onError && callback.onError()
+ callback?.onError?.()
}
finally {
- callback?.onSettled && callback.onSettled()
+ callback?.onSettled?.()
}
}
}, [workflowStore, getPostParams, getNodesReadOnly, handleRefreshWorkflowDraft])
diff --git a/web/app/components/workflow-app/index.tsx b/web/app/components/workflow-app/index.tsx
index 0d2e5ce290..14f72af7fa 100644
--- a/web/app/components/workflow-app/index.tsx
+++ b/web/app/components/workflow-app/index.tsx
@@ -8,7 +8,7 @@ import {
} from '@/app/components/workflow/types'
import {
useWorkflowInit,
-} from './hooks'
+} from './hooks/use-workflow-init'
import {
initialEdges,
initialNodes,
diff --git a/web/app/components/workflow/block-selector/tool/tool.tsx b/web/app/components/workflow/block-selector/tool/tool.tsx
index 6004bb119a..30d3e218d2 100644
--- a/web/app/components/workflow/block-selector/tool/tool.tsx
+++ b/web/app/components/workflow/block-selector/tool/tool.tsx
@@ -73,7 +73,7 @@ const Tool: FC = ({
if (isHovering && !isAllSelected) {
return (
{
+ onClick={() => {
onSelectMultiple?.(BlockEnum.Tool, actions.filter(action => !getIsDisabled(action)).map((tool) => {
const params: Record = {}
if (tool.parameters) {
diff --git a/web/app/components/workflow/hooks/use-shortcuts.ts b/web/app/components/workflow/hooks/use-shortcuts.ts
index 072c35033d..a02b8010cc 100644
--- a/web/app/components/workflow/hooks/use-shortcuts.ts
+++ b/web/app/components/workflow/hooks/use-shortcuts.ts
@@ -108,7 +108,8 @@ export const useShortcuts = (): void => {
const { showDebugAndPreviewPanel } = workflowStore.getState()
if (shouldHandleShortcut(e) && !showDebugAndPreviewPanel) {
e.preventDefault()
- workflowHistoryShortcutsEnabled && handleHistoryBack()
+ if (workflowHistoryShortcutsEnabled)
+ handleHistoryBack()
}
}, { exactMatch: true, useCapture: true })
@@ -117,7 +118,8 @@ export const useShortcuts = (): void => {
(e) => {
if (shouldHandleShortcut(e)) {
e.preventDefault()
- workflowHistoryShortcutsEnabled && handleHistoryForward()
+ if (workflowHistoryShortcutsEnabled)
+ handleHistoryForward()
}
},
{ exactMatch: true, useCapture: true },
diff --git a/web/app/components/workflow/hooks/use-workflow-history.ts b/web/app/components/workflow/hooks/use-workflow-history.ts
index b7338dc4f8..58bbe415a8 100644
--- a/web/app/components/workflow/hooks/use-workflow-history.ts
+++ b/web/app/components/workflow/hooks/use-workflow-history.ts
@@ -16,39 +16,41 @@ import type { WorkflowHistoryEventMeta } from '../workflow-history-store'
* - InputChange events in Node Panels do not trigger state changes.
* - Resizing UI elements does not trigger state changes.
*/
-export enum WorkflowHistoryEvent {
- NodeTitleChange = 'NodeTitleChange',
- NodeDescriptionChange = 'NodeDescriptionChange',
- NodeDragStop = 'NodeDragStop',
- NodeChange = 'NodeChange',
- NodeConnect = 'NodeConnect',
- NodePaste = 'NodePaste',
- NodeDelete = 'NodeDelete',
- EdgeDelete = 'EdgeDelete',
- EdgeDeleteByDeleteBranch = 'EdgeDeleteByDeleteBranch',
- NodeAdd = 'NodeAdd',
- NodeResize = 'NodeResize',
- NoteAdd = 'NoteAdd',
- NoteChange = 'NoteChange',
- NoteDelete = 'NoteDelete',
- LayoutOrganize = 'LayoutOrganize',
-}
+export const WorkflowHistoryEvent = {
+ NodeTitleChange: 'NodeTitleChange',
+ NodeDescriptionChange: 'NodeDescriptionChange',
+ NodeDragStop: 'NodeDragStop',
+ NodeChange: 'NodeChange',
+ NodeConnect: 'NodeConnect',
+ NodePaste: 'NodePaste',
+ NodeDelete: 'NodeDelete',
+ EdgeDelete: 'EdgeDelete',
+ EdgeDeleteByDeleteBranch: 'EdgeDeleteByDeleteBranch',
+ NodeAdd: 'NodeAdd',
+ NodeResize: 'NodeResize',
+ NoteAdd: 'NoteAdd',
+ NoteChange: 'NoteChange',
+ NoteDelete: 'NoteDelete',
+ LayoutOrganize: 'LayoutOrganize',
+} as const
+
+export type WorkflowHistoryEventT = keyof typeof WorkflowHistoryEvent
export const useWorkflowHistory = () => {
const store = useStoreApi()
const { store: workflowHistoryStore } = useWorkflowHistoryStore()
const { t } = useTranslation()
- const [undoCallbacks, setUndoCallbacks] = useState([])
- const [redoCallbacks, setRedoCallbacks] = useState([])
+ const [undoCallbacks, setUndoCallbacks] = useState<(() => void)[]>([])
+ const [redoCallbacks, setRedoCallbacks] = useState<(() => void)[]>([])
- const onUndo = useCallback((callback: unknown) => {
- setUndoCallbacks((prev: any) => [...prev, callback])
+ const onUndo = useCallback((callback: () => void) => {
+ setUndoCallbacks(prev => [...prev, callback])
return () => setUndoCallbacks(prev => prev.filter(cb => cb !== callback))
}, [])
- const onRedo = useCallback((callback: unknown) => {
- setRedoCallbacks((prev: any) => [...prev, callback])
+ const onRedo = useCallback((callback: () => void) => {
+ setRedoCallbacks(prev => [...prev, callback])
return () => setRedoCallbacks(prev => prev.filter(cb => cb !== callback))
}, [])
@@ -65,7 +67,7 @@ export const useWorkflowHistory = () => {
// Some events may be triggered multiple times in a short period of time.
// We debounce the history state update to avoid creating multiple history states
// with minimal changes.
- const saveStateToHistoryRef = useRef(debounce((event: WorkflowHistoryEvent, meta?: WorkflowHistoryEventMeta) => {
+ const saveStateToHistoryRef = useRef(debounce((event: WorkflowHistoryEventT, meta?: WorkflowHistoryEventMeta) => {
workflowHistoryStore.setState({
workflowHistoryEvent: event,
workflowHistoryEventMeta: meta,
@@ -74,7 +76,7 @@ export const useWorkflowHistory = () => {
})
}, 500))
- const saveStateToHistory = useCallback((event: WorkflowHistoryEvent, meta?: WorkflowHistoryEventMeta) => {
+ const saveStateToHistory = useCallback((event: WorkflowHistoryEventT, meta?: WorkflowHistoryEventMeta) => {
switch (event) {
case WorkflowHistoryEvent.NoteChange:
// Hint: Note change does not trigger when note text changes,
@@ -105,7 +107,7 @@ export const useWorkflowHistory = () => {
}
}, [])
- const getHistoryLabel = useCallback((event: WorkflowHistoryEvent) => {
+ const getHistoryLabel = useCallback((event: WorkflowHistoryEventT) => {
switch (event) {
case WorkflowHistoryEvent.NodeTitleChange:
return t('workflow.changeHistory.nodeTitleChange')
diff --git a/web/app/components/workflow/hooks/use-workflow-interactions.ts b/web/app/components/workflow/hooks/use-workflow-interactions.ts
index 30e0d75151..697330ae47 100644
--- a/web/app/components/workflow/hooks/use-workflow-interactions.ts
+++ b/web/app/components/workflow/hooks/use-workflow-interactions.ts
@@ -10,7 +10,7 @@ import {
NODE_LAYOUT_VERTICAL_PADDING,
WORKFLOW_DATA_UPDATE,
} from '../constants'
-import type { Node, WorkflowDataUpdater } from '../types'
+import type { WorkflowDataUpdater } from '../types'
import { BlockEnum, ControlMode } from '../types'
import {
getLayoutByDagre,
@@ -18,6 +18,7 @@ import {
initialEdges,
initialNodes,
} from '../utils'
+import type { LayoutResult } from '../utils'
import {
useNodesReadOnly,
useSelectionInteractions,
@@ -111,10 +112,17 @@ export const useWorkflowOrganize = () => {
&& node.type === CUSTOM_NODE,
)
- const childLayoutsMap: Record = {}
- loopAndIterationNodes.forEach((node) => {
- childLayoutsMap[node.id] = getLayoutForChildNodes(node.id, nodes, edges)
- })
+ const childLayoutEntries = await Promise.all(
+ loopAndIterationNodes.map(async node => [
+ node.id,
+ await getLayoutForChildNodes(node.id, nodes, edges),
+ ] as const),
+ )
+ const childLayoutsMap = childLayoutEntries.reduce((acc, [nodeId, layout]) => {
+ if (layout)
+ acc[nodeId] = layout
+ return acc
+ }, {} as Record)
const containerSizeChanges: Record = {}
@@ -122,37 +130,20 @@ export const useWorkflowOrganize = () => {
const childLayout = childLayoutsMap[parentNode.id]
if (!childLayout) return
- let minX = Infinity
- let minY = Infinity
- let maxX = -Infinity
- let maxY = -Infinity
- let hasChildren = false
+ const {
+ bounds,
+ nodes: layoutNodes,
+ } = childLayout
- const childNodes = nodes.filter(node => node.parentId === parentNode.id)
+ if (!layoutNodes.size)
+ return
- childNodes.forEach((node) => {
- if (childLayout.node(node.id)) {
- hasChildren = true
- const childNodeWithPosition = childLayout.node(node.id)
+ const requiredWidth = (bounds.maxX - bounds.minX) + NODE_LAYOUT_HORIZONTAL_PADDING * 2
+ const requiredHeight = (bounds.maxY - bounds.minY) + NODE_LAYOUT_VERTICAL_PADDING * 2
- const nodeX = childNodeWithPosition.x - node.width! / 2
- const nodeY = childNodeWithPosition.y - node.height! / 2
-
- minX = Math.min(minX, nodeX)
- minY = Math.min(minY, nodeY)
- maxX = Math.max(maxX, nodeX + node.width!)
- maxY = Math.max(maxY, nodeY + node.height!)
- }
- })
-
- if (hasChildren) {
- const requiredWidth = maxX - minX + NODE_LAYOUT_HORIZONTAL_PADDING * 2
- const requiredHeight = maxY - minY + NODE_LAYOUT_VERTICAL_PADDING * 2
-
- containerSizeChanges[parentNode.id] = {
- width: Math.max(parentNode.width || 0, requiredWidth),
- height: Math.max(parentNode.height || 0, requiredHeight),
- }
+ containerSizeChanges[parentNode.id] = {
+ width: Math.max(parentNode.width || 0, requiredWidth),
+ height: Math.max(parentNode.height || 0, requiredHeight),
}
})
@@ -175,63 +166,65 @@ export const useWorkflowOrganize = () => {
})
})
- const layout = getLayoutByDagre(nodesWithUpdatedSizes, edges)
+ const layout = await getLayoutByDagre(nodesWithUpdatedSizes, edges)
- const rankMap = {} as Record
- nodesWithUpdatedSizes.forEach((node) => {
- if (!node.parentId && node.type === CUSTOM_NODE) {
- const rank = layout.node(node.id).rank!
-
- if (!rankMap[rank]) {
- rankMap[rank] = node
- }
- else {
- if (rankMap[rank].position.y > node.position.y)
- rankMap[rank] = node
+ // Build layer map for vertical alignment - nodes in the same layer should align
+ const layerMap = new Map()
+ layout.nodes.forEach((layoutInfo) => {
+ if (layoutInfo.layer !== undefined) {
+ const existing = layerMap.get(layoutInfo.layer)
+ const newLayerInfo = {
+ minY: existing ? Math.min(existing.minY, layoutInfo.y) : layoutInfo.y,
+ maxHeight: existing ? Math.max(existing.maxHeight, layoutInfo.height) : layoutInfo.height,
}
+ layerMap.set(layoutInfo.layer, newLayerInfo)
}
})
const newNodes = produce(nodesWithUpdatedSizes, (draft) => {
draft.forEach((node) => {
if (!node.parentId && node.type === CUSTOM_NODE) {
- const nodeWithPosition = layout.node(node.id)
+ const layoutInfo = layout.nodes.get(node.id)
+ if (!layoutInfo)
+ return
+
+ // Calculate vertical position with layer alignment
+ let yPosition = layoutInfo.y
+ if (layoutInfo.layer !== undefined) {
+ const layerInfo = layerMap.get(layoutInfo.layer)
+ if (layerInfo) {
+ // Align to the center of the tallest node in this layer
+ const layerCenterY = layerInfo.minY + layerInfo.maxHeight / 2
+ yPosition = layerCenterY - layoutInfo.height / 2
+ }
+ }
node.position = {
- x: nodeWithPosition.x - node.width! / 2,
- y: nodeWithPosition.y - node.height! / 2 + rankMap[nodeWithPosition.rank!].height! / 2,
+ x: layoutInfo.x,
+ y: yPosition,
}
}
})
loopAndIterationNodes.forEach((parentNode) => {
const childLayout = childLayoutsMap[parentNode.id]
- if (!childLayout) return
+ if (!childLayout)
+ return
const childNodes = draft.filter(node => node.parentId === parentNode.id)
+ const {
+ bounds,
+ nodes: layoutNodes,
+ } = childLayout
- let minX = Infinity
- let minY = Infinity
+ childNodes.forEach((childNode) => {
+ const layoutInfo = layoutNodes.get(childNode.id)
+ if (!layoutInfo)
+ return
- childNodes.forEach((node) => {
- if (childLayout.node(node.id)) {
- const childNodeWithPosition = childLayout.node(node.id)
- const nodeX = childNodeWithPosition.x - node.width! / 2
- const nodeY = childNodeWithPosition.y - node.height! / 2
-
- minX = Math.min(minX, nodeX)
- minY = Math.min(minY, nodeY)
- }
- })
-
- childNodes.forEach((node) => {
- if (childLayout.node(node.id)) {
- const childNodeWithPosition = childLayout.node(node.id)
-
- node.position = {
- x: NODE_LAYOUT_HORIZONTAL_PADDING + (childNodeWithPosition.x - node.width! / 2 - minX),
- y: NODE_LAYOUT_VERTICAL_PADDING + (childNodeWithPosition.y - node.height! / 2 - minY),
- }
+ childNode.position = {
+ x: NODE_LAYOUT_HORIZONTAL_PADDING + (layoutInfo.x - bounds.minX),
+ y: NODE_LAYOUT_VERTICAL_PADDING + (layoutInfo.y - bounds.minY),
}
})
})
diff --git a/web/app/components/workflow/hooks/use-workflow.ts b/web/app/components/workflow/hooks/use-workflow.ts
index bedc91d8df..609126cda8 100644
--- a/web/app/components/workflow/hooks/use-workflow.ts
+++ b/web/app/components/workflow/hooks/use-workflow.ts
@@ -322,7 +322,7 @@ export const useWorkflow = () => {
return startNodes
}, [nodesMap, getRootNodesById])
- const isValidConnection = useCallback(({ source, sourceHandle, target }: Connection) => {
+ const isValidConnection = useCallback(({ source, sourceHandle: _sourceHandle, target }: Connection) => {
const { nodes, edges } = collaborativeWorkflow.getState()
const sourceNode: Node = nodes.find(node => node.id === source)!
const targetNode: Node = nodes.find(node => node.id === target)!
diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx
index 708c9a5c1d..e078b41b48 100644
--- a/web/app/components/workflow/index.tsx
+++ b/web/app/components/workflow/index.tsx
@@ -532,10 +532,10 @@ export const Workflow: FC = memo(({
nodesFocusable={!nodesReadOnly}
edgesFocusable={!nodesReadOnly}
panOnScroll={false}
- panOnDrag={controlMode === ControlMode.Hand && !workflowReadOnly}
- zoomOnPinch={!workflowReadOnly}
- zoomOnScroll={!workflowReadOnly}
- zoomOnDoubleClick={!workflowReadOnly}
+ panOnDrag={controlMode === ControlMode.Hand}
+ zoomOnPinch={true}
+ zoomOnScroll={true}
+ zoomOnDoubleClick={true}
isValidConnection={isValidConnection}
selectionKeyCode={null}
selectionMode={SelectionMode.Partial}
diff --git a/web/app/components/workflow/nodes/_base/components/retry/utils.ts b/web/app/components/workflow/nodes/_base/components/retry/utils.ts
index e69de29bb2..336ce12bb9 100644
--- a/web/app/components/workflow/nodes/_base/components/retry/utils.ts
+++ b/web/app/components/workflow/nodes/_base/components/retry/utils.ts
@@ -0,0 +1 @@
+export {}
diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx
index 273e100f8e..4d74e09fde 100644
--- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx
+++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx
@@ -127,7 +127,7 @@ const VarReferencePicker: FC = ({
const reactflow = useReactFlow()
- const startNode = availableNodes.find((node: any) => {
+ const startNode = availableNodes.find((node: Node) => {
return node.data.type === BlockEnum.Start
})
@@ -407,7 +407,10 @@ const VarReferencePicker: FC = ({
{
if (readonly)
return
- !isConstant ? setOpen(!open) : setControlFocus(Date.now())
+ if (!isConstant)
+ setOpen(!open)
+ else
+ setControlFocus(Date.now())
}} className='group/picker-trigger-wrap relative !flex'>
<>
{isAddBtnTrigger
@@ -457,7 +460,10 @@ const VarReferencePicker: FC = ({
onClick={() => {
if (readonly)
return
- !isConstant ? setOpen(!open) : setControlFocus(Date.now())
+ if (!isConstant)
+ setOpen(!open)
+ else
+ setControlFocus(Date.now())
}}
className='h-full grow'
>
diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx
index 067dbf8652..e70cfed97c 100644
--- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx
+++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx
@@ -137,7 +137,7 @@ const Item: FC = ({
const isHovering = isItemHovering || isChildrenHovering
const open = (isObj || isStructureOutput) && isHovering
useEffect(() => {
- onHovering && onHovering(isHovering)
+ onHovering?.(isHovering)
}, [isHovering])
const handleChosen = (e: React.MouseEvent) => {
e.stopPropagation()
diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx
index 06962389c9..b26dd74714 100644
--- a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx
+++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx
@@ -25,12 +25,12 @@ type Props = {
} & Partial
const LastRun: FC = ({
- appId,
+ appId: _appId,
nodeId,
canSingleRun,
isRunAfterSingleRun,
updateNodeRunningStatus,
- nodeInfo,
+ nodeInfo: _nodeInfo,
runningStatus: oneStepRunRunningStatus,
onSingleRunClicked,
singleRunResult,
diff --git a/web/app/components/workflow/nodes/http/components/timeout/index.tsx b/web/app/components/workflow/nodes/http/components/timeout/index.tsx
index 40ebab0e2a..bb84091d67 100644
--- a/web/app/components/workflow/nodes/http/components/timeout/index.tsx
+++ b/web/app/components/workflow/nodes/http/components/timeout/index.tsx
@@ -5,6 +5,8 @@ import { useTranslation } from 'react-i18next'
import type { Timeout as TimeoutPayloadType } from '../../types'
import Input from '@/app/components/base/input'
import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse'
+import { useStore } from '@/app/components/workflow/store'
+import { BlockEnum } from '@/app/components/workflow/types'
type Props = {
readonly: boolean
@@ -61,6 +63,11 @@ const Timeout: FC = ({ readonly, payload, onChange }) => {
const { t } = useTranslation()
const { connect, read, write, max_connect_timeout, max_read_timeout, max_write_timeout } = payload ?? {}
+ // Get default config from store for max timeout values
+ const nodesDefaultConfigs = useStore(s => s.nodesDefaultConfigs)
+ const defaultConfig = nodesDefaultConfigs?.[BlockEnum.HttpRequest]
+ const defaultTimeout = defaultConfig?.timeout || {}
+
return (
@@ -73,7 +80,7 @@ const Timeout: FC
= ({ readonly, payload, onChange }) => {
value={connect}
onChange={v => onChange?.({ ...payload, connect: v })}
min={1}
- max={max_connect_timeout || 300}
+ max={max_connect_timeout || defaultTimeout.max_connect_timeout || 10}
/>
= ({ readonly, payload, onChange }) => {
value={read}
onChange={v => onChange?.({ ...payload, read: v })}
min={1}
- max={max_read_timeout || 600}
+ max={max_read_timeout || defaultTimeout.max_read_timeout || 600}
/>
= ({ readonly, payload, onChange }) => {
value={write}
onChange={v => onChange?.({ ...payload, write: v })}
min={1}
- max={max_write_timeout || 600}
+ max={max_write_timeout || defaultTimeout.max_write_timeout || 600}
/>
diff --git a/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx b/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx
index 789e24835f..99ee0d25b5 100644
--- a/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx
+++ b/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx
@@ -88,7 +88,8 @@ const OptionCard = memo(({
)}
onClick={(e) => {
e.stopPropagation()
- !readonly && enableSelect && id && onClick?.(id)
+ if (!readonly && enableSelect && id)
+ onClick?.(id)
}}
>
= ({
setJson(JSON.stringify(schema, null, 2))
}, [currentTab])
- const handleSubmit = useCallback((schema: any) => {
+ const handleSubmit = useCallback((schema: Record
) => {
const jsonSchema = jsonToSchema(schema) as SchemaRoot
if (currentTab === SchemaView.VisualEditor)
setJsonSchema(jsonSchema)
@@ -139,8 +139,10 @@ const JsonSchemaConfig: FC = ({
const handleResetDefaults = useCallback(() => {
if (currentTab === SchemaView.VisualEditor) {
setHoveringProperty(null)
- advancedEditing && setAdvancedEditing(false)
- isAddingNewField && setIsAddingNewField(false)
+ if (advancedEditing)
+ setAdvancedEditing(false)
+ if (isAddingNewField)
+ setIsAddingNewField(false)
}
setJsonSchema(DEFAULT_SCHEMA)
setJson(JSON.stringify(DEFAULT_SCHEMA, null, 2))
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx
index ae72d494d1..4aa0f99d3f 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx
@@ -87,8 +87,10 @@ const EditCard: FC = ({
})
useSubscribe('fieldChangeSuccess', () => {
- isAddingNewField && setIsAddingNewField(false)
- advancedEditing && setAdvancedEditing(false)
+ if (isAddingNewField)
+ setIsAddingNewField(false)
+ if (advancedEditing)
+ setAdvancedEditing(false)
})
const emitPropertyNameChange = useCallback(() => {
@@ -150,14 +152,16 @@ const EditCard: FC = ({
}, [isAdvancedEditing, emitPropertyOptionsChange, currentFields])
const handleAdvancedOptionsChange = useCallback((options: AdvancedOptionsType) => {
- let enumValue: any = options.enum
- if (enumValue === '') {
+ let enumValue: SchemaEnumType | undefined
+ if (options.enum === '') {
enumValue = undefined
}
else {
- enumValue = options.enum.replace(/\s/g, '').split(',')
+ const stringArray = options.enum.replace(/\s/g, '').split(',')
if (currentFields.type === Type.number)
- enumValue = (enumValue as SchemaEnumType).map(value => Number(value)).filter(num => !Number.isNaN(num))
+ enumValue = stringArray.map(value => Number(value)).filter(num => !Number.isNaN(num))
+ else
+ enumValue = stringArray
}
setCurrentFields(prev => ({ ...prev, enum: enumValue }))
if (isAdvancedEditing) return
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts
index 8256a3c862..4f7e1e6f00 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts
@@ -45,8 +45,10 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => {
onChange(backupSchema)
setBackupSchema(null)
}
- isAddingNewField && setIsAddingNewField(false)
- advancedEditing && setAdvancedEditing(false)
+ if (isAddingNewField)
+ setIsAddingNewField(false)
+ if (advancedEditing)
+ setAdvancedEditing(false)
setHoveringProperty(null)
})
@@ -221,7 +223,8 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => {
})
useSubscribe('addField', (params) => {
- advancedEditing && setAdvancedEditing(false)
+ if (advancedEditing)
+ setAdvancedEditing(false)
setBackupSchema(jsonSchema)
const { path } = params as AddEventParams
setIsAddingNewField(true)
diff --git a/web/app/components/workflow/nodes/llm/panel.tsx b/web/app/components/workflow/nodes/llm/panel.tsx
index 03569f6f7a..cd79b9f3d9 100644
--- a/web/app/components/workflow/nodes/llm/panel.tsx
+++ b/web/app/components/workflow/nodes/llm/panel.tsx
@@ -293,6 +293,11 @@ const Panel: FC> = ({
type='string'
description={t(`${i18nPrefix}.outputVars.output`)}
/>
+
{
diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx
index 07eb79a269..9d19b61093 100644
--- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx
+++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx
@@ -35,7 +35,8 @@ const VariableModalTrigger = ({
open={open}
onOpenChange={() => {
setOpen(v => !v)
- open && onClose()
+ if (open)
+ onClose()
}}
placement='left-start'
offset={{
@@ -45,7 +46,8 @@ const VariableModalTrigger = ({
>
{
setOpen(v => !v)
- open && onClose()
+ if (open)
+ onClose()
}}>
diff --git a/web/app/components/workflow/panel/env-panel/variable-trigger.tsx b/web/app/components/workflow/panel/env-panel/variable-trigger.tsx
index 52474860b1..604fceef81 100644
--- a/web/app/components/workflow/panel/env-panel/variable-trigger.tsx
+++ b/web/app/components/workflow/panel/env-panel/variable-trigger.tsx
@@ -33,7 +33,8 @@ const VariableTrigger = ({
open={open}
onOpenChange={() => {
setOpen(v => !v)
- open && onClose()
+ if (open)
+ onClose()
}}
placement='left-start'
offset={{
@@ -43,7 +44,8 @@ const VariableTrigger = ({
>
{
setOpen(v => !v)
- open && onClose()
+ if (open)
+ onClose()
}}>
diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx
index 2c5cc165c1..2e9ae392a6 100644
--- a/web/app/components/workflow/run/index.tsx
+++ b/web/app/components/workflow/run/index.tsx
@@ -81,9 +81,12 @@ const RunPanel: FC = ({
const switchTab = async (tab: string) => {
setCurrentTab(tab)
- if (tab === 'RESULT')
- runDetailUrl && await getResult()
- tracingListUrl && await getTracingList()
+ if (tab === 'RESULT') {
+ if (runDetailUrl)
+ await getResult()
+ }
+ if (tracingListUrl)
+ await getTracingList()
}
useEffect(() => {
diff --git a/web/app/components/workflow/utils/dagre-layout.ts b/web/app/components/workflow/utils/dagre-layout.ts
deleted file mode 100644
index 5eafe77586..0000000000
--- a/web/app/components/workflow/utils/dagre-layout.ts
+++ /dev/null
@@ -1,246 +0,0 @@
-import dagre from '@dagrejs/dagre'
-import {
- cloneDeep,
-} from 'lodash-es'
-import type {
- Edge,
- Node,
-} from '../types'
-import {
- BlockEnum,
-} from '../types'
-import {
- CUSTOM_NODE,
- NODE_LAYOUT_HORIZONTAL_PADDING,
- NODE_LAYOUT_MIN_DISTANCE,
- NODE_LAYOUT_VERTICAL_PADDING,
-} from '../constants'
-import { CUSTOM_ITERATION_START_NODE } from '../nodes/iteration-start/constants'
-import { CUSTOM_LOOP_START_NODE } from '../nodes/loop-start/constants'
-
-export const getLayoutByDagre = (originNodes: Node[], originEdges: Edge[]) => {
- const dagreGraph = new dagre.graphlib.Graph({ compound: true })
- dagreGraph.setDefaultEdgeLabel(() => ({}))
-
- const nodes = cloneDeep(originNodes).filter(node => !node.parentId && node.type === CUSTOM_NODE)
- const edges = cloneDeep(originEdges).filter(edge => (!edge.data?.isInIteration && !edge.data?.isInLoop))
-
-// The default dagre layout algorithm often fails to correctly order the branches
-// of an If/Else node, leading to crossed edges.
-//
-// To solve this, we employ a "virtual container" strategy:
-// 1. A virtual, compound parent node (the "container") is created for each If/Else node's branches.
-// 2. Each direct child of the If/Else node is preceded by a virtual dummy node. These dummies are placed inside the container.
-// 3. A rigid, sequential chain of invisible edges is created between these dummy nodes (e.g., dummy_IF -> dummy_ELIF -> dummy_ELSE).
-//
-// This forces dagre to treat the ordered branches as an unbreakable, atomic group,
-// ensuring their layout respects the intended logical sequence.
- const ifElseNodes = nodes.filter(node => node.data.type === BlockEnum.IfElse)
- let virtualLogicApplied = false
-
- ifElseNodes.forEach((ifElseNode) => {
- const childEdges = edges.filter(e => e.source === ifElseNode.id)
- if (childEdges.length <= 1)
- return
-
- virtualLogicApplied = true
- const sortedChildEdges = childEdges.sort((edgeA, edgeB) => {
- const handleA = edgeA.sourceHandle
- const handleB = edgeB.sourceHandle
-
- if (handleA && handleB) {
- const cases = (ifElseNode.data as any).cases || []
- const isAElse = handleA === 'false'
- const isBElse = handleB === 'false'
-
- if (isAElse) return 1
- if (isBElse) return -1
-
- const indexA = cases.findIndex((c: any) => c.case_id === handleA)
- const indexB = cases.findIndex((c: any) => c.case_id === handleB)
-
- if (indexA !== -1 && indexB !== -1)
- return indexA - indexB
- }
- return 0
- })
-
- const parentDummyId = `dummy-parent-${ifElseNode.id}`
- dagreGraph.setNode(parentDummyId, { width: 1, height: 1 })
-
- const dummyNodes: string[] = []
- sortedChildEdges.forEach((edge) => {
- const dummyNodeId = `dummy-${edge.source}-${edge.target}`
- dummyNodes.push(dummyNodeId)
- dagreGraph.setNode(dummyNodeId, { width: 1, height: 1 })
- dagreGraph.setParent(dummyNodeId, parentDummyId)
-
- const edgeIndex = edges.findIndex(e => e.id === edge.id)
- if (edgeIndex > -1)
- edges.splice(edgeIndex, 1)
-
- edges.push({ id: `e-${edge.source}-${dummyNodeId}`, source: edge.source, target: dummyNodeId, sourceHandle: edge.sourceHandle } as Edge)
- edges.push({ id: `e-${dummyNodeId}-${edge.target}`, source: dummyNodeId, target: edge.target, targetHandle: edge.targetHandle } as Edge)
- })
-
- for (let i = 0; i < dummyNodes.length - 1; i++) {
- const sourceDummy = dummyNodes[i]
- const targetDummy = dummyNodes[i + 1]
- edges.push({ id: `e-dummy-${sourceDummy}-${targetDummy}`, source: sourceDummy, target: targetDummy } as Edge)
- }
- })
-
- dagreGraph.setGraph({
- rankdir: 'LR',
- align: 'UL',
- nodesep: 40,
- ranksep: virtualLogicApplied ? 30 : 60,
- ranker: 'tight-tree',
- marginx: 30,
- marginy: 200,
- })
-
- nodes.forEach((node) => {
- dagreGraph.setNode(node.id, {
- width: node.width!,
- height: node.height!,
- })
- })
- edges.forEach((edge) => {
- dagreGraph.setEdge(edge.source, edge.target)
- })
- dagre.layout(dagreGraph)
- return dagreGraph
-}
-
-export const getLayoutForChildNodes = (parentNodeId: string, originNodes: Node[], originEdges: Edge[]) => {
- const dagreGraph = new dagre.graphlib.Graph()
- dagreGraph.setDefaultEdgeLabel(() => ({}))
-
- const nodes = cloneDeep(originNodes).filter(node => node.parentId === parentNodeId)
- const edges = cloneDeep(originEdges).filter(edge =>
- (edge.data?.isInIteration && edge.data?.iteration_id === parentNodeId)
- || (edge.data?.isInLoop && edge.data?.loop_id === parentNodeId),
- )
-
- const startNode = nodes.find(node =>
- node.type === CUSTOM_ITERATION_START_NODE
- || node.type === CUSTOM_LOOP_START_NODE
- || node.data?.type === BlockEnum.LoopStart
- || node.data?.type === BlockEnum.IterationStart,
- )
-
- if (!startNode) {
- dagreGraph.setGraph({
- rankdir: 'LR',
- align: 'UL',
- nodesep: 40,
- ranksep: 60,
- marginx: NODE_LAYOUT_HORIZONTAL_PADDING,
- marginy: NODE_LAYOUT_VERTICAL_PADDING,
- })
-
- nodes.forEach((node) => {
- dagreGraph.setNode(node.id, {
- width: node.width || 244,
- height: node.height || 100,
- })
- })
-
- edges.forEach((edge) => {
- dagreGraph.setEdge(edge.source, edge.target)
- })
-
- dagre.layout(dagreGraph)
- return dagreGraph
- }
-
- const startNodeOutEdges = edges.filter(edge => edge.source === startNode.id)
- const firstConnectedNodes = startNodeOutEdges.map(edge =>
- nodes.find(node => node.id === edge.target),
- ).filter(Boolean) as Node[]
-
- const nonStartNodes = nodes.filter(node => node.id !== startNode.id)
- const nonStartEdges = edges.filter(edge => edge.source !== startNode.id && edge.target !== startNode.id)
-
- dagreGraph.setGraph({
- rankdir: 'LR',
- align: 'UL',
- nodesep: 40,
- ranksep: 60,
- marginx: NODE_LAYOUT_HORIZONTAL_PADDING / 2,
- marginy: NODE_LAYOUT_VERTICAL_PADDING / 2,
- })
-
- nonStartNodes.forEach((node) => {
- dagreGraph.setNode(node.id, {
- width: node.width || 244,
- height: node.height || 100,
- })
- })
-
- nonStartEdges.forEach((edge) => {
- dagreGraph.setEdge(edge.source, edge.target)
- })
-
- dagre.layout(dagreGraph)
-
- const startNodeSize = {
- width: startNode.width || 44,
- height: startNode.height || 48,
- }
-
- const startNodeX = NODE_LAYOUT_HORIZONTAL_PADDING / 1.5
- let startNodeY = 100
-
- let minFirstLayerX = Infinity
- let avgFirstLayerY = 0
- let firstLayerCount = 0
-
- if (firstConnectedNodes.length > 0) {
- firstConnectedNodes.forEach((node) => {
- if (dagreGraph.node(node.id)) {
- const nodePos = dagreGraph.node(node.id)
- avgFirstLayerY += nodePos.y
- firstLayerCount++
- minFirstLayerX = Math.min(minFirstLayerX, nodePos.x - nodePos.width / 2)
- }
- })
-
- if (firstLayerCount > 0) {
- avgFirstLayerY /= firstLayerCount
- startNodeY = avgFirstLayerY
- }
-
- const minRequiredX = startNodeX + startNodeSize.width + NODE_LAYOUT_MIN_DISTANCE
-
- if (minFirstLayerX < minRequiredX) {
- const shiftX = minRequiredX - minFirstLayerX
-
- nonStartNodes.forEach((node) => {
- if (dagreGraph.node(node.id)) {
- const nodePos = dagreGraph.node(node.id)
- dagreGraph.setNode(node.id, {
- x: nodePos.x + shiftX,
- y: nodePos.y,
- width: nodePos.width,
- height: nodePos.height,
- })
- }
- })
- }
- }
-
- dagreGraph.setNode(startNode.id, {
- x: startNodeX + startNodeSize.width / 2,
- y: startNodeY,
- width: startNodeSize.width,
- height: startNodeSize.height,
- })
-
- startNodeOutEdges.forEach((edge) => {
- dagreGraph.setEdge(edge.source, edge.target)
- })
-
- return dagreGraph
-}
diff --git a/web/app/components/workflow/utils/index.ts b/web/app/components/workflow/utils/index.ts
index ab59f513bc..e9ae2d1ef0 100644
--- a/web/app/components/workflow/utils/index.ts
+++ b/web/app/components/workflow/utils/index.ts
@@ -1,7 +1,7 @@
export * from './node'
export * from './edge'
export * from './workflow-init'
-export * from './dagre-layout'
+export * from './layout'
export * from './common'
export * from './tool'
export * from './workflow'
diff --git a/web/app/components/workflow/utils/layout.ts b/web/app/components/workflow/utils/layout.ts
new file mode 100644
index 0000000000..b3cf3b0d88
--- /dev/null
+++ b/web/app/components/workflow/utils/layout.ts
@@ -0,0 +1,529 @@
+import ELK from 'elkjs/lib/elk.bundled.js'
+import type { ElkNode, LayoutOptions } from 'elkjs/lib/elk-api'
+import { cloneDeep } from 'lodash-es'
+import type {
+ Edge,
+ Node,
+} from '../types'
+import {
+ BlockEnum,
+} from '../types'
+import {
+ CUSTOM_NODE,
+ NODE_LAYOUT_HORIZONTAL_PADDING,
+ NODE_LAYOUT_VERTICAL_PADDING,
+} from '../constants'
+import { CUSTOM_ITERATION_START_NODE } from '../nodes/iteration-start/constants'
+import { CUSTOM_LOOP_START_NODE } from '../nodes/loop-start/constants'
+import type { CaseItem, IfElseNodeType } from '../nodes/if-else/types'
+
+// Although the file name refers to Dagre, the implementation now relies on ELK's layered algorithm.
+// Keep the export signatures unchanged to minimise the blast radius while we migrate the layout stack.
+
+const elk = new ELK()
+
+const DEFAULT_NODE_WIDTH = 244
+const DEFAULT_NODE_HEIGHT = 100
+
+const ROOT_LAYOUT_OPTIONS = {
+ 'elk.algorithm': 'layered',
+ 'elk.direction': 'RIGHT',
+
+ // === Spacing - Maximum spacing to prevent any overlap ===
+ 'elk.layered.spacing.nodeNodeBetweenLayers': '100',
+ 'elk.spacing.nodeNode': '80',
+ 'elk.spacing.edgeNode': '50',
+ 'elk.spacing.edgeEdge': '30',
+ 'elk.spacing.edgeLabel': '10',
+ 'elk.spacing.portPort': '20',
+
+ // === Port Configuration ===
+ 'elk.portConstraints': 'FIXED_ORDER',
+ 'elk.layered.considerModelOrder.strategy': 'PREFER_EDGES',
+ 'elk.port.side': 'SOUTH',
+
+ // === Node Placement - Best quality ===
+ 'elk.layered.nodePlacement.strategy': 'NETWORK_SIMPLEX',
+ 'elk.layered.nodePlacement.favorStraightEdges': 'true',
+ 'elk.layered.nodePlacement.linearSegments.deflectionDampening': '0.5',
+ 'elk.layered.nodePlacement.networkSimplex.nodeFlexibility': 'NODE_SIZE',
+
+ // === Edge Routing - Maximum quality ===
+ 'elk.edgeRouting': 'SPLINES',
+ 'elk.layered.edgeRouting.selfLoopPlacement': 'NORTH',
+ 'elk.layered.edgeRouting.sloppySplineRouting': 'false',
+ 'elk.layered.edgeRouting.splines.mode': 'CONSERVATIVE',
+ 'elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor': '1.2',
+
+ // === Crossing Minimization - Most aggressive ===
+ 'elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP',
+ 'elk.layered.crossingMinimization.greedySwitch.type': 'TWO_SIDED',
+ 'elk.layered.crossingMinimization.greedySwitchHierarchical.type': 'TWO_SIDED',
+ 'elk.layered.crossingMinimization.semiInteractive': 'true',
+ 'elk.layered.crossingMinimization.hierarchicalSweepiness': '0.9',
+
+ // === Layering Strategy - Best quality ===
+ 'elk.layered.layering.strategy': 'NETWORK_SIMPLEX',
+ 'elk.layered.layering.networkSimplex.nodeFlexibility': 'NODE_SIZE',
+ 'elk.layered.layering.layerConstraint': 'NONE',
+ 'elk.layered.layering.minWidth.upperBoundOnWidth': '4',
+
+ // === Cycle Breaking ===
+ 'elk.layered.cycleBreaking.strategy': 'DEPTH_FIRST',
+
+ // === Connected Components ===
+ 'elk.separateConnectedComponents': 'true',
+ 'elk.spacing.componentComponent': '100',
+
+ // === Node Size Constraints ===
+ 'elk.nodeSize.constraints': 'NODE_LABELS',
+ 'elk.nodeSize.options': 'DEFAULT_MINIMUM_SIZE MINIMUM_SIZE_ACCOUNTS_FOR_PADDING',
+
+ // === Edge Label Placement ===
+ 'elk.edgeLabels.placement': 'CENTER',
+ 'elk.edgeLabels.inline': 'true',
+
+ // === Compaction ===
+ 'elk.layered.compaction.postCompaction.strategy': 'EDGE_LENGTH',
+ 'elk.layered.compaction.postCompaction.constraints': 'EDGE_LENGTH',
+
+ // === High-Quality Mode ===
+ 'elk.layered.thoroughness': '10',
+ 'elk.layered.wrapping.strategy': 'OFF',
+ 'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
+
+ // === Additional Optimizations ===
+ 'elk.layered.feedbackEdges': 'true',
+ 'elk.layered.mergeEdges': 'false',
+ 'elk.layered.mergeHierarchyEdges': 'false',
+ 'elk.layered.allowNonFlowPortsToSwitchSides': 'false',
+ 'elk.layered.northOrSouthPort': 'false',
+ 'elk.partitioning.activate': 'false',
+ 'elk.junctionPoints': 'true',
+
+ // === Content Alignment ===
+ 'elk.contentAlignment': 'V_TOP H_LEFT',
+ 'elk.alignment': 'AUTOMATIC',
+}
+
+const CHILD_LAYOUT_OPTIONS = {
+ 'elk.algorithm': 'layered',
+ 'elk.direction': 'RIGHT',
+
+ // === Spacing - High quality for child nodes ===
+ 'elk.layered.spacing.nodeNodeBetweenLayers': '80',
+ 'elk.spacing.nodeNode': '60',
+ 'elk.spacing.edgeNode': '40',
+ 'elk.spacing.edgeEdge': '25',
+ 'elk.spacing.edgeLabel': '8',
+ 'elk.spacing.portPort': '15',
+
+ // === Node Placement - Best quality ===
+ 'elk.layered.nodePlacement.strategy': 'NETWORK_SIMPLEX',
+ 'elk.layered.nodePlacement.favorStraightEdges': 'true',
+ 'elk.layered.nodePlacement.linearSegments.deflectionDampening': '0.5',
+ 'elk.layered.nodePlacement.networkSimplex.nodeFlexibility': 'NODE_SIZE',
+
+ // === Edge Routing - Maximum quality ===
+ 'elk.edgeRouting': 'SPLINES',
+ 'elk.layered.edgeRouting.sloppySplineRouting': 'false',
+ 'elk.layered.edgeRouting.splines.mode': 'CONSERVATIVE',
+
+ // === Crossing Minimization - Aggressive ===
+ 'elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP',
+ 'elk.layered.crossingMinimization.greedySwitch.type': 'TWO_SIDED',
+ 'elk.layered.crossingMinimization.semiInteractive': 'true',
+
+ // === Layering Strategy ===
+ 'elk.layered.layering.strategy': 'NETWORK_SIMPLEX',
+ 'elk.layered.layering.networkSimplex.nodeFlexibility': 'NODE_SIZE',
+
+ // === Cycle Breaking ===
+ 'elk.layered.cycleBreaking.strategy': 'DEPTH_FIRST',
+
+ // === Node Size ===
+ 'elk.nodeSize.constraints': 'NODE_LABELS',
+
+ // === Compaction ===
+ 'elk.layered.compaction.postCompaction.strategy': 'EDGE_LENGTH',
+
+ // === High-Quality Mode ===
+ 'elk.layered.thoroughness': '10',
+ 'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
+
+ // === Additional Optimizations ===
+ 'elk.layered.feedbackEdges': 'true',
+ 'elk.layered.mergeEdges': 'false',
+ 'elk.junctionPoints': 'true',
+}
+
+type LayoutInfo = {
+ x: number
+ y: number
+ width: number
+ height: number
+ layer?: number
+}
+
+type LayoutBounds = {
+ minX: number
+ minY: number
+ maxX: number
+ maxY: number
+}
+
+export type LayoutResult = {
+ nodes: Map
+ bounds: LayoutBounds
+}
+
+// ELK Port definition for native port support
+type ElkPortShape = {
+ id: string
+ layoutOptions?: LayoutOptions
+}
+
+type ElkNodeShape = {
+ id: string
+ width: number
+ height: number
+ ports?: ElkPortShape[]
+ layoutOptions?: LayoutOptions
+ children?: ElkNodeShape[]
+}
+
+type ElkEdgeShape = {
+ id: string
+ sources: string[]
+ targets: string[]
+ sourcePort?: string
+ targetPort?: string
+}
+
+const toElkNode = (node: Node): ElkNodeShape => ({
+ id: node.id,
+ width: node.width ?? DEFAULT_NODE_WIDTH,
+ height: node.height ?? DEFAULT_NODE_HEIGHT,
+})
+
+let edgeCounter = 0
+const nextEdgeId = () => `elk-edge-${edgeCounter++}`
+
+const createEdge = (
+ source: string,
+ target: string,
+ sourcePort?: string,
+ targetPort?: string,
+): ElkEdgeShape => ({
+ id: nextEdgeId(),
+ sources: [source],
+ targets: [target],
+ sourcePort,
+ targetPort,
+})
+
+const collectLayout = (graph: ElkNode, predicate: (id: string) => boolean): LayoutResult => {
+ const result = new Map()
+ let minX = Infinity
+ let minY = Infinity
+ let maxX = -Infinity
+ let maxY = -Infinity
+
+ const visit = (node: ElkNode) => {
+ node.children?.forEach((child: ElkNode) => {
+ if (predicate(child.id)) {
+ const x = child.x ?? 0
+ const y = child.y ?? 0
+ const width = child.width ?? DEFAULT_NODE_WIDTH
+ const height = child.height ?? DEFAULT_NODE_HEIGHT
+ const layer = child?.layoutOptions?.['org.eclipse.elk.layered.layerIndex']
+
+ result.set(child.id, {
+ x,
+ y,
+ width,
+ height,
+ layer: layer ? Number.parseInt(layer) : undefined,
+ })
+
+ minX = Math.min(minX, x)
+ minY = Math.min(minY, y)
+ maxX = Math.max(maxX, x + width)
+ maxY = Math.max(maxY, y + height)
+ }
+
+ if (child.children?.length)
+ visit(child)
+ })
+ }
+
+ visit(graph)
+
+ if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
+ minX = 0
+ minY = 0
+ maxX = 0
+ maxY = 0
+ }
+
+ return {
+ nodes: result,
+ bounds: {
+ minX,
+ minY,
+ maxX,
+ maxY,
+ },
+ }
+}
+
+/**
+ * Build If/Else node with ELK native Ports instead of dummy nodes
+ * This is the recommended approach for handling multiple branches
+ */
+const buildIfElseWithPorts = (
+ ifElseNode: Node,
+ edges: Edge[],
+): { node: ElkNodeShape; portMap: Map } | null => {
+ const childEdges = edges.filter(edge => edge.source === ifElseNode.id)
+
+ if (childEdges.length <= 1)
+ return null
+
+ // Sort child edges according to case order
+ const sortedChildEdges = [...childEdges].sort((edgeA, edgeB) => {
+ const handleA = edgeA.sourceHandle
+ const handleB = edgeB.sourceHandle
+
+ if (handleA && handleB) {
+ const cases = (ifElseNode.data as IfElseNodeType).cases || []
+ const isAElse = handleA === 'false'
+ const isBElse = handleB === 'false'
+
+ if (isAElse)
+ return 1
+ if (isBElse)
+ return -1
+
+ const indexA = cases.findIndex((c: CaseItem) => c.case_id === handleA)
+ const indexB = cases.findIndex((c: CaseItem) => c.case_id === handleB)
+
+ if (indexA !== -1 && indexB !== -1)
+ return indexA - indexB
+ }
+
+ return 0
+ })
+
+ // Create ELK ports for each branch
+ const ports: ElkPortShape[] = sortedChildEdges.map((edge, index) => ({
+ id: `${ifElseNode.id}-port-${edge.sourceHandle || index}`,
+ layoutOptions: {
+ 'port.side': 'EAST', // Ports on the right side (matching 'RIGHT' direction)
+ 'port.index': String(index),
+ },
+ }))
+
+ // Build port mapping: sourceHandle -> portId
+ const portMap = new Map()
+ sortedChildEdges.forEach((edge, index) => {
+ const portId = `${ifElseNode.id}-port-${edge.sourceHandle || index}`
+ portMap.set(edge.id, portId)
+ })
+
+ return {
+ node: {
+ id: ifElseNode.id,
+ width: ifElseNode.width ?? DEFAULT_NODE_WIDTH,
+ height: ifElseNode.height ?? DEFAULT_NODE_HEIGHT,
+ ports,
+ layoutOptions: {
+ 'elk.portConstraints': 'FIXED_ORDER',
+ },
+ },
+ portMap,
+ }
+}
+
+const normaliseBounds = (layout: LayoutResult): LayoutResult => {
+ const {
+ nodes,
+ bounds,
+ } = layout
+
+ if (nodes.size === 0)
+ return layout
+
+ const offsetX = bounds.minX
+ const offsetY = bounds.minY
+
+ const adjustedNodes = new Map()
+ nodes.forEach((info, id) => {
+ adjustedNodes.set(id, {
+ ...info,
+ x: info.x - offsetX,
+ y: info.y - offsetY,
+ })
+ })
+
+ return {
+ nodes: adjustedNodes,
+ bounds: {
+ minX: 0,
+ minY: 0,
+ maxX: bounds.maxX - offsetX,
+ maxY: bounds.maxY - offsetY,
+ },
+ }
+}
+
+export const getLayoutByDagre = async (originNodes: Node[], originEdges: Edge[]): Promise => {
+ edgeCounter = 0
+ const nodes = cloneDeep(originNodes).filter(node => !node.parentId && node.type === CUSTOM_NODE)
+ const edges = cloneDeep(originEdges).filter(edge => (!edge.data?.isInIteration && !edge.data?.isInLoop))
+
+ const elkNodes: ElkNodeShape[] = []
+ const elkEdges: ElkEdgeShape[] = []
+
+ // Track which edges have been processed for If/Else nodes with ports
+ const edgeToPortMap = new Map()
+
+ // Build nodes with ports for If/Else nodes
+ nodes.forEach((node) => {
+ if (node.data.type === BlockEnum.IfElse) {
+ const portsResult = buildIfElseWithPorts(node, edges)
+ if (portsResult) {
+ // Use node with ports
+ elkNodes.push(portsResult.node)
+ // Store port mappings for edges
+ portsResult.portMap.forEach((portId, edgeId) => {
+ edgeToPortMap.set(edgeId, portId)
+ })
+ }
+ else {
+ // No multiple branches, use normal node
+ elkNodes.push(toElkNode(node))
+ }
+ }
+ else {
+ elkNodes.push(toElkNode(node))
+ }
+ })
+
+ // Build edges with port connections
+ edges.forEach((edge) => {
+ const sourcePort = edgeToPortMap.get(edge.id)
+ elkEdges.push(createEdge(edge.source, edge.target, sourcePort))
+ })
+
+ const graph = {
+ id: 'workflow-root',
+ layoutOptions: ROOT_LAYOUT_OPTIONS,
+ children: elkNodes,
+ edges: elkEdges,
+ }
+
+ const layoutedGraph = await elk.layout(graph)
+ // No need to filter dummy nodes anymore, as we're using ports
+ const layout = collectLayout(layoutedGraph, () => true)
+ return normaliseBounds(layout)
+}
+
+const normaliseChildLayout = (
+ layout: LayoutResult,
+ nodes: Node[],
+): LayoutResult => {
+ const result = new Map()
+ layout.nodes.forEach((info, id) => {
+ result.set(id, info)
+ })
+
+ // Ensure iteration / loop start nodes do not collapse into the children.
+ const startNode = nodes.find(node =>
+ node.type === CUSTOM_ITERATION_START_NODE
+ || node.type === CUSTOM_LOOP_START_NODE
+ || node.data?.type === BlockEnum.LoopStart
+ || node.data?.type === BlockEnum.IterationStart,
+ )
+
+ if (startNode) {
+ const startLayout = result.get(startNode.id)
+
+ if (startLayout) {
+ const desiredMinX = NODE_LAYOUT_HORIZONTAL_PADDING / 1.5
+ if (startLayout.x > desiredMinX) {
+ const shiftX = startLayout.x - desiredMinX
+ result.forEach((value, key) => {
+ result.set(key, {
+ ...value,
+ x: value.x - shiftX,
+ })
+ })
+ }
+
+ const desiredMinY = startLayout.y
+ const deltaY = NODE_LAYOUT_VERTICAL_PADDING / 2
+ result.forEach((value, key) => {
+ result.set(key, {
+ ...value,
+ y: value.y - desiredMinY + deltaY,
+ })
+ })
+ }
+ }
+
+ let minX = Infinity
+ let minY = Infinity
+ let maxX = -Infinity
+ let maxY = -Infinity
+
+ result.forEach((value) => {
+ minX = Math.min(minX, value.x)
+ minY = Math.min(minY, value.y)
+ maxX = Math.max(maxX, value.x + value.width)
+ maxY = Math.max(maxY, value.y + value.height)
+ })
+
+ if (!Number.isFinite(minX) || !Number.isFinite(minY))
+ return layout
+
+ return normaliseBounds({
+ nodes: result,
+ bounds: {
+ minX,
+ minY,
+ maxX,
+ maxY,
+ },
+ })
+}
+
+export const getLayoutForChildNodes = async (
+ parentNodeId: string,
+ originNodes: Node[],
+ originEdges: Edge[],
+): Promise => {
+ edgeCounter = 0
+ const nodes = cloneDeep(originNodes).filter(node => node.parentId === parentNodeId)
+ if (!nodes.length)
+ return null
+
+ const edges = cloneDeep(originEdges).filter(edge =>
+ (edge.data?.isInIteration && edge.data?.iteration_id === parentNodeId)
+ || (edge.data?.isInLoop && edge.data?.loop_id === parentNodeId),
+ )
+
+ const elkNodes: ElkNodeShape[] = nodes.map(toElkNode)
+ const elkEdges: ElkEdgeShape[] = edges.map(edge => createEdge(edge.source, edge.target))
+
+ const graph = {
+ id: parentNodeId,
+ layoutOptions: CHILD_LAYOUT_OPTIONS,
+ children: elkNodes,
+ edges: elkEdges,
+ }
+
+ const layoutedGraph = await elk.layout(graph)
+ const layout = collectLayout(layoutedGraph, () => true)
+ return normaliseChildLayout(layout, nodes)
+}
diff --git a/web/app/components/workflow/workflow-history-store.tsx b/web/app/components/workflow/workflow-history-store.tsx
index c250708177..96e87f4fd4 100644
--- a/web/app/components/workflow/workflow-history-store.tsx
+++ b/web/app/components/workflow/workflow-history-store.tsx
@@ -3,7 +3,7 @@ import { type StoreApi, create } from 'zustand'
import { type TemporalState, temporal } from 'zundo'
import isDeepEqual from 'fast-deep-equal'
import type { Edge, Node } from './types'
-import type { WorkflowHistoryEvent } from './hooks'
+import type { WorkflowHistoryEventT } from './hooks'
import { noop } from 'lodash-es'
export const WorkflowHistoryStoreContext = createContext({ store: null, shortcutsEnabled: true, setShortcutsEnabled: noop })
@@ -98,7 +98,7 @@ function createStore({
export type WorkflowHistoryStore = {
nodes: Node[]
edges: Edge[]
- workflowHistoryEvent: WorkflowHistoryEvent | undefined
+ workflowHistoryEvent: WorkflowHistoryEventT | undefined
workflowHistoryEventMeta?: WorkflowHistoryEventMeta
}
diff --git a/web/app/components/workflow/workflow-preview/components/nodes/loop/node.tsx b/web/app/components/workflow/workflow-preview/components/nodes/loop/node.tsx
index 4c0080ec70..f41fa120a6 100644
--- a/web/app/components/workflow/workflow-preview/components/nodes/loop/node.tsx
+++ b/web/app/components/workflow/workflow-preview/components/nodes/loop/node.tsx
@@ -15,7 +15,7 @@ import { useNodeLoopInteractions } from './hooks'
const Node: FC> = ({
id,
- data,
+ data: _data,
}) => {
const { zoom } = useViewport()
const nodesInitialized = useNodesInitialized()
diff --git a/web/app/signin/components/mail-and-password-auth.tsx b/web/app/signin/components/mail-and-password-auth.tsx
index aaadc0b197..5214b73ee0 100644
--- a/web/app/signin/components/mail-and-password-auth.tsx
+++ b/web/app/signin/components/mail-and-password-auth.tsx
@@ -19,7 +19,7 @@ type MailAndPasswordAuthProps = {
allowRegistration: boolean
}
-export default function MailAndPasswordAuth({ isInvite, isEmailSetup, allowRegistration }: MailAndPasswordAuthProps) {
+export default function MailAndPasswordAuth({ isInvite, isEmailSetup, allowRegistration: _allowRegistration }: MailAndPasswordAuthProps) {
const { t } = useTranslation()
const { locale } = useContext(I18NContext)
const router = useRouter()
diff --git a/web/i18n-config/index.ts b/web/i18n-config/index.ts
index fdb31c49b4..b2b83fa76a 100644
--- a/web/i18n-config/index.ts
+++ b/web/i18n-config/index.ts
@@ -14,7 +14,8 @@ export type Locale = typeof i18n['locales'][number]
export const setLocaleOnClient = async (locale: Locale, reloadPage = true) => {
Cookies.set(LOCALE_COOKIE_NAME, locale, { expires: 365 })
await changeLanguage(locale)
- reloadPage && location.reload()
+ if (reloadPage)
+ location.reload()
}
export const getLocaleOnClient = (): Locale => {
diff --git a/web/i18n/de-DE/billing.ts b/web/i18n/de-DE/billing.ts
index 98d4488fab..fc45f3889c 100644
--- a/web/i18n/de-DE/billing.ts
+++ b/web/i18n/de-DE/billing.ts
@@ -94,6 +94,8 @@ const translation = {
teamMember_one: '{{count,number}} Teammitglied',
documentsRequestQuotaTooltip: 'Gibt die Gesamtzahl der Aktionen an, die ein Arbeitsbereich pro Minute innerhalb der Wissensbasis ausführen kann, einschließlich der Erstellung, Löschung, Aktualisierung von Datensätzen, des Hochladens von Dokumenten, von Änderungen, der Archivierung und von Abfragen in der Wissensbasis. Diese Kennzahl wird verwendet, um die Leistung von Anfragen an die Wissensbasis zu bewerten. Wenn ein Sandbox-Nutzer beispielsweise in einer Minute 10 aufeinanderfolgende Testdurchläufe durchführt, wird sein Arbeitsbereich für die nächste Minute vorübergehend daran gehindert, die folgenden Aktionen auszuführen: Erstellung, Löschung, Aktualisierung von Datensätzen sowie das Hochladen oder Ändern von Dokumenten.',
startBuilding: 'Beginnen Sie mit der Entwicklung',
+ taxTipSecond: 'Wenn in Ihrer Region keine relevanten Steuervorschriften gelten, wird an der Kasse keine Steuer angezeigt und Ihnen werden während der gesamten Abonnementlaufzeit keine zusätzlichen Gebühren berechnet.',
+ taxTip: 'Alle Abonnementspreise (monatlich/jährlich) verstehen sich zuzüglich der geltenden Steuern (z. B. MwSt., Umsatzsteuer).',
},
plans: {
sandbox: {
diff --git a/web/i18n/de-DE/workflow.ts b/web/i18n/de-DE/workflow.ts
index b45c3f2c35..06d3821e6a 100644
--- a/web/i18n/de-DE/workflow.ts
+++ b/web/i18n/de-DE/workflow.ts
@@ -476,6 +476,7 @@ const translation = {
},
outputVars: {
output: 'Generierter Inhalt',
+ reasoning_content: 'Reasoning-Inhalt',
usage: 'Nutzungsinformationen des Modells',
},
singleRun: {
diff --git a/web/i18n/en-US/billing.ts b/web/i18n/en-US/billing.ts
index 72cf9a3fca..9169631281 100644
--- a/web/i18n/en-US/billing.ts
+++ b/web/i18n/en-US/billing.ts
@@ -37,6 +37,8 @@ const translation = {
save: 'Save ',
free: 'Free',
annualBilling: 'Bill Annually Save {{percent}}%',
+ taxTip: 'All subscription prices (monthly/annual) exclude applicable taxes (e.g., VAT, sales tax).',
+ taxTipSecond: 'If your region has no applicable tax requirements, no tax will appear in your checkout, and you won’t be charged any additional fees for the entire subscription term.',
comparePlanAndFeatures: 'Compare plans & features',
priceTip: 'per workspace/',
currentPlan: 'Current Plan',
diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts
index 9109a4601f..c84bbefc58 100644
--- a/web/i18n/en-US/workflow.ts
+++ b/web/i18n/en-US/workflow.ts
@@ -492,6 +492,7 @@ const translation = {
},
outputVars: {
output: 'Generate content',
+ reasoning_content: 'Reasoning Content',
usage: 'Model Usage Information',
},
singleRun: {
diff --git a/web/i18n/es-ES/billing.ts b/web/i18n/es-ES/billing.ts
index c5d4ef95b9..a8180e2d07 100644
--- a/web/i18n/es-ES/billing.ts
+++ b/web/i18n/es-ES/billing.ts
@@ -94,6 +94,8 @@ const translation = {
apiRateLimitTooltip: 'El límite de tasa de la API se aplica a todas las solicitudes realizadas a través de la API de Dify, incluidos la generación de texto, las conversaciones de chat, las ejecuciones de flujo de trabajo y el procesamiento de documentos.',
documentsRequestQuotaTooltip: 'Especifica el número total de acciones que un espacio de trabajo puede realizar por minuto dentro de la base de conocimientos, incluyendo la creación, eliminación, actualización de conjuntos de datos, carga de documentos, modificaciones, archivo y consultas a la base de conocimientos. Esta métrica se utiliza para evaluar el rendimiento de las solicitudes a la base de conocimientos. Por ejemplo, si un usuario de Sandbox realiza 10 pruebas consecutivas en un minuto, su espacio de trabajo será temporalmente restringido de realizar las siguientes acciones durante el siguiente minuto: creación de conjuntos de datos, eliminación, actualizaciones y carga o modificaciones de documentos.',
startBuilding: 'Empezar a construir',
+ taxTip: 'Todos los precios de suscripción (mensuales/anuales) excluyen los impuestos aplicables (por ejemplo, IVA, impuesto sobre ventas).',
+ taxTipSecond: 'Si su región no tiene requisitos fiscales aplicables, no se mostrará ningún impuesto en su pago y no se le cobrará ninguna tarifa adicional durante todo el período de suscripción.',
},
plans: {
sandbox: {
diff --git a/web/i18n/es-ES/workflow.ts b/web/i18n/es-ES/workflow.ts
index e47ad076b4..d78850ad83 100644
--- a/web/i18n/es-ES/workflow.ts
+++ b/web/i18n/es-ES/workflow.ts
@@ -476,6 +476,7 @@ const translation = {
},
outputVars: {
output: 'Generar contenido',
+ reasoning_content: 'Contenido de razonamiento',
usage: 'Información de uso del modelo',
},
singleRun: {
diff --git a/web/i18n/fa-IR/billing.ts b/web/i18n/fa-IR/billing.ts
index 5634692dc2..3749036f3c 100644
--- a/web/i18n/fa-IR/billing.ts
+++ b/web/i18n/fa-IR/billing.ts
@@ -94,6 +94,8 @@ const translation = {
apiRateLimitTooltip: 'محدودیت نرخ API برای همه درخواستهای انجام شده از طریق API Dify اعمال میشود، از جمله تولید متن، محاورههای چت، اجرای گردشهای کار و پردازش اسناد.',
documentsRequestQuotaTooltip: 'تعیین میکند که تعداد کلی اقداماتی که یک فضای کاری میتواند در هر دقیقه در داخل پایگاه دانش انجام دهد، شامل ایجاد مجموعه داده، حذف، بهروزرسانی، بارگذاری مستندات، تغییرات، بایگانی و پرسش از پایگاه دانش است. این معیار برای ارزیابی عملکرد درخواستهای پایگاه دانش استفاده میشود. به عنوان مثال، اگر یک کاربر Sandbox در طی یک دقیقه 10 آزمایش متوالی انجام دهد، فضای کاری او به طور موقت از انجام اقدامات زیر در دقیقه بعدی محدود خواهد شد: ایجاد مجموعه داده، حذف، بهروزرسانی و بارگذاری یا تغییر مستندات.',
startBuilding: 'شروع به ساخت کنید',
+ taxTip: 'تمام قیمتهای اشتراک (ماهانه/سالانه) شامل مالیاتهای مربوطه (مثلاً مالیات بر ارزش افزوده، مالیات فروش) نمیشوند.',
+ taxTipSecond: 'اگر منطقه شما هیچ الزامات مالیاتی قابل اجرا نداشته باشد، هیچ مالیاتی در هنگام پرداخت نشان داده نمیشود و برای کل مدت اشتراک هیچ هزینه اضافی از شما دریافت نخواهد شد.',
},
plans: {
sandbox: {
diff --git a/web/i18n/fa-IR/workflow.ts b/web/i18n/fa-IR/workflow.ts
index b57d37e745..addb9618b9 100644
--- a/web/i18n/fa-IR/workflow.ts
+++ b/web/i18n/fa-IR/workflow.ts
@@ -476,6 +476,7 @@ const translation = {
},
outputVars: {
output: 'تولید محتوا',
+ reasoning_content: 'محتوای استدلال',
usage: 'اطلاعات استفاده از مدل',
},
singleRun: {
diff --git a/web/i18n/fr-FR/billing.ts b/web/i18n/fr-FR/billing.ts
index 117d1c6654..a41eed7e23 100644
--- a/web/i18n/fr-FR/billing.ts
+++ b/web/i18n/fr-FR/billing.ts
@@ -94,6 +94,8 @@ const translation = {
documents: '{{count,number}} Documents de connaissance',
documentsRequestQuotaTooltip: 'Spécifie le nombre total d\'actions qu\'un espace de travail peut effectuer par minute dans la base de connaissances, y compris la création, la suppression, les mises à jour de jeux de données, le téléchargement de documents, les modifications, l\'archivage et les requêtes de la base de connaissances. Ce paramètre est utilisé pour évaluer les performances des requêtes de la base de connaissances. Par exemple, si un utilisateur de Sandbox effectue 10 tests de validité consécutifs en une minute, son espace de travail sera temporairement restreint dans l\'exécution des actions suivantes pendant la minute suivante : création, suppression, mises à jour de jeux de données, et téléchargements ou modifications de documents.',
startBuilding: 'Commencez à construire',
+ taxTip: 'Tous les prix des abonnements (mensuels/annuels) s\'entendent hors taxes applicables (par exemple, TVA, taxe de vente).',
+ taxTipSecond: 'Si votre région n\'a pas de exigences fiscales applicables, aucune taxe n\'apparaîtra lors de votre paiement et vous ne serez pas facturé de frais supplémentaires pendant toute la durée de l\'abonnement.',
},
plans: {
sandbox: {
diff --git a/web/i18n/fr-FR/workflow.ts b/web/i18n/fr-FR/workflow.ts
index c6eda423c5..104606789f 100644
--- a/web/i18n/fr-FR/workflow.ts
+++ b/web/i18n/fr-FR/workflow.ts
@@ -476,6 +476,7 @@ const translation = {
},
outputVars: {
output: 'Contenu généré',
+ reasoning_content: 'Contenu de raisonnement',
usage: 'Informations sur l\'utilisation du modèle',
},
singleRun: {
diff --git a/web/i18n/hi-IN/billing.ts b/web/i18n/hi-IN/billing.ts
index 749ab804ab..fbc6dffc7c 100644
--- a/web/i18n/hi-IN/billing.ts
+++ b/web/i18n/hi-IN/billing.ts
@@ -102,6 +102,8 @@ const translation = {
teamMember_one: '{{count,number}} टीम सदस्य',
documentsRequestQuotaTooltip: 'यह ज्ञान आधार में एक कार्यक्षेत्र द्वारा प्रति मिनट किए जा सकने वाले कुल कार्यों की संख्या को निर्दिष्ट करता है, जिसमें डेटासेट बनाना, हटाना, अपडेट करना, दस्तावेज़ अपलोड करना, संशोधन करना, संग्रहित करना और ज्ञान आधार अनुरोध शामिल हैं। इस मीट्रिक का उपयोग ज्ञान आधार अनुरोधों के प्रदर्शन का मूल्यांकन करने के लिए किया जाता है। उदाहरण के लिए, यदि एक सैंडबॉक्स उपयोगकर्ता एक मिनट के भीतर 10 लगातार हिट परीक्षण करता है, तो उनके कार्यक्षेत्र को अगले मिनट के लिए निम्नलिखित कार्यों को करने से अस्थायी रूप से प्रतिबंधित किया जाएगा: डेटासेट बनाना, हटाना, अपडेट करना और दस्तावेज़ अपलोड या संशोधन करना।',
startBuilding: 'बनाना शुरू करें',
+ taxTip: 'सभी सदस्यता मूल्य (मासिक/वार्षिक) लागू करों (जैसे, VAT, बिक्री कर) को शामिल नहीं करते हैं।',
+ taxTipSecond: 'यदि आपके क्षेत्र में कोई लागू कर आवश्यकताएँ नहीं हैं, तो आपकी चेकआउट में कोई कर नहीं दिखाई देगा, और पूरे सदस्यता अवधि के लिए आपसे कोई अतिरिक्त शुल्क नहीं लिया जाएगा।',
},
plans: {
sandbox: {
diff --git a/web/i18n/hi-IN/workflow.ts b/web/i18n/hi-IN/workflow.ts
index ce0900d2ad..b6330fe79f 100644
--- a/web/i18n/hi-IN/workflow.ts
+++ b/web/i18n/hi-IN/workflow.ts
@@ -489,6 +489,7 @@ const translation = {
},
outputVars: {
output: 'सामग्री उत्पन्न करें',
+ reasoning_content: 'तर्क सामग्री',
usage: 'मॉडल उपयोग जानकारी',
},
singleRun: {
diff --git a/web/i18n/id-ID/billing.ts b/web/i18n/id-ID/billing.ts
index 11419c3b16..c6c718d15b 100644
--- a/web/i18n/id-ID/billing.ts
+++ b/web/i18n/id-ID/billing.ts
@@ -87,6 +87,8 @@ const translation = {
modelProviders: 'Mendukung OpenAI/Anthropic/Llama2/Azure OpenAI/Hugging Face/Replite',
member: 'Anggota',
startBuilding: 'Mulai Membangun',
+ taxTip: 'Semua harga langganan (bulanan/tahunan) belum termasuk pajak yang berlaku (misalnya, PPN, pajak penjualan).',
+ taxTipSecond: 'Jika wilayah Anda tidak memiliki persyaratan pajak yang berlaku, tidak akan ada pajak yang muncul saat checkout, dan Anda tidak akan dikenakan biaya tambahan apa pun selama masa langganan.',
},
plans: {
sandbox: {
diff --git a/web/i18n/id-ID/workflow.ts b/web/i18n/id-ID/workflow.ts
index f6ef6e08f1..63496f58b5 100644
--- a/web/i18n/id-ID/workflow.ts
+++ b/web/i18n/id-ID/workflow.ts
@@ -459,6 +459,7 @@ const translation = {
},
outputVars: {
output: 'Hasilkan konten',
+ reasoning_content: 'Konten penalaran',
usage: 'Informasi Penggunaan Model',
},
singleRun: {
diff --git a/web/i18n/it-IT/billing.ts b/web/i18n/it-IT/billing.ts
index f89502ee5b..ef6b1943e3 100644
--- a/web/i18n/it-IT/billing.ts
+++ b/web/i18n/it-IT/billing.ts
@@ -102,6 +102,8 @@ const translation = {
annualBilling: 'Fatturazione annuale',
documentsRequestQuotaTooltip: 'Specifica il numero totale di azioni che un\'area di lavoro può eseguire al minuto all\'interno della base di conoscenza, compresi la creazione, l\'eliminazione, gli aggiornamenti dei dataset, il caricamento di documenti, le modifiche, l\'archiviazione e le query sulla base di conoscenza. Questa metrica viene utilizzata per valutare le prestazioni delle richieste alla base di conoscenza. Ad esempio, se un utente di Sandbox esegue 10 test consecutivi in un minuto, la sua area di lavoro sarà temporaneamente limitata dall\'eseguire le seguenti azioni per il minuto successivo: creazione, eliminazione, aggiornamenti dei dataset e caricamento o modifica di documenti.',
startBuilding: 'Inizia a costruire',
+ taxTip: 'Tutti i prezzi degli abbonamenti (mensili/annuali) non includono le tasse applicabili (ad esempio, IVA, imposta sulle vendite).',
+ taxTipSecond: 'Se nella tua regione non ci sono requisiti fiscali applicabili, nessuna tassa apparirà al momento del pagamento e non ti verranno addebitate spese aggiuntive per l\'intera durata dell\'abbonamento.',
},
plans: {
sandbox: {
diff --git a/web/i18n/it-IT/workflow.ts b/web/i18n/it-IT/workflow.ts
index e4602df5a8..a94b7d06ca 100644
--- a/web/i18n/it-IT/workflow.ts
+++ b/web/i18n/it-IT/workflow.ts
@@ -493,6 +493,7 @@ const translation = {
},
outputVars: {
output: 'Genera contenuto',
+ reasoning_content: 'Contenuto del ragionamento',
usage: 'Informazioni sull\'utilizzo del modello',
},
singleRun: {
diff --git a/web/i18n/ja-JP/billing.ts b/web/i18n/ja-JP/billing.ts
index 426687da6c..6dbff60d5a 100644
--- a/web/i18n/ja-JP/billing.ts
+++ b/web/i18n/ja-JP/billing.ts
@@ -36,6 +36,8 @@ const translation = {
save: '節約 ',
free: '無料',
annualBilling: '年次請求',
+ taxTip: 'すべてのサブスクリプション料金(月額/年額)は、適用される税金(例:消費税、付加価値税)を含みません。',
+ taxTipSecond: 'お客様の地域に適用税がない場合、チェックアウト時に税金は表示されず、サブスクリプション期間中に追加料金が請求されることもありません。',
comparePlanAndFeatures: 'プランと機能を比較する',
priceTip: 'ワークスペース/',
currentPlan: '現在のプラン',
diff --git a/web/i18n/ja-JP/workflow.ts b/web/i18n/ja-JP/workflow.ts
index 5a947c4209..cefe0b742f 100644
--- a/web/i18n/ja-JP/workflow.ts
+++ b/web/i18n/ja-JP/workflow.ts
@@ -488,6 +488,7 @@ const translation = {
},
outputVars: {
output: '生成内容',
+ reasoning_content: '推論内容',
usage: 'モデル使用量',
},
singleRun: {
diff --git a/web/i18n/ko-KR/billing.ts b/web/i18n/ko-KR/billing.ts
index ff0dd189e4..c5f081d41b 100644
--- a/web/i18n/ko-KR/billing.ts
+++ b/web/i18n/ko-KR/billing.ts
@@ -103,6 +103,8 @@ const translation = {
documentsRequestQuotaTooltip:
'지식 기반 내에서 작업 공간이 분당 수행할 수 있는 총 작업 수를 지정합니다. 여기에는 데이터 세트 생성, 삭제, 업데이트, 문서 업로드, 수정, 보관 및 지식 기반 쿼리가 포함됩니다. 이 지표는 지식 기반 요청의 성능을 평가하는 데 사용됩니다. 예를 들어, 샌드박스 사용자가 1 분 이내에 10 회의 연속 히트 테스트를 수행하면, 해당 작업 공간은 다음 1 분 동안 데이터 세트 생성, 삭제, 업데이트 및 문서 업로드 또는 수정과 같은 작업을 수행하는 것이 일시적으로 제한됩니다.',
startBuilding: '구축 시작',
+ taxTip: '모든 구독 요금(월간/연간)에는 해당 세금(예: 부가가치세, 판매세)이 포함되어 있지 않습니다.',
+ taxTipSecond: '귀하의 지역에 적용 가능한 세금 요구 사항이 없는 경우, 결제 시 세금이 표시되지 않으며 전체 구독 기간 동안 추가 요금이 부과되지 않습니다.',
},
plans: {
sandbox: {
diff --git a/web/i18n/ko-KR/workflow.ts b/web/i18n/ko-KR/workflow.ts
index 6f87ffb5ce..73eb5da3dd 100644
--- a/web/i18n/ko-KR/workflow.ts
+++ b/web/i18n/ko-KR/workflow.ts
@@ -501,6 +501,7 @@ const translation = {
},
outputVars: {
output: '생성된 내용',
+ reasoning_content: '추론 내용',
usage: '모델 사용 정보',
},
singleRun: {
diff --git a/web/i18n/pl-PL/billing.ts b/web/i18n/pl-PL/billing.ts
index 3bf0867877..cf0859468b 100644
--- a/web/i18n/pl-PL/billing.ts
+++ b/web/i18n/pl-PL/billing.ts
@@ -101,6 +101,8 @@ const translation = {
documentsRequestQuota: '{{count,number}}/min Limit wiedzy na żądanie',
documentsRequestQuotaTooltip: 'Określa całkowitą liczbę działań, jakie przestrzeń robocza może wykonać na minutę w ramach bazy wiedzy, w tym tworzenie zbiorów danych, usuwanie, aktualizacje, przesyłanie dokumentów, modyfikacje, archiwizowanie i zapytania do bazy wiedzy. Ta metryka jest używana do oceny wydajności zapytań do bazy wiedzy. Na przykład, jeśli użytkownik Sandbox wykona 10 kolejnych testów w ciągu jednej minuty, jego przestrzeń robocza zostanie tymczasowo ograniczona w wykonywaniu następujących działań przez następną minutę: tworzenie zbiorów danych, usuwanie, aktualizacje oraz przesyłanie lub modyfikacje dokumentów.',
startBuilding: 'Zacznij budować',
+ taxTip: 'Wszystkie ceny subskrypcji (miesięczne/roczne) nie obejmują obowiązujących podatków (np. VAT, podatek od sprzedaży).',
+ taxTipSecond: 'Jeśli w Twoim regionie nie ma obowiązujących przepisów podatkowych, podatek nie pojawi się podczas realizacji zamówienia i nie zostaną naliczone żadne dodatkowe opłaty przez cały okres subskrypcji.',
},
plans: {
sandbox: {
diff --git a/web/i18n/pl-PL/workflow.ts b/web/i18n/pl-PL/workflow.ts
index 87c96c758f..1d2a892941 100644
--- a/web/i18n/pl-PL/workflow.ts
+++ b/web/i18n/pl-PL/workflow.ts
@@ -444,6 +444,7 @@ const translation = {
},
outputVars: {
output: 'Generowana treść',
+ reasoning_content: 'Treść rozumowania',
usage: 'Informacje o użyciu modelu',
},
singleRun: {
diff --git a/web/i18n/pt-BR/billing.ts b/web/i18n/pt-BR/billing.ts
index 91ccaa7794..e4ca0a064a 100644
--- a/web/i18n/pt-BR/billing.ts
+++ b/web/i18n/pt-BR/billing.ts
@@ -94,6 +94,8 @@ const translation = {
apiRateLimitTooltip: 'O limite da taxa da API se aplica a todas as solicitações feitas através da API Dify, incluindo geração de texto, conversas de chat, execuções de fluxo de trabalho e processamento de documentos.',
documentsRequestQuotaTooltip: 'Especifica o número total de ações que um espaço de trabalho pode realizar por minuto dentro da base de conhecimento, incluindo criação, exclusão, atualizações de conjuntos de dados, uploads de documentos, modificações, arquivamento e consultas à base de conhecimento. Esse métrica é utilizada para avaliar o desempenho das solicitações à base de conhecimento. Por exemplo, se um usuário do Sandbox realizar 10 testes de impacto consecutivos dentro de um minuto, seu espaço de trabalho ficará temporariamente restrito de realizar as seguintes ações no minuto seguinte: criação, exclusão, atualizações de conjuntos de dados e uploads ou modificações de documentos.',
startBuilding: 'Comece a construir',
+ taxTip: 'Todos os preços de assinatura (mensal/anual) não incluem os impostos aplicáveis (por exemplo, IVA, imposto sobre vendas).',
+ taxTipSecond: 'Se a sua região não tiver requisitos fiscais aplicáveis, nenhum imposto aparecerá no seu checkout e você não será cobrado por taxas adicionais durante todo o período da assinatura.',
},
plans: {
sandbox: {
diff --git a/web/i18n/pt-BR/workflow.ts b/web/i18n/pt-BR/workflow.ts
index 9657ef8e7f..5610cacc13 100644
--- a/web/i18n/pt-BR/workflow.ts
+++ b/web/i18n/pt-BR/workflow.ts
@@ -444,6 +444,7 @@ const translation = {
},
outputVars: {
output: 'Conteúdo gerado',
+ reasoning_content: 'Conteúdo de raciocínio',
usage: 'Informações de uso do modelo',
},
singleRun: {
diff --git a/web/i18n/ro-RO/billing.ts b/web/i18n/ro-RO/billing.ts
index 550ff3e677..3f5577dc32 100644
--- a/web/i18n/ro-RO/billing.ts
+++ b/web/i18n/ro-RO/billing.ts
@@ -94,6 +94,8 @@ const translation = {
documentsRequestQuotaTooltip: 'Specificați numărul total de acțiuni pe care un spațiu de lucru le poate efectua pe minut în cadrul bazei de cunoștințe, inclusiv crearea, ștergerea, actualizările setului de date, încărcările de documente, modificările, arhivarea și interogările bazei de cunoștințe. Acest metric este utilizat pentru a evalua performanța cererilor din baza de cunoștințe. De exemplu, dacă un utilizator Sandbox efectuează 10 teste consecutive de hituri într-un minut, spațiul său de lucru va fi restricționat temporar de la efectuarea următoarelor acțiuni pentru minutul următor: crearea setului de date, ștergerea, actualizările și încărcările sau modificările documentelor.',
apiRateLimitTooltip: 'Limita de rată API se aplică tuturor cererilor efectuate prin API-ul Dify, inclusiv generarea de texte, conversațiile de chat, execuțiile fluxului de lucru și procesarea documentelor.',
startBuilding: 'Începeți să construiți',
+ taxTip: 'Toate prețurile abonamentelor (lunare/anuale) nu includ taxele aplicabile (de exemplu, TVA, taxa pe vânzări).',
+ taxTipSecond: 'Dacă regiunea dumneavoastră nu are cerințe fiscale aplicabile, niciun impozit nu va apărea la finalizarea comenzii și nu vi se vor percepe taxe suplimentare pe întreaga durată a abonamentului.',
},
plans: {
sandbox: {
diff --git a/web/i18n/ro-RO/workflow.ts b/web/i18n/ro-RO/workflow.ts
index 94d01ec1ba..d2239e7979 100644
--- a/web/i18n/ro-RO/workflow.ts
+++ b/web/i18n/ro-RO/workflow.ts
@@ -444,6 +444,7 @@ const translation = {
},
outputVars: {
output: 'Conținut generat',
+ reasoning_content: 'Conținut de raționament',
usage: 'Informații de utilizare a modelului',
},
singleRun: {
diff --git a/web/i18n/ru-RU/billing.ts b/web/i18n/ru-RU/billing.ts
index 27f5c71685..7017f90cc2 100644
--- a/web/i18n/ru-RU/billing.ts
+++ b/web/i18n/ru-RU/billing.ts
@@ -94,6 +94,8 @@ const translation = {
priceTip: 'по рабочему месту/',
documentsTooltip: 'Квота на количество документов, импортируемых из источника знаний.',
startBuilding: 'Начать строительство',
+ taxTip: 'Все цены на подписку (ежемесячную/годовую) не включают применимые налоги (например, НДС, налог с продаж).',
+ taxTipSecond: 'Если в вашем регионе нет применимых налоговых требований, налоги не будут отображаться при оформлении заказа, и с вас не будут взиматься дополнительные сборы за весь срок подписки.',
},
plans: {
sandbox: {
diff --git a/web/i18n/ru-RU/workflow.ts b/web/i18n/ru-RU/workflow.ts
index 1e0ecf1276..2345f3447b 100644
--- a/web/i18n/ru-RU/workflow.ts
+++ b/web/i18n/ru-RU/workflow.ts
@@ -444,6 +444,7 @@ const translation = {
},
outputVars: {
output: 'Создать контент',
+ reasoning_content: 'Содержимое рассуждений',
usage: 'Информация об использовании модели',
},
singleRun: {
diff --git a/web/i18n/sl-SI/billing.ts b/web/i18n/sl-SI/billing.ts
index 4481100dd8..fb9d9ec435 100644
--- a/web/i18n/sl-SI/billing.ts
+++ b/web/i18n/sl-SI/billing.ts
@@ -94,6 +94,8 @@ const translation = {
getStarted: 'Začnite',
documentsRequestQuotaTooltip: 'Določa skupno število dejanj, ki jih lahko delovno mesto opravi na minuto znotraj znanja baze, vključno s kreiranjem, brisanjem, posodobitvami, nalaganjem dokumentov, spremembami, arhiviranjem in poizvedbami po znanju bazi. Ta meritev se uporablja za ocenjevanje uspešnosti poizvedb v bazi znanja. Na primer, če uporabnik Sandbox izvede 10 zaporednih testov udarca v eni minuti, bo njegovo delovno mesto začasno omejeno pri izvajanju naslednjih dejanj v naslednji minuti: kreiranje podatkovnih nizov, brisanje, posodobitve in nalaganje ali spremembe dokumentov.',
startBuilding: 'Začnite graditi',
+ taxTip: 'Vse cene naročnin (mesečne/letne) ne vključujejo veljavnih davkov (npr. DDV, davek na promet).',
+ taxTipSecond: 'Če vaša regija nima veljavnih davčnih zahtev, se v vaši košarici ne bo prikazal noben davek in za celotno obdobje naročnine vam ne bodo zaračunani nobeni dodatni stroški.',
},
plans: {
sandbox: {
diff --git a/web/i18n/sl-SI/workflow.ts b/web/i18n/sl-SI/workflow.ts
index baeff90ee6..7a167c236f 100644
--- a/web/i18n/sl-SI/workflow.ts
+++ b/web/i18n/sl-SI/workflow.ts
@@ -442,6 +442,7 @@ const translation = {
},
outputVars: {
output: 'Ustvari vsebino',
+ reasoning_content: 'Vsebina razmišljanja',
usage: 'Informacije o uporabi modela',
},
singleRun: {
diff --git a/web/i18n/th-TH/billing.ts b/web/i18n/th-TH/billing.ts
index 55a01449eb..461e4a8240 100644
--- a/web/i18n/th-TH/billing.ts
+++ b/web/i18n/th-TH/billing.ts
@@ -94,6 +94,8 @@ const translation = {
annualBilling: 'การเรียกเก็บเงินประจำปี',
documentsRequestQuotaTooltip: 'ระบุจำนวนรวมของการกระทำที่เวิร์กสเปซสามารถดำเนินการต่อหนึ่งนาทีภายในฐานความรู้ รวมถึงการสร้างชุดข้อมูล การลบ การอัปเดต การอัปโหลดเอกสาร การปรับเปลี่ยน การเก็บถาวร และการสอบถามฐานความรู้ เมตริกนี้ถูกใช้ในการประเมินประสิทธิภาพของคำขอฐานความรู้ ตัวอย่างเช่น หากผู้ใช้ Sandbox ทำการทดสอบการตี 10 ครั้งต่อเนื่องภายในหนึ่งนาที เวิร์กสเปซของพวกเขาจะถูกจำกัดชั่วคราวในการดำเนินการต่อไปนี้ในนาทีถัดไป: การสร้างชุดข้อมูล การลบ การอัปเดต หรือการอัปโหลดหรือปรับเปลี่ยนเอกสาร.',
startBuilding: 'เริ่มสร้าง',
+ taxTip: 'ราคาการสมัครสมาชิกทั้งหมด (รายเดือน/รายปี) ไม่รวมภาษีที่ใช้บังคับ (เช่น ภาษีมูลค่าเพิ่ม, ภาษีการขาย)',
+ taxTipSecond: 'หากภูมิภาคของคุณไม่มีข้อกำหนดเกี่ยวกับภาษีที่ใช้ได้ จะไม่มีการคิดภาษีในขั้นตอนการชำระเงินของคุณ และคุณจะไม่ถูกเรียกเก็บค่าธรรมเนียมเพิ่มเติมใด ๆ ตลอดระยะเวลาสมาชิกทั้งหมด',
},
plans: {
sandbox: {
diff --git a/web/i18n/th-TH/workflow.ts b/web/i18n/th-TH/workflow.ts
index e2db4ceb4a..1cea01690a 100644
--- a/web/i18n/th-TH/workflow.ts
+++ b/web/i18n/th-TH/workflow.ts
@@ -444,6 +444,7 @@ const translation = {
},
outputVars: {
output: 'สร้างเนื้อหา',
+ reasoning_content: 'เนื้อหาการให้เหตุผล',
usage: 'ข้อมูลการใช้งานรุ่น',
},
singleRun: {
diff --git a/web/i18n/tr-TR/billing.ts b/web/i18n/tr-TR/billing.ts
index 62d6e0a07e..6d01d9dd32 100644
--- a/web/i18n/tr-TR/billing.ts
+++ b/web/i18n/tr-TR/billing.ts
@@ -94,6 +94,8 @@ const translation = {
teamWorkspace: '{{count,number}} Takım Çalışma Alanı',
documentsRequestQuotaTooltip: 'Bir çalışma alanının bilgi tabanında, veri seti oluşturma, silme, güncellemeler, belge yüklemeleri, değişiklikler, arşivleme ve bilgi tabanı sorguları dahil olmak üzere, dakikada gerçekleştirebileceği toplam işlem sayısını belirtir. Bu ölçüt, bilgi tabanı taleplerinin performansını değerlendirmek için kullanılır. Örneğin, bir Sandbox kullanıcısı bir dakika içinde ardışık 10 vurma testi gerçekleştirirse, çalışma alanı bir sonraki dakika için aşağıdaki işlemleri gerçekleştirmesi geçici olarak kısıtlanacaktır: veri seti oluşturma, silme, güncellemeler ve belge yüklemeleri veya değişiklikler.',
startBuilding: 'İnşa Etmeye Başlayın',
+ taxTip: 'Tüm abonelik fiyatları (aylık/yıllık) geçerli vergiler (ör. KDV, satış vergisi) hariçtir.',
+ taxTipSecond: 'Bölgenizde geçerli vergi gereksinimleri yoksa, ödeme sayfanızda herhangi bir vergi görünmeyecek ve tüm abonelik süresi boyunca ek bir ücret tahsil edilmeyecektir.',
},
plans: {
sandbox: {
diff --git a/web/i18n/tr-TR/workflow.ts b/web/i18n/tr-TR/workflow.ts
index 68f3d5c0c2..dfab5c2c0c 100644
--- a/web/i18n/tr-TR/workflow.ts
+++ b/web/i18n/tr-TR/workflow.ts
@@ -444,6 +444,7 @@ const translation = {
},
outputVars: {
output: 'İçerik Üret',
+ reasoning_content: 'Akıl yürütme içeriği',
usage: 'Model Kullanım Bilgileri',
},
singleRun: {
diff --git a/web/i18n/uk-UA/billing.ts b/web/i18n/uk-UA/billing.ts
index 10dafedb24..03b743e4fe 100644
--- a/web/i18n/uk-UA/billing.ts
+++ b/web/i18n/uk-UA/billing.ts
@@ -94,6 +94,8 @@ const translation = {
apiRateLimitTooltip: 'Обмеження частоти запитів застосовується до всіх запитів, зроблених через API Dify, включаючи генерацію тексту, чат-розмови, виконання робочих процесів та обробку документів.',
documentsRequestQuotaTooltip: 'Вказує загальну кількість дій, які робоча область може виконувати за хвилину в межах бази знань, включаючи створення, видалення, оновлення наборів даних, завантаження документів, модифікації, архівування та запити до бази знань. Цей показник використовується для оцінки ефективності запитів до бази знань. Наприклад, якщо користувач Sandbox виконує 10 послідовних тестів за один хвилину, його робочій області буде тимчасово заборонено виконувати наступні дії протягом наступної хвилини: створення наборів даних, видалення, оновлення, а також завантаження чи модифікацію документів.',
startBuilding: 'Почніть будувати',
+ taxTip: 'Всі ціни на підписку (щомісячна/щорічна) не включають відповідні податки (наприклад, ПДВ, податок з продажу).',
+ taxTipSecond: 'Якщо для вашого регіону немає відповідних податкових вимог, податок не відображатиметься на вашому чек-ауті, і з вас не стягуватимуть додаткові збори протягом усього терміну підписки.',
},
plans: {
sandbox: {
diff --git a/web/i18n/uk-UA/workflow.ts b/web/i18n/uk-UA/workflow.ts
index 56715c5e37..09f2b71eea 100644
--- a/web/i18n/uk-UA/workflow.ts
+++ b/web/i18n/uk-UA/workflow.ts
@@ -444,6 +444,7 @@ const translation = {
},
outputVars: {
output: 'Генерований вміст',
+ reasoning_content: 'Зміст міркування',
usage: 'Інформація про використання моделі',
},
singleRun: {
diff --git a/web/i18n/vi-VN/billing.ts b/web/i18n/vi-VN/billing.ts
index 68e662425f..0166185e45 100644
--- a/web/i18n/vi-VN/billing.ts
+++ b/web/i18n/vi-VN/billing.ts
@@ -94,6 +94,8 @@ const translation = {
freeTrialTipSuffix: 'Không cần thẻ tín dụng',
documentsRequestQuotaTooltip: 'Chỉ định tổng số hành động mà một không gian làm việc có thể thực hiện mỗi phút trong cơ sở tri thức, bao gồm tạo mới tập dữ liệu, xóa, cập nhật, tải tài liệu lên, thay đổi, lưu trữ và truy vấn cơ sở tri thức. Chỉ số này được sử dụng để đánh giá hiệu suất của các yêu cầu cơ sở tri thức. Ví dụ, nếu một người dùng Sandbox thực hiện 10 lần kiểm tra liên tiếp trong một phút, không gian làm việc của họ sẽ bị hạn chế tạm thời không thực hiện các hành động sau trong phút tiếp theo: tạo mới tập dữ liệu, xóa, cập nhật và tải tài liệu lên hoặc thay đổi.',
startBuilding: 'Bắt đầu xây dựng',
+ taxTipSecond: 'Nếu khu vực của bạn không có yêu cầu thuế áp dụng, sẽ không có thuế xuất hiện trong quá trình thanh toán của bạn và bạn sẽ không bị tính bất kỳ khoản phí bổ sung nào trong suốt thời gian đăng ký.',
+ taxTip: 'Tất cả giá đăng ký (hàng tháng/hàng năm) chưa bao gồm các loại thuế áp dụng (ví dụ: VAT, thuế bán hàng).',
},
plans: {
sandbox: {
diff --git a/web/i18n/vi-VN/workflow.ts b/web/i18n/vi-VN/workflow.ts
index 3016d79a23..27d19a37f4 100644
--- a/web/i18n/vi-VN/workflow.ts
+++ b/web/i18n/vi-VN/workflow.ts
@@ -444,6 +444,7 @@ const translation = {
},
outputVars: {
output: 'Nội dung được tạo',
+ reasoning_content: 'Nội dung lập luận',
usage: 'Thông tin sử dụng mô hình',
},
singleRun: {
diff --git a/web/i18n/zh-Hans/billing.ts b/web/i18n/zh-Hans/billing.ts
index 96ba7970c8..00a7dd909a 100644
--- a/web/i18n/zh-Hans/billing.ts
+++ b/web/i18n/zh-Hans/billing.ts
@@ -36,6 +36,8 @@ const translation = {
save: '节省',
free: '免费',
annualBilling: '按年计费节省 {{percent}}%',
+ taxTip: '所有订阅价格(按月/按年)均不含适用税费(如增值税、销售税)。',
+ taxTipSecond: '如果您所在地区无适用税费要求,结账时将不会显示税费,且在整个订阅周期内您都无需支付任何额外费用。',
comparePlanAndFeatures: '对比套餐 & 功能特性',
priceTip: '每个团队空间/',
currentPlan: '当前计划',
diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts
index f92d923bc1..f23bc97910 100644
--- a/web/i18n/zh-Hans/workflow.ts
+++ b/web/i18n/zh-Hans/workflow.ts
@@ -489,6 +489,7 @@ const translation = {
},
outputVars: {
output: '生成内容',
+ reasoning_content: '推理内容',
usage: '模型用量信息',
},
singleRun: {
diff --git a/web/i18n/zh-Hant/billing.ts b/web/i18n/zh-Hant/billing.ts
index f99b1ef2cf..1b0b1f5e1f 100644
--- a/web/i18n/zh-Hant/billing.ts
+++ b/web/i18n/zh-Hant/billing.ts
@@ -94,6 +94,8 @@ const translation = {
documentsTooltip: '從知識數據來源導入的文件數量配額。',
documentsRequestQuotaTooltip: '指定工作區在知識基礎中每分鐘可以執行的總操作次數,包括數據集的創建、刪除、更新、文檔上傳、修改、歸檔和知識基礎查詢。這個指標用於評估知識基礎請求的性能。例如,如果一個沙箱用戶在一分鐘內連續執行 10 次命中測試,他們的工作區將在接下來的一分鐘內暫時禁止執行以下操作:數據集的創建、刪除、更新以及文檔上傳或修改。',
startBuilding: '開始建造',
+ taxTip: '所有訂閱價格(月費/年費)不包含適用的稅費(例如增值稅、銷售稅)。',
+ taxTipSecond: '如果您的地區沒有適用的稅務要求,結帳時將不會顯示任何稅款,且在整個訂閱期間您也不會被收取任何額外費用。',
},
plans: {
sandbox: {
diff --git a/web/i18n/zh-Hant/workflow.ts b/web/i18n/zh-Hant/workflow.ts
index df737dc1eb..bb580c3de4 100644
--- a/web/i18n/zh-Hant/workflow.ts
+++ b/web/i18n/zh-Hant/workflow.ts
@@ -476,6 +476,7 @@ const translation = {
},
outputVars: {
output: '生成內容',
+ reasoning_content: '推理內容',
usage: '模型用量信息',
},
singleRun: {
diff --git a/web/package.json b/web/package.json
index 90720b36cc..fe0d827533 100644
--- a/web/package.json
+++ b/web/package.json
@@ -2,7 +2,7 @@
"name": "dify-web",
"version": "1.9.1",
"private": true,
- "packageManager": "pnpm@10.17.1",
+ "packageManager": "pnpm@10.18.2",
"engines": {
"node": ">=v22.11.0"
},
@@ -43,7 +43,6 @@
"knip": "knip"
},
"dependencies": {
- "@dagrejs/dagre": "^1.1.4",
"@emoji-mart/data": "^1.2.1",
"@floating-ui/react": "^0.26.25",
"@formatjs/intl-localematcher": "^0.5.6",
@@ -108,7 +107,7 @@
"react": "19.1.1",
"react-18-input-autosize": "^3.0.0",
"react-dom": "19.1.1",
- "react-easy-crop": "^5.1.0",
+ "react-easy-crop": "^5.5.3",
"react-hook-form": "^7.53.1",
"react-hotkeys-hook": "^4.6.1",
"react-i18next": "^15.1.0",
@@ -164,7 +163,6 @@
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/react": "^16.0.1",
- "@types/dagre": "^0.7.52",
"@types/jest": "^29.5.13",
"@types/js-cookie": "^3.0.6",
"@types/lodash-es": "^4.17.12",
@@ -180,7 +178,7 @@
"@types/sortablejs": "^1.15.1",
"@types/uuid": "^10.0.0",
"autoprefixer": "^10.4.20",
- "babel-loader": "^9.2.1",
+ "babel-loader": "^10.0.0",
"bing-translate-api": "^4.0.2",
"code-inspector-plugin": "1.2.9",
"cross-env": "^7.0.3",
diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml
index 17b3cebfb8..4778eba359 100644
--- a/web/pnpm-lock.yaml
+++ b/web/pnpm-lock.yaml
@@ -49,9 +49,6 @@ importers:
.:
dependencies:
- '@dagrejs/dagre':
- specifier: ^1.1.4
- version: 1.1.5
'@emoji-mart/data':
specifier: ^1.2.1
version: 1.2.1
@@ -245,8 +242,8 @@ importers:
specifier: 19.1.1
version: 19.1.1(react@19.1.1)
react-easy-crop:
- specifier: ^5.1.0
- version: 5.5.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ specifier: ^5.5.3
+ version: 5.5.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
react-hook-form:
specifier: ^7.53.1
version: 7.60.0(react@19.1.1)
@@ -407,9 +404,6 @@ importers:
'@testing-library/react':
specifier: ^16.0.1
version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
- '@types/dagre':
- specifier: ^0.7.52
- version: 0.7.53
'@types/jest':
specifier: ^29.5.13
version: 29.5.14
@@ -456,8 +450,8 @@ importers:
specifier: ^10.4.20
version: 10.4.21(postcss@8.5.6)
babel-loader:
- specifier: ^9.2.1
- version: 9.2.1(@babel/core@7.28.3)(webpack@5.100.2(esbuild@0.25.0)(uglify-js@3.19.3))
+ specifier: ^10.0.0
+ version: 10.0.0(@babel/core@7.28.3)(webpack@5.100.2(esbuild@0.25.0)(uglify-js@3.19.3))
bing-translate-api:
specifier: ^4.0.2
version: 4.1.0
@@ -1331,13 +1325,6 @@ packages:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
- '@dagrejs/dagre@1.1.5':
- resolution: {integrity: sha512-Ghgrh08s12DCL5SeiR6AoyE80mQELTWhJBRmXfFoqDiFkR458vPEdgTbbjA0T+9ETNxUblnD0QW55tfdvi5pjQ==}
-
- '@dagrejs/graphlib@2.2.4':
- resolution: {integrity: sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==}
- engines: {node: '>17.0.0'}
-
'@discoveryjs/json-ext@0.5.7':
resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==}
engines: {node: '>=10.0.0'}
@@ -3375,9 +3362,6 @@ packages:
'@types/d3@7.4.3':
resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==}
- '@types/dagre@0.7.53':
- resolution: {integrity: sha512-f4gkWqzPZvYmKhOsDnhq/R8mO4UMcKdxZo+i5SCkOU1wvGeHJeUXGIHeE9pnwGyPMDof1Vx5ZQo4nxpeg2TTVQ==}
-
'@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
@@ -3936,6 +3920,13 @@ packages:
peerDependencies:
'@babel/core': ^7.8.0
+ babel-loader@10.0.0:
+ resolution: {integrity: sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==}
+ engines: {node: ^18.20.0 || ^20.10.0 || >=22.0.0}
+ peerDependencies:
+ '@babel/core': ^7.12.0
+ webpack: '>=5.61.0'
+
babel-loader@8.4.1:
resolution: {integrity: sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==}
engines: {node: '>= 8.9'}
@@ -7307,8 +7298,8 @@ packages:
react: '>= 16.3.0'
react-dom: '>= 16.3.0'
- react-easy-crop@5.5.0:
- resolution: {integrity: sha512-OZzU+yXMhe69vLkDex+5QxcfT94FdcgVCyW2dBUw35ZoC3Is42TUxUy04w8nH1mfMKaizVdC3rh/wUfNW1mK4w==}
+ react-easy-crop@5.5.3:
+ resolution: {integrity: sha512-iKwFTnAsq+IVuyF6N0Q3zjRx9DG1NMySkwWxVfM/xAOeHYH1vhvM+V2kFiq5HOIQGWouITjfltCx54mbDpMpmA==}
peerDependencies:
react: '>=16.4.0'
react-dom: '>=16.4.0'
@@ -9799,12 +9790,6 @@ snapshots:
'@jridgewell/trace-mapping': 0.3.9
optional: true
- '@dagrejs/dagre@1.1.5':
- dependencies:
- '@dagrejs/graphlib': 2.2.4
-
- '@dagrejs/graphlib@2.2.4': {}
-
'@discoveryjs/json-ext@0.5.7': {}
'@emnapi/core@1.5.0':
@@ -12137,8 +12122,6 @@ snapshots:
'@types/d3-transition': 3.0.9
'@types/d3-zoom': 3.0.8
- '@types/dagre@0.7.53': {}
-
'@types/debug@4.1.12':
dependencies:
'@types/ms': 2.1.0
@@ -12795,6 +12778,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ babel-loader@10.0.0(@babel/core@7.28.3)(webpack@5.100.2(esbuild@0.25.0)(uglify-js@3.19.3)):
+ dependencies:
+ '@babel/core': 7.28.3
+ find-up: 5.0.0
+ webpack: 5.100.2(esbuild@0.25.0)(uglify-js@3.19.3)
+
babel-loader@8.4.1(@babel/core@7.28.3)(webpack@5.100.2(esbuild@0.25.0)(uglify-js@3.19.3)):
dependencies:
'@babel/core': 7.28.3
@@ -17043,7 +17032,7 @@ snapshots:
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
- react-easy-crop@5.5.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
+ react-easy-crop@5.5.3(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
dependencies:
normalize-wheel: 1.0.1
react: 19.1.1
diff --git a/web/service/base.ts b/web/service/base.ts
index 526c8d75d2..358f54183b 100644
--- a/web/service/base.ts
+++ b/web/service/base.ts
@@ -180,9 +180,9 @@ const handleStream = (
let isFirstMessage = true
function read() {
let hasError = false
- reader?.read().then((result: any) => {
+ reader?.read().then((result: ReadableStreamReadResult) => {
if (result.done) {
- onCompleted && onCompleted()
+ onCompleted?.()
return
}
buffer += decoder.decode(result.value, { stream: true })
@@ -322,7 +322,21 @@ const handleStream = (
const baseFetch = base
-export const upload = async (options: any, isPublicAPI?: boolean, url?: string, searchParams?: string): Promise => {
+type UploadOptions = {
+ xhr: XMLHttpRequest
+ method: string
+ url?: string
+ headers?: Record
+ data: FormData
+ onprogress?: (this: XMLHttpRequest, ev: ProgressEvent) => void
+}
+
+type UploadResponse = {
+ id: string
+ [key: string]: unknown
+}
+
+export const upload = async (options: UploadOptions, isPublicAPI?: boolean, url?: string, searchParams?: string): Promise => {
const urlPrefix = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX
const token = await getAccessToken(isPublicAPI)
const defaultOptions = {
@@ -331,18 +345,18 @@ export const upload = async (options: any, isPublicAPI?: boolean, url?: string,
headers: {
Authorization: `Bearer ${token}`,
},
- data: {},
}
- options = {
+ const mergedOptions = {
...defaultOptions,
...options,
- headers: { ...defaultOptions.headers, ...options.headers },
+ url: options.url || defaultOptions.url,
+ headers: { ...defaultOptions.headers, ...options.headers } as Record,
}
return new Promise((resolve, reject) => {
- const xhr = options.xhr
- xhr.open(options.method, options.url)
- for (const key in options.headers)
- xhr.setRequestHeader(key, options.headers[key])
+ const xhr = mergedOptions.xhr
+ xhr.open(mergedOptions.method, mergedOptions.url)
+ for (const key in mergedOptions.headers)
+ xhr.setRequestHeader(key, mergedOptions.headers[key])
xhr.withCredentials = true
xhr.responseType = 'json'
@@ -354,8 +368,9 @@ export const upload = async (options: any, isPublicAPI?: boolean, url?: string,
reject(xhr)
}
}
- xhr.upload.onprogress = options.onprogress
- xhr.send(options.data)
+ if (mergedOptions.onprogress)
+ xhr.upload.onprogress = mergedOptions.onprogress
+ xhr.send(mergedOptions.data)
})
}
@@ -432,7 +447,7 @@ export const ssePost = async (
if (!/^[23]\d{2}$/.test(String(res.status))) {
if (res.status === 401) {
if (isPublicAPI) {
- res.json().then((data: any) => {
+ res.json().then((data: { code?: string; message?: string }) => {
if (isPublicAPI) {
if (data.code === 'web_app_access_denied')
requiredWebSSOLogin(data.message, 403)
diff --git a/web/service/knowledge/use-hit-testing.ts b/web/service/knowledge/use-hit-testing.ts
index e69de29bb2..336ce12bb9 100644
--- a/web/service/knowledge/use-hit-testing.ts
+++ b/web/service/knowledge/use-hit-testing.ts
@@ -0,0 +1 @@
+export {}
diff --git a/web/service/share.ts b/web/service/share.ts
index f1e512564b..ab8e0deb4a 100644
--- a/web/service/share.ts
+++ b/web/service/share.ts
@@ -290,8 +290,10 @@ export const fetchAccessToken = async ({ appCode, userId, webAppAccessToken }: {
const headers = new Headers()
headers.append('X-App-Code', appCode)
const params = new URLSearchParams()
- webAppAccessToken && params.append('web_app_access_token', webAppAccessToken)
- userId && params.append('user_id', userId)
+ if (webAppAccessToken)
+ params.append('web_app_access_token', webAppAccessToken)
+ if (userId)
+ params.append('user_id', userId)
const url = `/passport?${params.toString()}`
return get(url, { headers }) as Promise<{ access_token: string }>
}
diff --git a/web/utils/mcp.ts b/web/utils/mcp.ts
new file mode 100644
index 0000000000..dcbb63ee8a
--- /dev/null
+++ b/web/utils/mcp.ts
@@ -0,0 +1,22 @@
+/**
+ * MCP (Model Context Protocol) utility functions
+ */
+
+/**
+ * Determines if the MCP icon should be used based on the icon source
+ * @param src - The icon source, can be a string URL or an object with content and background
+ * @returns true if the MCP icon should be used (when it's an emoji object with 🔗 content)
+ */
+export const shouldUseMcpIcon = (src: any): boolean => {
+ return typeof src === 'object' && src?.content === '🔗'
+}
+
+/**
+ * Checks if an app icon should use the MCP icon
+ * @param iconType - The type of icon ('emoji' | 'image')
+ * @param icon - The icon content (emoji or file ID)
+ * @returns true if the MCP icon should be used
+ */
+export const shouldUseMcpIconForAppIcon = (iconType: string, icon: string): boolean => {
+ return iconType === 'emoji' && icon === '🔗'
+}
diff --git a/web/utils/model-config.ts b/web/utils/model-config.ts
index c940a6969f..3f655ce036 100644
--- a/web/utils/model-config.ts
+++ b/web/utils/model-config.ts
@@ -61,6 +61,17 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] |
default: content.default,
})
}
+ else if (type === 'boolean') {
+ promptVariables.push({
+ key: content.variable,
+ name: content.label,
+ required: content.required,
+ type: 'checkbox',
+ options: [],
+ hide: content.hide,
+ default: content.default,
+ })
+ }
else if (type === 'select') {
promptVariables.push({
key: content.variable,
diff --git a/web/utils/var.ts b/web/utils/var.ts
index a9849bdc4b..3181d2bbd7 100644
--- a/web/utils/var.ts
+++ b/web/utils/var.ts
@@ -29,7 +29,7 @@ export const getNewVar = (key: string, type: string) => {
}
export const getNewVarInWorkflow = (key: string, type = InputVarType.textInput): InputVar => {
- const { max_length, ...rest } = VAR_ITEM_TEMPLATE_IN_WORKFLOW
+ const { max_length: _maxLength, ...rest } = VAR_ITEM_TEMPLATE_IN_WORKFLOW
if (type !== InputVarType.textInput) {
return {
...rest,
@@ -49,7 +49,7 @@ export const getNewVarInWorkflow = (key: string, type = InputVarType.textInput):
}
}
-export const checkKey = (key: string, canBeEmpty?: boolean, keys?: string[]) => {
+export const checkKey = (key: string, canBeEmpty?: boolean, _keys?: string[]) => {
if (key.length === 0 && !canBeEmpty)
return 'canNoBeEmpty'