diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 0000000000..7af24b7ddb
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+22.11.0
diff --git a/api/controllers/console/explore/completion.py b/api/controllers/console/explore/completion.py
index 78e9a87a3d..5901eca915 100644
--- a/api/controllers/console/explore/completion.py
+++ b/api/controllers/console/explore/completion.py
@@ -2,7 +2,7 @@ import logging
from typing import Any, Literal
from uuid import UUID
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Field, field_validator
from werkzeug.exceptions import InternalServerError, NotFound
import services
@@ -52,10 +52,24 @@ class ChatMessagePayload(BaseModel):
inputs: dict[str, Any]
query: str
files: list[dict[str, Any]] | None = None
- conversation_id: UUID | None = None
- parent_message_id: UUID | None = None
+ conversation_id: str | None = None
+ parent_message_id: str | None = None
retriever_from: str = Field(default="explore_app")
+ @field_validator("conversation_id", "parent_message_id", mode="before")
+ @classmethod
+ def normalize_uuid(cls, value: str | UUID | None) -> str | None:
+ """
+ Accept blank IDs and validate UUID format when provided.
+ """
+ if not value:
+ return None
+
+ try:
+ return helper.uuid_value(value)
+ except ValueError as exc:
+ raise ValueError("must be a valid UUID") from exc
+
register_schema_models(console_ns, CompletionMessagePayload, ChatMessagePayload)
diff --git a/api/controllers/console/explore/conversation.py b/api/controllers/console/explore/conversation.py
index 157d5a135b..92da591ab4 100644
--- a/api/controllers/console/explore/conversation.py
+++ b/api/controllers/console/explore/conversation.py
@@ -3,7 +3,7 @@ from uuid import UUID
from flask import request
from flask_restx import marshal_with
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Field, model_validator
from sqlalchemy.orm import Session
from werkzeug.exceptions import NotFound
@@ -30,9 +30,16 @@ class ConversationListQuery(BaseModel):
class ConversationRenamePayload(BaseModel):
- name: str
+ name: str | None = None
auto_generate: bool = False
+ @model_validator(mode="after")
+ def validate_name_requirement(self):
+ if not self.auto_generate:
+ if self.name is None or not self.name.strip():
+ raise ValueError("name is required when auto_generate is false")
+ return self
+
register_schema_models(console_ns, ConversationListQuery, ConversationRenamePayload)
diff --git a/api/controllers/console/workspace/models.py b/api/controllers/console/workspace/models.py
index 246a869291..a5b45ef514 100644
--- a/api/controllers/console/workspace/models.py
+++ b/api/controllers/console/workspace/models.py
@@ -230,7 +230,7 @@ class ModelProviderModelApi(Resource):
return {"result": "success"}, 200
- @console_ns.expect(console_ns.models[ParserDeleteModels.__name__], validate=True)
+ @console_ns.expect(console_ns.models[ParserDeleteModels.__name__])
@setup_required
@login_required
@is_admin_or_owner_required
diff --git a/api/controllers/service_api/app/conversation.py b/api/controllers/service_api/app/conversation.py
index 724ad3448d..be6d837032 100644
--- a/api/controllers/service_api/app/conversation.py
+++ b/api/controllers/service_api/app/conversation.py
@@ -4,7 +4,7 @@ from uuid import UUID
from flask import request
from flask_restx import Resource
from flask_restx._http import HTTPStatus
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Field, model_validator
from sqlalchemy.orm import Session
from werkzeug.exceptions import BadRequest, NotFound
@@ -37,9 +37,16 @@ class ConversationListQuery(BaseModel):
class ConversationRenamePayload(BaseModel):
- name: str = Field(description="New conversation name")
+ name: str | None = Field(default=None, description="New conversation name (required if auto_generate is false)")
auto_generate: bool = Field(default=False, description="Auto-generate conversation name")
+ @model_validator(mode="after")
+ def validate_name_requirement(self):
+ if not self.auto_generate:
+ if self.name is None or not self.name.strip():
+ raise ValueError("name is required when auto_generate is false")
+ return self
+
class ConversationVariablesQuery(BaseModel):
last_id: UUID | None = Field(default=None, description="Last variable ID for pagination")
diff --git a/api/core/app/apps/advanced_chat/generate_task_pipeline.py b/api/core/app/apps/advanced_chat/generate_task_pipeline.py
index b297f3ff20..da1e9f19b6 100644
--- a/api/core/app/apps/advanced_chat/generate_task_pipeline.py
+++ b/api/core/app/apps/advanced_chat/generate_task_pipeline.py
@@ -62,8 +62,7 @@ from core.app.task_pipeline.message_cycle_manager import MessageCycleManager
from core.base.tts import AppGeneratorTTSPublisher, AudioTrunk
from core.model_runtime.entities.llm_entities import LLMUsage
from core.model_runtime.utils.encoders import jsonable_encoder
-from core.ops.entities.trace_entity import TraceTaskName
-from core.ops.ops_trace_manager import TraceQueueManager, TraceTask
+from core.ops.ops_trace_manager import TraceQueueManager
from core.workflow.enums import WorkflowExecutionStatus
from core.workflow.nodes import NodeType
from core.workflow.repositories.draft_variable_repository import DraftVariableSaverFactory
@@ -73,7 +72,7 @@ from extensions.ext_database import db
from libs.datetime_utils import naive_utc_now
from models import Account, Conversation, EndUser, Message, MessageFile
from models.enums import CreatorUserRole
-from models.workflow import Workflow, WorkflowNodeExecutionModel
+from models.workflow import Workflow
logger = logging.getLogger(__name__)
@@ -581,7 +580,7 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport):
with self._database_session() as session:
# Save message
- self._save_message(session=session, graph_runtime_state=resolved_state, trace_manager=trace_manager)
+ self._save_message(session=session, graph_runtime_state=resolved_state)
yield workflow_finish_resp
elif event.stopped_by in (
@@ -591,7 +590,7 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport):
# When hitting input-moderation or annotation-reply, the workflow will not start
with self._database_session() as session:
# Save message
- self._save_message(session=session, trace_manager=trace_manager)
+ self._save_message(session=session)
yield self._message_end_to_stream_response()
@@ -600,7 +599,6 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport):
event: QueueAdvancedChatMessageEndEvent,
*,
graph_runtime_state: GraphRuntimeState | None = None,
- trace_manager: TraceQueueManager | None = None,
**kwargs,
) -> Generator[StreamResponse, None, None]:
"""Handle advanced chat message end events."""
@@ -618,7 +616,7 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport):
# Save message
with self._database_session() as session:
- self._save_message(session=session, graph_runtime_state=resolved_state, trace_manager=trace_manager)
+ self._save_message(session=session, graph_runtime_state=resolved_state)
yield self._message_end_to_stream_response()
@@ -772,13 +770,7 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport):
if self._conversation_name_generate_thread:
logger.debug("Conversation name generation running as daemon thread")
- def _save_message(
- self,
- *,
- session: Session,
- graph_runtime_state: GraphRuntimeState | None = None,
- trace_manager: TraceQueueManager | None = None,
- ):
+ def _save_message(self, *, session: Session, graph_runtime_state: GraphRuntimeState | None = None):
message = self._get_message(session=session)
# If there are assistant files, remove markdown image links from answer
@@ -817,14 +809,6 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport):
metadata = self._task_state.metadata.model_dump()
message.message_metadata = json.dumps(jsonable_encoder(metadata))
-
- # Extract model provider and model_id from workflow node executions for tracing
- if message.workflow_run_id:
- model_info = self._extract_model_info_from_workflow(session, message.workflow_run_id)
- if model_info:
- message.model_provider = model_info.get("provider")
- message.model_id = model_info.get("model")
-
message_files = [
MessageFile(
message_id=message.id,
@@ -842,68 +826,6 @@ class AdvancedChatAppGenerateTaskPipeline(GraphRuntimeStateSupport):
]
session.add_all(message_files)
- # Trigger MESSAGE_TRACE for tracing integrations
- if trace_manager:
- trace_manager.add_trace_task(
- TraceTask(
- TraceTaskName.MESSAGE_TRACE, conversation_id=self._conversation_id, message_id=self._message_id
- )
- )
-
- def _extract_model_info_from_workflow(self, session: Session, workflow_run_id: str) -> dict[str, str] | None:
- """
- Extract model provider and model_id from workflow node executions.
- Returns dict with 'provider' and 'model' keys, or None if not found.
- """
- try:
- # Query workflow node executions for LLM or Agent nodes
- stmt = (
- select(WorkflowNodeExecutionModel)
- .where(WorkflowNodeExecutionModel.workflow_run_id == workflow_run_id)
- .where(WorkflowNodeExecutionModel.node_type.in_(["llm", "agent"]))
- .order_by(WorkflowNodeExecutionModel.created_at.desc())
- .limit(1)
- )
- node_execution = session.scalar(stmt)
-
- if not node_execution:
- return None
-
- # Try to extract from execution_metadata for agent nodes
- if node_execution.execution_metadata:
- try:
- metadata = json.loads(node_execution.execution_metadata)
- agent_log = metadata.get("agent_log", [])
- # Look for the first agent thought with provider info
- for log_entry in agent_log:
- entry_metadata = log_entry.get("metadata", {})
- provider_str = entry_metadata.get("provider")
- if provider_str:
- # Parse format like "langgenius/deepseek/deepseek"
- parts = provider_str.split("/")
- if len(parts) >= 3:
- return {"provider": parts[1], "model": parts[2]}
- elif len(parts) == 2:
- return {"provider": parts[0], "model": parts[1]}
- except (json.JSONDecodeError, KeyError, AttributeError) as e:
- logger.debug("Failed to parse execution_metadata: %s", e)
-
- # Try to extract from process_data for llm nodes
- if node_execution.process_data:
- try:
- process_data = json.loads(node_execution.process_data)
- provider = process_data.get("model_provider")
- model = process_data.get("model_name")
- if provider and model:
- return {"provider": provider, "model": model}
- except (json.JSONDecodeError, KeyError) as e:
- logger.debug("Failed to parse process_data: %s", e)
-
- return None
- except Exception as e:
- logger.warning("Failed to extract model info from workflow: %s", e)
- return None
-
def _seed_graph_runtime_state_from_queue_manager(self) -> None:
"""Bootstrap the cached runtime state from the queue manager when present."""
candidate = self._base_task_pipeline.queue_manager.graph_runtime_state
diff --git a/api/core/app/entities/task_entities.py b/api/core/app/entities/task_entities.py
index 7692128985..79a5e657b3 100644
--- a/api/core/app/entities/task_entities.py
+++ b/api/core/app/entities/task_entities.py
@@ -40,9 +40,6 @@ class EasyUITaskState(TaskState):
"""
llm_result: LLMResult
- first_token_time: float | None = None
- last_token_time: float | None = None
- is_streaming_response: bool = False
class WorkflowTaskState(TaskState):
diff --git a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py
index 98548ddfbb..5c169f4db1 100644
--- a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py
+++ b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py
@@ -332,12 +332,6 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline):
if not self._task_state.llm_result.prompt_messages:
self._task_state.llm_result.prompt_messages = chunk.prompt_messages
- # Track streaming response times
- if self._task_state.first_token_time is None:
- self._task_state.first_token_time = time.perf_counter()
- self._task_state.is_streaming_response = True
- self._task_state.last_token_time = time.perf_counter()
-
# handle output moderation chunk
should_direct_answer = self._handle_output_moderation_chunk(cast(str, delta_text))
if should_direct_answer:
@@ -404,18 +398,6 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline):
message.total_price = usage.total_price
message.currency = usage.currency
self._task_state.llm_result.usage.latency = message.provider_response_latency
-
- # Add streaming metrics to usage if available
- if self._task_state.is_streaming_response and self._task_state.first_token_time:
- start_time = self.start_at
- first_token_time = self._task_state.first_token_time
- last_token_time = self._task_state.last_token_time or first_token_time
- usage.time_to_first_token = round(first_token_time - start_time, 3)
- usage.time_to_generate = round(last_token_time - first_token_time, 3)
-
- # Update metadata with the complete usage info
- self._task_state.metadata.usage = usage
-
message.message_metadata = self._task_state.metadata.model_dump_json()
if trace_manager:
diff --git a/api/core/entities/knowledge_entities.py b/api/core/entities/knowledge_entities.py
index b9ca7414dc..bed3a35400 100644
--- a/api/core/entities/knowledge_entities.py
+++ b/api/core/entities/knowledge_entities.py
@@ -1,4 +1,4 @@
-from pydantic import BaseModel
+from pydantic import BaseModel, Field
class PreviewDetail(BaseModel):
@@ -20,7 +20,7 @@ class IndexingEstimate(BaseModel):
class PipelineDataset(BaseModel):
id: str
name: str
- description: str
+ description: str | None = Field(default="", description="knowledge dataset description")
chunk_structure: str
diff --git a/api/core/ops/tencent_trace/span_builder.py b/api/core/ops/tencent_trace/span_builder.py
index db92e9b8bd..26e8779e3e 100644
--- a/api/core/ops/tencent_trace/span_builder.py
+++ b/api/core/ops/tencent_trace/span_builder.py
@@ -222,59 +222,6 @@ class TencentSpanBuilder:
links=links,
)
- @staticmethod
- def build_message_llm_span(
- trace_info: MessageTraceInfo, trace_id: int, parent_span_id: int, user_id: str
- ) -> SpanData:
- """Build LLM span for message traces with detailed LLM attributes."""
- status = Status(StatusCode.OK)
- if trace_info.error:
- status = Status(StatusCode.ERROR, trace_info.error)
-
- # Extract model information from `metadata`` or `message_data`
- trace_metadata = trace_info.metadata or {}
- message_data = trace_info.message_data or {}
-
- model_provider = trace_metadata.get("ls_provider") or (
- message_data.get("model_provider", "") if isinstance(message_data, dict) else ""
- )
- model_name = trace_metadata.get("ls_model_name") or (
- message_data.get("model_id", "") if isinstance(message_data, dict) else ""
- )
-
- inputs_str = str(trace_info.inputs or "")
- outputs_str = str(trace_info.outputs or "")
-
- attributes = {
- GEN_AI_SESSION_ID: trace_metadata.get("conversation_id", ""),
- GEN_AI_USER_ID: str(user_id),
- GEN_AI_SPAN_KIND: GenAISpanKind.GENERATION.value,
- GEN_AI_FRAMEWORK: "dify",
- GEN_AI_MODEL_NAME: str(model_name),
- GEN_AI_PROVIDER: str(model_provider),
- GEN_AI_USAGE_INPUT_TOKENS: str(trace_info.message_tokens or 0),
- GEN_AI_USAGE_OUTPUT_TOKENS: str(trace_info.answer_tokens or 0),
- GEN_AI_USAGE_TOTAL_TOKENS: str(trace_info.total_tokens or 0),
- GEN_AI_PROMPT: inputs_str,
- GEN_AI_COMPLETION: outputs_str,
- INPUT_VALUE: inputs_str,
- OUTPUT_VALUE: outputs_str,
- }
-
- if trace_info.is_streaming_request:
- attributes[GEN_AI_IS_STREAMING_REQUEST] = "true"
-
- return SpanData(
- trace_id=trace_id,
- parent_span_id=parent_span_id,
- span_id=TencentTraceUtils.convert_to_span_id(trace_info.message_id, "llm"),
- name="GENERATION",
- start_time=TencentSpanBuilder._get_time_nanoseconds(trace_info.start_time),
- end_time=TencentSpanBuilder._get_time_nanoseconds(trace_info.end_time),
- attributes=attributes,
- status=status,
- )
-
@staticmethod
def build_tool_span(trace_info: ToolTraceInfo, trace_id: int, parent_span_id: int) -> SpanData:
"""Build tool span."""
diff --git a/api/core/ops/tencent_trace/tencent_trace.py b/api/core/ops/tencent_trace/tencent_trace.py
index c345cee7a9..93ec186863 100644
--- a/api/core/ops/tencent_trace/tencent_trace.py
+++ b/api/core/ops/tencent_trace/tencent_trace.py
@@ -107,12 +107,8 @@ class TencentDataTrace(BaseTraceInstance):
links.append(TencentTraceUtils.create_link(trace_info.trace_id))
message_span = TencentSpanBuilder.build_message_span(trace_info, trace_id, str(user_id), links)
- self.trace_client.add_span(message_span)
- # Add LLM child span with detailed attributes
- parent_span_id = TencentTraceUtils.convert_to_span_id(trace_info.message_id, "message")
- llm_span = TencentSpanBuilder.build_message_llm_span(trace_info, trace_id, parent_span_id, str(user_id))
- self.trace_client.add_span(llm_span)
+ self.trace_client.add_span(message_span)
self._record_message_llm_metrics(trace_info)
diff --git a/api/core/rag/retrieval/dataset_retrieval.py b/api/core/rag/retrieval/dataset_retrieval.py
index a65069b1b7..635eab73f0 100644
--- a/api/core/rag/retrieval/dataset_retrieval.py
+++ b/api/core/rag/retrieval/dataset_retrieval.py
@@ -592,111 +592,116 @@ class DatasetRetrieval:
"""Handle retrieval end."""
with flask_app.app_context():
dify_documents = [document for document in documents if document.provider == "dify"]
- segment_ids = []
- segment_index_node_ids = []
+ if not dify_documents:
+ self._send_trace_task(message_id, documents, timer)
+ return
+
with Session(db.engine) as session:
- for document in dify_documents:
- if document.metadata is not None:
- dataset_document_stmt = select(DatasetDocument).where(
- DatasetDocument.id == document.metadata["document_id"]
- )
- dataset_document = session.scalar(dataset_document_stmt)
- if dataset_document:
- if dataset_document.doc_form == IndexStructureType.PARENT_CHILD_INDEX:
- segment_id = None
- if (
- "doc_type" not in document.metadata
- or document.metadata.get("doc_type") == DocType.TEXT
- ):
- child_chunk_stmt = select(ChildChunk).where(
- ChildChunk.index_node_id == document.metadata["doc_id"],
- ChildChunk.dataset_id == dataset_document.dataset_id,
- ChildChunk.document_id == dataset_document.id,
- )
- child_chunk = session.scalar(child_chunk_stmt)
- if child_chunk:
- segment_id = child_chunk.segment_id
- elif (
- "doc_type" in document.metadata
- and document.metadata.get("doc_type") == DocType.IMAGE
- ):
- attachment_info_dict = RetrievalService.get_segment_attachment_info(
- dataset_document.dataset_id,
- dataset_document.tenant_id,
- document.metadata.get("doc_id") or "",
- session,
- )
- if attachment_info_dict:
- segment_id = attachment_info_dict["segment_id"]
+ # Collect all document_ids and batch fetch DatasetDocuments
+ document_ids = {
+ doc.metadata["document_id"]
+ for doc in dify_documents
+ if doc.metadata and "document_id" in doc.metadata
+ }
+ if not document_ids:
+ self._send_trace_task(message_id, documents, timer)
+ return
+
+ dataset_docs_stmt = select(DatasetDocument).where(DatasetDocument.id.in_(document_ids))
+ dataset_docs = session.scalars(dataset_docs_stmt).all()
+ dataset_doc_map = {str(doc.id): doc for doc in dataset_docs}
+
+ # Categorize documents by type and collect necessary IDs
+ parent_child_text_docs: list[tuple[Document, DatasetDocument]] = []
+ parent_child_image_docs: list[tuple[Document, DatasetDocument]] = []
+ normal_text_docs: list[tuple[Document, DatasetDocument]] = []
+ normal_image_docs: list[tuple[Document, DatasetDocument]] = []
+
+ for doc in dify_documents:
+ if not doc.metadata or "document_id" not in doc.metadata:
+ continue
+ dataset_doc = dataset_doc_map.get(doc.metadata["document_id"])
+ if not dataset_doc:
+ continue
+
+ is_image = doc.metadata.get("doc_type") == DocType.IMAGE
+ is_parent_child = dataset_doc.doc_form == IndexStructureType.PARENT_CHILD_INDEX
+
+ if is_parent_child:
+ if is_image:
+ parent_child_image_docs.append((doc, dataset_doc))
+ else:
+ parent_child_text_docs.append((doc, dataset_doc))
+ else:
+ if is_image:
+ normal_image_docs.append((doc, dataset_doc))
+ else:
+ normal_text_docs.append((doc, dataset_doc))
+
+ segment_ids_to_update: set[str] = set()
+
+ # Process PARENT_CHILD_INDEX text documents - batch fetch ChildChunks
+ if parent_child_text_docs:
+ index_node_ids = [doc.metadata["doc_id"] for doc, _ in parent_child_text_docs if doc.metadata]
+ if index_node_ids:
+ child_chunks_stmt = select(ChildChunk).where(ChildChunk.index_node_id.in_(index_node_ids))
+ child_chunks = session.scalars(child_chunks_stmt).all()
+ child_chunk_map = {chunk.index_node_id: chunk.segment_id for chunk in child_chunks}
+ for doc, _ in parent_child_text_docs:
+ if doc.metadata:
+ segment_id = child_chunk_map.get(doc.metadata["doc_id"])
if segment_id:
- if segment_id not in segment_ids:
- segment_ids.append(segment_id)
- _ = (
- session.query(DocumentSegment)
- .where(DocumentSegment.id == segment_id)
- .update(
- {DocumentSegment.hit_count: DocumentSegment.hit_count + 1},
- synchronize_session=False,
- )
- )
- else:
- query = None
- if (
- "doc_type" not in document.metadata
- or document.metadata.get("doc_type") == DocType.TEXT
- ):
- if document.metadata["doc_id"] not in segment_index_node_ids:
- segment = (
- session.query(DocumentSegment)
- .where(DocumentSegment.index_node_id == document.metadata["doc_id"])
- .first()
- )
- if segment:
- segment_index_node_ids.append(document.metadata["doc_id"])
- segment_ids.append(segment.id)
- query = session.query(DocumentSegment).where(
- DocumentSegment.id == segment.id
- )
- elif (
- "doc_type" in document.metadata
- and document.metadata.get("doc_type") == DocType.IMAGE
- ):
- attachment_info_dict = RetrievalService.get_segment_attachment_info(
- dataset_document.dataset_id,
- dataset_document.tenant_id,
- document.metadata.get("doc_id") or "",
- session,
- )
- if attachment_info_dict:
- segment_id = attachment_info_dict["segment_id"]
- if segment_id not in segment_ids:
- segment_ids.append(segment_id)
- query = session.query(DocumentSegment).where(DocumentSegment.id == segment_id)
- if query:
- # if 'dataset_id' in document.metadata:
- if "dataset_id" in document.metadata:
- query = query.where(
- DocumentSegment.dataset_id == document.metadata["dataset_id"]
- )
+ segment_ids_to_update.add(str(segment_id))
- # add hit count to document segment
- query.update(
- {DocumentSegment.hit_count: DocumentSegment.hit_count + 1},
- synchronize_session=False,
- )
+ # Process non-PARENT_CHILD_INDEX text documents - batch fetch DocumentSegments
+ if normal_text_docs:
+ index_node_ids = [doc.metadata["doc_id"] for doc, _ in normal_text_docs if doc.metadata]
+ if index_node_ids:
+ segments_stmt = select(DocumentSegment).where(DocumentSegment.index_node_id.in_(index_node_ids))
+ segments = session.scalars(segments_stmt).all()
+ segment_map = {seg.index_node_id: seg.id for seg in segments}
+ for doc, _ in normal_text_docs:
+ if doc.metadata:
+ segment_id = segment_map.get(doc.metadata["doc_id"])
+ if segment_id:
+ segment_ids_to_update.add(str(segment_id))
- db.session.commit()
+ # Process IMAGE documents - batch fetch SegmentAttachmentBindings
+ all_image_docs = parent_child_image_docs + normal_image_docs
+ if all_image_docs:
+ attachment_ids = [
+ doc.metadata["doc_id"]
+ for doc, _ in all_image_docs
+ if doc.metadata and doc.metadata.get("doc_id")
+ ]
+ if attachment_ids:
+ bindings_stmt = select(SegmentAttachmentBinding).where(
+ SegmentAttachmentBinding.attachment_id.in_(attachment_ids)
+ )
+ bindings = session.scalars(bindings_stmt).all()
+ segment_ids_to_update.update(str(binding.segment_id) for binding in bindings)
- # get tracing instance
- trace_manager: TraceQueueManager | None = (
- self.application_generate_entity.trace_manager if self.application_generate_entity else None
- )
- if trace_manager:
- trace_manager.add_trace_task(
- TraceTask(
- TraceTaskName.DATASET_RETRIEVAL_TRACE, message_id=message_id, documents=documents, timer=timer
+ # Batch update hit_count for all segments
+ if segment_ids_to_update:
+ session.query(DocumentSegment).where(DocumentSegment.id.in_(segment_ids_to_update)).update(
+ {DocumentSegment.hit_count: DocumentSegment.hit_count + 1},
+ synchronize_session=False,
)
+ session.commit()
+
+ self._send_trace_task(message_id, documents, timer)
+
+ def _send_trace_task(self, message_id: str | None, documents: list[Document], timer: dict | None):
+ """Send trace task if trace manager is available."""
+ trace_manager: TraceQueueManager | None = (
+ self.application_generate_entity.trace_manager if self.application_generate_entity else None
+ )
+ if trace_manager:
+ trace_manager.add_trace_task(
+ TraceTask(
+ TraceTaskName.DATASET_RETRIEVAL_TRACE, message_id=message_id, documents=documents, timer=timer
)
+ )
def _on_query(
self,
diff --git a/api/core/workflow/nodes/base/entities.py b/api/core/workflow/nodes/base/entities.py
index e816e16d74..5aab6bbde4 100644
--- a/api/core/workflow/nodes/base/entities.py
+++ b/api/core/workflow/nodes/base/entities.py
@@ -59,7 +59,7 @@ class OutputVariableEntity(BaseModel):
"""
variable: str
- value_type: OutputVariableType
+ value_type: OutputVariableType = OutputVariableType.ANY
value_selector: Sequence[str]
@field_validator("value_type", mode="before")
diff --git a/api/migrations/versions/2025_11_15_2102-09cfdda155d1_mysql_adaptation.py b/api/migrations/versions/2025_11_15_2102-09cfdda155d1_mysql_adaptation.py
index a3f6c3cb19..877fa2f309 100644
--- a/api/migrations/versions/2025_11_15_2102-09cfdda155d1_mysql_adaptation.py
+++ b/api/migrations/versions/2025_11_15_2102-09cfdda155d1_mysql_adaptation.py
@@ -1,4 +1,4 @@
-"""empty message
+"""mysql adaptation
Revision ID: 09cfdda155d1
Revises: 669ffd70119c
@@ -97,11 +97,31 @@ def downgrade():
batch_op.alter_column('include_plugins',
existing_type=sa.JSON(),
type_=postgresql.ARRAY(sa.VARCHAR(length=255)),
- existing_nullable=False)
+ existing_nullable=False,
+ postgresql_using="""
+ COALESCE(
+ regexp_replace(
+ replace(replace(include_plugins::text, '[', '{'), ']', '}'),
+ '"',
+ '',
+ 'g'
+ )::varchar(255)[],
+ ARRAY[]::varchar(255)[]
+ )""")
batch_op.alter_column('exclude_plugins',
existing_type=sa.JSON(),
type_=postgresql.ARRAY(sa.VARCHAR(length=255)),
- existing_nullable=False)
+ existing_nullable=False,
+ postgresql_using="""
+ COALESCE(
+ regexp_replace(
+ replace(replace(exclude_plugins::text, '[', '{'), ']', '}'),
+ '"',
+ '',
+ 'g'
+ )::varchar(255)[],
+ ARRAY[]::varchar(255)[]
+ )""")
with op.batch_alter_table('external_knowledge_bindings', schema=None) as batch_op:
batch_op.alter_column('external_knowledge_id',
diff --git a/api/models/model.py b/api/models/model.py
index 6b0bf4b4a2..c8fbdc40ec 100644
--- a/api/models/model.py
+++ b/api/models/model.py
@@ -835,7 +835,29 @@ class Conversation(Base):
@property
def status_count(self):
- messages = db.session.scalars(select(Message).where(Message.conversation_id == self.id)).all()
+ from models.workflow import WorkflowRun
+
+ # Get all messages with workflow_run_id for this conversation
+ messages = db.session.scalars(
+ select(Message).where(Message.conversation_id == self.id, Message.workflow_run_id.isnot(None))
+ ).all()
+
+ if not messages:
+ return None
+
+ # Batch load all workflow runs in a single query, filtered by this conversation's app_id
+ workflow_run_ids = [msg.workflow_run_id for msg in messages if msg.workflow_run_id]
+ workflow_runs = {}
+
+ if workflow_run_ids:
+ workflow_runs_query = db.session.scalars(
+ select(WorkflowRun).where(
+ WorkflowRun.id.in_(workflow_run_ids),
+ WorkflowRun.app_id == self.app_id, # Filter by this conversation's app_id
+ )
+ ).all()
+ workflow_runs = {run.id: run for run in workflow_runs_query}
+
status_counts = {
WorkflowExecutionStatus.RUNNING: 0,
WorkflowExecutionStatus.SUCCEEDED: 0,
@@ -845,18 +867,24 @@ class Conversation(Base):
}
for message in messages:
- if message.workflow_run:
- status_counts[WorkflowExecutionStatus(message.workflow_run.status)] += 1
+ # Guard against None to satisfy type checker and avoid invalid dict lookups
+ if message.workflow_run_id is None:
+ continue
+ workflow_run = workflow_runs.get(message.workflow_run_id)
+ if not workflow_run:
+ continue
- return (
- {
- "success": status_counts[WorkflowExecutionStatus.SUCCEEDED],
- "failed": status_counts[WorkflowExecutionStatus.FAILED],
- "partial_success": status_counts[WorkflowExecutionStatus.PARTIAL_SUCCEEDED],
- }
- if messages
- else None
- )
+ try:
+ status_counts[WorkflowExecutionStatus(workflow_run.status)] += 1
+ except (ValueError, KeyError):
+ # Handle invalid status values gracefully
+ pass
+
+ return {
+ "success": status_counts[WorkflowExecutionStatus.SUCCEEDED],
+ "failed": status_counts[WorkflowExecutionStatus.FAILED],
+ "partial_success": status_counts[WorkflowExecutionStatus.PARTIAL_SUCCEEDED],
+ }
@property
def first_message(self):
@@ -1255,13 +1283,9 @@ class Message(Base):
"id": self.id,
"app_id": self.app_id,
"conversation_id": self.conversation_id,
- "model_provider": self.model_provider,
"model_id": self.model_id,
"inputs": self.inputs,
"query": self.query,
- "message_tokens": self.message_tokens,
- "answer_tokens": self.answer_tokens,
- "provider_response_latency": self.provider_response_latency,
"total_price": self.total_price,
"message": self.message,
"answer": self.answer,
@@ -1283,12 +1307,8 @@ class Message(Base):
id=data["id"],
app_id=data["app_id"],
conversation_id=data["conversation_id"],
- model_provider=data.get("model_provider"),
model_id=data["model_id"],
inputs=data["inputs"],
- message_tokens=data.get("message_tokens", 0),
- answer_tokens=data.get("answer_tokens", 0),
- provider_response_latency=data.get("provider_response_latency", 0.0),
total_price=data["total_price"],
query=data["query"],
message=data["message"],
diff --git a/api/services/conversation_service.py b/api/services/conversation_service.py
index 39d6c81621..5253199552 100644
--- a/api/services/conversation_service.py
+++ b/api/services/conversation_service.py
@@ -118,7 +118,7 @@ class ConversationService:
app_model: App,
conversation_id: str,
user: Union[Account, EndUser] | None,
- name: str,
+ name: str | None,
auto_generate: bool,
):
conversation = cls.get_conversation(app_model, conversation_id, user)
diff --git a/api/tests/fixtures/workflow/end_node_without_value_type_field_workflow.yml b/api/tests/fixtures/workflow/end_node_without_value_type_field_workflow.yml
new file mode 100644
index 0000000000..a69339691d
--- /dev/null
+++ b/api/tests/fixtures/workflow/end_node_without_value_type_field_workflow.yml
@@ -0,0 +1,127 @@
+app:
+ description: 'End node without value_type field reproduction'
+ icon: 🤖
+ icon_background: '#FFEAD5'
+ mode: workflow
+ name: end_node_without_value_type_field_reproduction
+ use_icon_as_answer_icon: false
+dependencies: []
+kind: app
+version: 0.5.0
+workflow:
+ conversation_variables: []
+ environment_variables: []
+ features:
+ file_upload:
+ allowed_file_extensions:
+ - .JPG
+ - .JPEG
+ - .PNG
+ - .GIF
+ - .WEBP
+ - .SVG
+ allowed_file_types:
+ - image
+ allowed_file_upload_methods:
+ - local_file
+ - remote_url
+ enabled: false
+ fileUploadConfig:
+ audio_file_size_limit: 50
+ batch_count_limit: 5
+ file_size_limit: 15
+ image_file_batch_limit: 10
+ image_file_size_limit: 10
+ single_chunk_attachment_limit: 10
+ video_file_size_limit: 100
+ workflow_file_upload_limit: 10
+ image:
+ enabled: false
+ number_limits: 3
+ transfer_methods:
+ - local_file
+ - remote_url
+ number_limits: 3
+ opening_statement: ''
+ retriever_resource:
+ enabled: true
+ sensitive_word_avoidance:
+ enabled: false
+ speech_to_text:
+ enabled: false
+ suggested_questions: []
+ suggested_questions_after_answer:
+ enabled: false
+ text_to_speech:
+ enabled: false
+ language: ''
+ voice: ''
+ graph:
+ edges:
+ - data:
+ isInIteration: false
+ isInLoop: false
+ sourceType: start
+ targetType: end
+ id: 1765423445456-source-1765423454810-target
+ source: '1765423445456'
+ sourceHandle: source
+ target: '1765423454810'
+ targetHandle: target
+ type: custom
+ zIndex: 0
+ nodes:
+ - data:
+ selected: false
+ title: 用户输入
+ type: start
+ variables:
+ - default: ''
+ hint: ''
+ label: query
+ max_length: 48
+ options: []
+ placeholder: ''
+ required: true
+ type: text-input
+ variable: query
+ height: 109
+ id: '1765423445456'
+ position:
+ x: -48
+ y: 261
+ positionAbsolute:
+ x: -48
+ y: 261
+ selected: false
+ sourcePosition: right
+ targetPosition: left
+ type: custom
+ width: 242
+ - data:
+ outputs:
+ - value_selector:
+ - '1765423445456'
+ - query
+ variable: query
+ selected: true
+ title: 输出
+ type: end
+ height: 88
+ id: '1765423454810'
+ position:
+ x: 382
+ y: 282
+ positionAbsolute:
+ x: 382
+ y: 282
+ selected: true
+ sourcePosition: right
+ targetPosition: left
+ type: custom
+ width: 242
+ viewport:
+ x: 139
+ y: -135
+ zoom: 1
+ rag_pipeline_variables: []
diff --git a/api/tests/unit_tests/controllers/test_conversation_rename_payload.py b/api/tests/unit_tests/controllers/test_conversation_rename_payload.py
new file mode 100644
index 0000000000..494176cbd9
--- /dev/null
+++ b/api/tests/unit_tests/controllers/test_conversation_rename_payload.py
@@ -0,0 +1,20 @@
+import pytest
+from pydantic import ValidationError
+
+from controllers.console.explore.conversation import ConversationRenamePayload as ConsolePayload
+from controllers.service_api.app.conversation import ConversationRenamePayload as ServicePayload
+
+
+@pytest.mark.parametrize("payload_cls", [ConsolePayload, ServicePayload])
+def test_payload_allows_auto_generate_without_name(payload_cls):
+ payload = payload_cls.model_validate({"auto_generate": True})
+
+ assert payload.auto_generate is True
+ assert payload.name is None
+
+
+@pytest.mark.parametrize("payload_cls", [ConsolePayload, ServicePayload])
+@pytest.mark.parametrize("value", [None, "", " "])
+def test_payload_requires_name_when_not_auto_generate(payload_cls, value):
+ with pytest.raises(ValidationError):
+ payload_cls.model_validate({"name": value, "auto_generate": False})
diff --git a/api/tests/unit_tests/core/workflow/graph_engine/test_end_node_without_value_type.py b/api/tests/unit_tests/core/workflow/graph_engine/test_end_node_without_value_type.py
new file mode 100644
index 0000000000..b1380cd6d2
--- /dev/null
+++ b/api/tests/unit_tests/core/workflow/graph_engine/test_end_node_without_value_type.py
@@ -0,0 +1,60 @@
+"""
+Test case for end node without value_type field (backward compatibility).
+
+This test validates that end nodes work correctly even when the value_type
+field is missing from the output configuration, ensuring backward compatibility
+with older workflow definitions.
+"""
+
+from core.workflow.graph_events import (
+ GraphRunStartedEvent,
+ GraphRunSucceededEvent,
+ NodeRunStartedEvent,
+ NodeRunStreamChunkEvent,
+ NodeRunSucceededEvent,
+)
+
+from .test_table_runner import TableTestRunner, WorkflowTestCase
+
+
+def test_end_node_without_value_type_field():
+ """
+ Test that end node works without explicit value_type field.
+
+ The fixture implements a simple workflow that:
+ 1. Takes a query input from start node
+ 2. Passes it directly to end node
+ 3. End node outputs the value without specifying value_type
+ 4. Should correctly infer the type and output the value
+
+ This ensures backward compatibility with workflow definitions
+ created before value_type became a required field.
+ """
+ fixture_name = "end_node_without_value_type_field_workflow"
+
+ case = WorkflowTestCase(
+ fixture_path=fixture_name,
+ inputs={"query": "test query"},
+ expected_outputs={"query": "test query"},
+ expected_event_sequence=[
+ # Graph start
+ GraphRunStartedEvent,
+ # Start node
+ NodeRunStartedEvent,
+ NodeRunStreamChunkEvent, # Start node streams the input value
+ NodeRunSucceededEvent,
+ # End node
+ NodeRunStartedEvent,
+ NodeRunSucceededEvent,
+ # Graph end
+ GraphRunSucceededEvent,
+ ],
+ description="End node without value_type field should work correctly",
+ )
+
+ runner = TableTestRunner()
+ result = runner.run_test_case(case)
+ assert result.success, f"Test failed: {result.error}"
+ assert result.actual_outputs == {"query": "test query"}, (
+ f"Expected output to be {{'query': 'test query'}}, got {result.actual_outputs}"
+ )
diff --git a/api/tests/unit_tests/models/test_app_models.py b/api/tests/unit_tests/models/test_app_models.py
index 268ba1282a..e35788660d 100644
--- a/api/tests/unit_tests/models/test_app_models.py
+++ b/api/tests/unit_tests/models/test_app_models.py
@@ -1149,3 +1149,258 @@ class TestModelIntegration:
# Assert
assert site.app_id == app.id
assert app.enable_site is True
+
+
+class TestConversationStatusCount:
+ """Test suite for Conversation.status_count property N+1 query fix."""
+
+ def test_status_count_no_messages(self):
+ """Test status_count returns None when conversation has no messages."""
+ # Arrange
+ conversation = Conversation(
+ app_id=str(uuid4()),
+ mode=AppMode.CHAT,
+ name="Test Conversation",
+ status="normal",
+ from_source="api",
+ )
+ conversation.id = str(uuid4())
+
+ # Mock the database query to return no messages
+ with patch("models.model.db.session.scalars") as mock_scalars:
+ mock_scalars.return_value.all.return_value = []
+
+ # Act
+ result = conversation.status_count
+
+ # Assert
+ assert result is None
+
+ def test_status_count_messages_without_workflow_runs(self):
+ """Test status_count when messages have no workflow_run_id."""
+ # Arrange
+ app_id = str(uuid4())
+ conversation_id = str(uuid4())
+
+ conversation = Conversation(
+ app_id=app_id,
+ mode=AppMode.CHAT,
+ name="Test Conversation",
+ status="normal",
+ from_source="api",
+ )
+ conversation.id = conversation_id
+
+ # Mock the database query to return no messages with workflow_run_id
+ with patch("models.model.db.session.scalars") as mock_scalars:
+ mock_scalars.return_value.all.return_value = []
+
+ # Act
+ result = conversation.status_count
+
+ # Assert
+ assert result is None
+
+ def test_status_count_batch_loading_implementation(self):
+ """Test that status_count uses batch loading instead of N+1 queries."""
+ # Arrange
+ from core.workflow.enums import WorkflowExecutionStatus
+
+ app_id = str(uuid4())
+ conversation_id = str(uuid4())
+
+ # Create workflow run IDs
+ workflow_run_id_1 = str(uuid4())
+ workflow_run_id_2 = str(uuid4())
+ workflow_run_id_3 = str(uuid4())
+
+ conversation = Conversation(
+ app_id=app_id,
+ mode=AppMode.CHAT,
+ name="Test Conversation",
+ status="normal",
+ from_source="api",
+ )
+ conversation.id = conversation_id
+
+ # Mock messages with workflow_run_id
+ mock_messages = [
+ MagicMock(
+ conversation_id=conversation_id,
+ workflow_run_id=workflow_run_id_1,
+ ),
+ MagicMock(
+ conversation_id=conversation_id,
+ workflow_run_id=workflow_run_id_2,
+ ),
+ MagicMock(
+ conversation_id=conversation_id,
+ workflow_run_id=workflow_run_id_3,
+ ),
+ ]
+
+ # Mock workflow runs with different statuses
+ mock_workflow_runs = [
+ MagicMock(
+ id=workflow_run_id_1,
+ status=WorkflowExecutionStatus.SUCCEEDED.value,
+ app_id=app_id,
+ ),
+ MagicMock(
+ id=workflow_run_id_2,
+ status=WorkflowExecutionStatus.FAILED.value,
+ app_id=app_id,
+ ),
+ MagicMock(
+ id=workflow_run_id_3,
+ status=WorkflowExecutionStatus.PARTIAL_SUCCEEDED.value,
+ app_id=app_id,
+ ),
+ ]
+
+ # Track database calls
+ calls_made = []
+
+ def mock_scalars(query):
+ calls_made.append(str(query))
+ mock_result = MagicMock()
+
+ # Return messages for the first query (messages with workflow_run_id)
+ if "messages" in str(query) and "conversation_id" in str(query):
+ mock_result.all.return_value = mock_messages
+ # Return workflow runs for the batch query
+ elif "workflow_runs" in str(query):
+ mock_result.all.return_value = mock_workflow_runs
+ else:
+ mock_result.all.return_value = []
+
+ return mock_result
+
+ # Act & Assert
+ with patch("models.model.db.session.scalars", side_effect=mock_scalars):
+ result = conversation.status_count
+
+ # Verify only 2 database queries were made (not N+1)
+ assert len(calls_made) == 2, f"Expected 2 queries, got {len(calls_made)}: {calls_made}"
+
+ # Verify the first query gets messages
+ assert "messages" in calls_made[0]
+ assert "conversation_id" in calls_made[0]
+
+ # Verify the second query batch loads workflow runs with proper filtering
+ assert "workflow_runs" in calls_made[1]
+ assert "app_id" in calls_made[1] # Security filter applied
+ assert "IN" in calls_made[1] # Batch loading with IN clause
+
+ # Verify correct status counts
+ assert result["success"] == 1 # One SUCCEEDED
+ assert result["failed"] == 1 # One FAILED
+ assert result["partial_success"] == 1 # One PARTIAL_SUCCEEDED
+
+ def test_status_count_app_id_filtering(self):
+ """Test that status_count filters workflow runs by app_id for security."""
+ # Arrange
+ app_id = str(uuid4())
+ other_app_id = str(uuid4())
+ conversation_id = str(uuid4())
+ workflow_run_id = str(uuid4())
+
+ conversation = Conversation(
+ app_id=app_id,
+ mode=AppMode.CHAT,
+ name="Test Conversation",
+ status="normal",
+ from_source="api",
+ )
+ conversation.id = conversation_id
+
+ # Mock message with workflow_run_id
+ mock_messages = [
+ MagicMock(
+ conversation_id=conversation_id,
+ workflow_run_id=workflow_run_id,
+ ),
+ ]
+
+ calls_made = []
+
+ def mock_scalars(query):
+ calls_made.append(str(query))
+ mock_result = MagicMock()
+
+ if "messages" in str(query):
+ mock_result.all.return_value = mock_messages
+ elif "workflow_runs" in str(query):
+ # Return empty list because no workflow run matches the correct app_id
+ mock_result.all.return_value = [] # Workflow run filtered out by app_id
+ else:
+ mock_result.all.return_value = []
+
+ return mock_result
+
+ # Act
+ with patch("models.model.db.session.scalars", side_effect=mock_scalars):
+ result = conversation.status_count
+
+ # Assert - query should include app_id filter
+ workflow_query = calls_made[1]
+ assert "app_id" in workflow_query
+
+ # Since workflow run has wrong app_id, it shouldn't be included in counts
+ assert result["success"] == 0
+ assert result["failed"] == 0
+ assert result["partial_success"] == 0
+
+ def test_status_count_handles_invalid_workflow_status(self):
+ """Test that status_count gracefully handles invalid workflow status values."""
+ # Arrange
+ app_id = str(uuid4())
+ conversation_id = str(uuid4())
+ workflow_run_id = str(uuid4())
+
+ conversation = Conversation(
+ app_id=app_id,
+ mode=AppMode.CHAT,
+ name="Test Conversation",
+ status="normal",
+ from_source="api",
+ )
+ conversation.id = conversation_id
+
+ mock_messages = [
+ MagicMock(
+ conversation_id=conversation_id,
+ workflow_run_id=workflow_run_id,
+ ),
+ ]
+
+ # Mock workflow run with invalid status
+ mock_workflow_runs = [
+ MagicMock(
+ id=workflow_run_id,
+ status="invalid_status", # Invalid status that should raise ValueError
+ app_id=app_id,
+ ),
+ ]
+
+ with patch("models.model.db.session.scalars") as mock_scalars:
+ # Mock the messages query
+ def mock_scalars_side_effect(query):
+ mock_result = MagicMock()
+ if "messages" in str(query):
+ mock_result.all.return_value = mock_messages
+ elif "workflow_runs" in str(query):
+ mock_result.all.return_value = mock_workflow_runs
+ else:
+ mock_result.all.return_value = []
+ return mock_result
+
+ mock_scalars.side_effect = mock_scalars_side_effect
+
+ # Act - should not raise exception
+ result = conversation.status_count
+
+ # Assert - should handle invalid status gracefully
+ assert result["success"] == 0
+ assert result["failed"] == 0
+ assert result["partial_success"] == 0
diff --git a/docker/.env.example b/docker/.env.example
index 85e8b1dc7f..04088b72a8 100644
--- a/docker/.env.example
+++ b/docker/.env.example
@@ -1432,3 +1432,6 @@ WORKFLOW_SCHEDULE_MAX_DISPATCH_PER_TICK=0
# Tenant isolated task queue configuration
TENANT_ISOLATED_TASK_CONCURRENCY=1
+
+# The API key of amplitude
+AMPLITUDE_API_KEY=
diff --git a/docker/docker-compose-template.yaml b/docker/docker-compose-template.yaml
index b3d5cca245..c89224fa8a 100644
--- a/docker/docker-compose-template.yaml
+++ b/docker/docker-compose-template.yaml
@@ -136,6 +136,7 @@ services:
environment:
CONSOLE_API_URL: ${CONSOLE_API_URL:-}
APP_API_URL: ${APP_API_URL:-}
+ AMPLITUDE_API_KEY: ${AMPLITUDE_API_KEY:-}
NEXT_PUBLIC_COOKIE_DOMAIN: ${NEXT_PUBLIC_COOKIE_DOMAIN:-}
SENTRY_DSN: ${WEB_SENTRY_DSN:-}
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml
index b961f6b216..68f5726797 100644
--- a/docker/docker-compose.yaml
+++ b/docker/docker-compose.yaml
@@ -635,6 +635,7 @@ x-shared-env: &shared-api-worker-env
WORKFLOW_SCHEDULE_POLLER_BATCH_SIZE: ${WORKFLOW_SCHEDULE_POLLER_BATCH_SIZE:-100}
WORKFLOW_SCHEDULE_MAX_DISPATCH_PER_TICK: ${WORKFLOW_SCHEDULE_MAX_DISPATCH_PER_TICK:-0}
TENANT_ISOLATED_TASK_CONCURRENCY: ${TENANT_ISOLATED_TASK_CONCURRENCY:-1}
+ AMPLITUDE_API_KEY: ${AMPLITUDE_API_KEY:-}
services:
# Init container to fix permissions
@@ -773,6 +774,7 @@ services:
environment:
CONSOLE_API_URL: ${CONSOLE_API_URL:-}
APP_API_URL: ${APP_API_URL:-}
+ AMPLITUDE_API_KEY: ${AMPLITUDE_API_KEY:-}
NEXT_PUBLIC_COOKIE_DOMAIN: ${NEXT_PUBLIC_COOKIE_DOMAIN:-}
SENTRY_DSN: ${WEB_SENTRY_DSN:-}
NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0}
diff --git a/web/.env.example b/web/.env.example
index eff6f77fd9..b488c31057 100644
--- a/web/.env.example
+++ b/web/.env.example
@@ -70,3 +70,6 @@ NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX=false
# The maximum number of tree node depth for workflow
NEXT_PUBLIC_MAX_TREE_DEPTH=50
+
+# The API key of amplitude
+NEXT_PUBLIC_AMPLITUDE_API_KEY=
diff --git a/web/app/components/app/configuration/config-prompt/confirm-add-var/index.spec.tsx b/web/app/components/app/configuration/config-prompt/confirm-add-var/index.spec.tsx
new file mode 100644
index 0000000000..7ffafbb172
--- /dev/null
+++ b/web/app/components/app/configuration/config-prompt/confirm-add-var/index.spec.tsx
@@ -0,0 +1,49 @@
+import React from 'react'
+import { fireEvent, render, screen } from '@testing-library/react'
+import ConfirmAddVar from './index'
+
+jest.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (key: string) => key,
+ }),
+}))
+
+jest.mock('../../base/var-highlight', () => ({
+ __esModule: true,
+ default: ({ name }: { name: string }) => {name},
+}))
+
+describe('ConfirmAddVar', () => {
+ beforeEach(() => {
+ jest.clearAllMocks()
+ })
+
+ it('should render variable names', () => {
+ render()
+
+ const highlights = screen.getAllByTestId('var-highlight')
+ expect(highlights).toHaveLength(2)
+ expect(highlights[0]).toHaveTextContent('foo')
+ expect(highlights[1]).toHaveTextContent('bar')
+ })
+
+ it('should trigger cancel actions', () => {
+ const onConfirm = jest.fn()
+ const onCancel = jest.fn()
+ render()
+
+ fireEvent.click(screen.getByText('common.operation.cancel'))
+
+ expect(onCancel).toHaveBeenCalledTimes(1)
+ })
+
+ it('should trigger confirm actions', () => {
+ const onConfirm = jest.fn()
+ const onCancel = jest.fn()
+ render()
+
+ fireEvent.click(screen.getByText('common.operation.add'))
+
+ expect(onConfirm).toHaveBeenCalledTimes(1)
+ })
+})
diff --git a/web/app/components/app/configuration/config-prompt/conversation-history/edit-modal.spec.tsx b/web/app/components/app/configuration/config-prompt/conversation-history/edit-modal.spec.tsx
new file mode 100644
index 0000000000..652f5409e8
--- /dev/null
+++ b/web/app/components/app/configuration/config-prompt/conversation-history/edit-modal.spec.tsx
@@ -0,0 +1,56 @@
+import React from 'react'
+import { fireEvent, render, screen } from '@testing-library/react'
+import EditModal from './edit-modal'
+import type { ConversationHistoriesRole } from '@/models/debug'
+
+jest.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (key: string) => key,
+ }),
+}))
+
+jest.mock('@/app/components/base/modal', () => ({
+ __esModule: true,
+ default: ({ children }: { children: React.ReactNode }) =>
{children}
,
+}))
+
+describe('Conversation history edit modal', () => {
+ const data: ConversationHistoriesRole = {
+ user_prefix: 'user',
+ assistant_prefix: 'assistant',
+ }
+
+ beforeEach(() => {
+ jest.clearAllMocks()
+ })
+
+ it('should render provided prefixes', () => {
+ render()
+
+ expect(screen.getByDisplayValue('user')).toBeInTheDocument()
+ expect(screen.getByDisplayValue('assistant')).toBeInTheDocument()
+ })
+
+ it('should update prefixes and save changes', () => {
+ const onSave = jest.fn()
+ render()
+
+ fireEvent.change(screen.getByDisplayValue('user'), { target: { value: 'member' } })
+ fireEvent.change(screen.getByDisplayValue('assistant'), { target: { value: 'helper' } })
+ fireEvent.click(screen.getByText('common.operation.save'))
+
+ expect(onSave).toHaveBeenCalledWith({
+ user_prefix: 'member',
+ assistant_prefix: 'helper',
+ })
+ })
+
+ it('should call close handler', () => {
+ const onClose = jest.fn()
+ render()
+
+ fireEvent.click(screen.getByText('common.operation.cancel'))
+
+ expect(onClose).toHaveBeenCalledTimes(1)
+ })
+})
diff --git a/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.spec.tsx b/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.spec.tsx
new file mode 100644
index 0000000000..61e361c057
--- /dev/null
+++ b/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.spec.tsx
@@ -0,0 +1,48 @@
+import React from 'react'
+import { render, screen } from '@testing-library/react'
+import HistoryPanel from './history-panel'
+
+jest.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (key: string) => key,
+ }),
+}))
+
+const mockDocLink = jest.fn(() => 'doc-link')
+jest.mock('@/context/i18n', () => ({
+ useDocLink: () => mockDocLink,
+}))
+
+jest.mock('@/app/components/app/configuration/base/operation-btn', () => ({
+ __esModule: true,
+ default: ({ onClick }: { onClick: () => void }) => (
+
+ ),
+}))
+
+jest.mock('@/app/components/app/configuration/base/feature-panel', () => ({
+ __esModule: true,
+ default: ({ children }: { children: React.ReactNode }) => {children}
,
+}))
+
+describe('HistoryPanel', () => {
+ beforeEach(() => {
+ jest.clearAllMocks()
+ })
+
+ it('should render warning content and link when showWarning is true', () => {
+ render()
+
+ expect(screen.getByText('appDebug.feature.conversationHistory.tip')).toBeInTheDocument()
+ const link = screen.getByText('appDebug.feature.conversationHistory.learnMore')
+ expect(link).toHaveAttribute('href', 'doc-link')
+ })
+
+ it('should hide warning when showWarning is false', () => {
+ render()
+
+ expect(screen.queryByText('appDebug.feature.conversationHistory.tip')).toBeNull()
+ })
+})
diff --git a/web/app/components/app/configuration/config-prompt/index.spec.tsx b/web/app/components/app/configuration/config-prompt/index.spec.tsx
new file mode 100644
index 0000000000..b2098862da
--- /dev/null
+++ b/web/app/components/app/configuration/config-prompt/index.spec.tsx
@@ -0,0 +1,351 @@
+import React from 'react'
+import { fireEvent, render, screen } from '@testing-library/react'
+import Prompt, { type IPromptProps } from './index'
+import ConfigContext from '@/context/debug-configuration'
+import { MAX_PROMPT_MESSAGE_LENGTH } from '@/config'
+import { type PromptItem, PromptRole, type PromptVariable } from '@/models/debug'
+import { AppModeEnum, ModelModeType } from '@/types/app'
+
+jest.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (key: string) => key,
+ }),
+}))
+
+type DebugConfiguration = {
+ isAdvancedMode: boolean
+ currentAdvancedPrompt: PromptItem | PromptItem[]
+ setCurrentAdvancedPrompt: (prompt: PromptItem | PromptItem[], isUserChanged?: boolean) => void
+ modelModeType: ModelModeType
+ dataSets: Array<{
+ id: string
+ name?: string
+ }>
+ hasSetBlockStatus: {
+ context: boolean
+ history: boolean
+ query: boolean
+ }
+}
+
+const defaultPromptVariables: PromptVariable[] = [
+ { key: 'var', name: 'Variable', type: 'string', required: true },
+]
+
+let mockSimplePromptInputProps: IPromptProps | null = null
+
+jest.mock('./simple-prompt-input', () => ({
+ __esModule: true,
+ default: (props: IPromptProps) => {
+ mockSimplePromptInputProps = props
+ return (
+ props.onChange?.('mocked prompt', props.promptVariables)}
+ >
+ SimplePromptInput Mock
+
+ )
+ },
+}))
+
+type AdvancedMessageInputProps = {
+ isChatMode: boolean
+ type: PromptRole
+ value: string
+ onTypeChange: (value: PromptRole) => void
+ canDelete: boolean
+ onDelete: () => void
+ onChange: (value: string) => void
+ promptVariables: PromptVariable[]
+ isContextMissing: boolean
+ onHideContextMissingTip: () => void
+ noResize?: boolean
+}
+
+jest.mock('./advanced-prompt-input', () => ({
+ __esModule: true,
+ default: (props: AdvancedMessageInputProps) => {
+ return (
+
+
+
+
+
+
+ )
+ },
+}))
+const getContextValue = (overrides: Partial = {}): DebugConfiguration => {
+ return {
+ setCurrentAdvancedPrompt: jest.fn(),
+ isAdvancedMode: false,
+ currentAdvancedPrompt: [],
+ modelModeType: ModelModeType.chat,
+ dataSets: [],
+ hasSetBlockStatus: {
+ context: false,
+ history: false,
+ query: false,
+ },
+ ...overrides,
+ }
+}
+
+const renderComponent = (
+ props: Partial = {},
+ contextOverrides: Partial = {},
+) => {
+ const mergedProps: IPromptProps = {
+ mode: AppModeEnum.CHAT,
+ promptTemplate: 'initial template',
+ promptVariables: defaultPromptVariables,
+ onChange: jest.fn(),
+ ...props,
+ }
+ const contextValue = getContextValue(contextOverrides)
+
+ return {
+ contextValue,
+ ...render(
+
+
+ ,
+ ),
+ }
+}
+
+describe('Prompt config component', () => {
+ beforeEach(() => {
+ jest.clearAllMocks()
+ mockSimplePromptInputProps = null
+ })
+
+ // Rendering simple mode
+ it('should render simple prompt when advanced mode is disabled', () => {
+ const onChange = jest.fn()
+ renderComponent({ onChange }, { isAdvancedMode: false })
+
+ const simplePrompt = screen.getByTestId('simple-prompt-input')
+ expect(simplePrompt).toBeInTheDocument()
+ expect(simplePrompt).toHaveAttribute('data-mode', AppModeEnum.CHAT)
+ expect(mockSimplePromptInputProps?.promptTemplate).toBe('initial template')
+ fireEvent.click(simplePrompt)
+ expect(onChange).toHaveBeenCalledWith('mocked prompt', defaultPromptVariables)
+ expect(screen.queryByTestId('advanced-message-input')).toBeNull()
+ })
+
+ // Rendering advanced chat messages
+ it('should render advanced chat prompts and show context missing tip when dataset context is not set', () => {
+ const currentAdvancedPrompt: PromptItem[] = [
+ { role: PromptRole.user, text: 'first' },
+ { role: PromptRole.assistant, text: 'second' },
+ ]
+ renderComponent(
+ {},
+ {
+ isAdvancedMode: true,
+ currentAdvancedPrompt,
+ modelModeType: ModelModeType.chat,
+ dataSets: [{ id: 'ds' } as unknown as DebugConfiguration['dataSets'][number]],
+ hasSetBlockStatus: { context: false, history: true, query: true },
+ },
+ )
+
+ const renderedMessages = screen.getAllByTestId('advanced-message-input')
+ expect(renderedMessages).toHaveLength(2)
+ expect(renderedMessages[0]).toHaveAttribute('data-context-missing', 'true')
+ fireEvent.click(screen.getAllByText('hide-context')[0])
+ expect(screen.getAllByTestId('advanced-message-input')[0]).toHaveAttribute('data-context-missing', 'false')
+ })
+
+ // Chat message mutations
+ it('should update chat prompt value and call setter with user change flag', () => {
+ const currentAdvancedPrompt: PromptItem[] = [
+ { role: PromptRole.user, text: 'first' },
+ { role: PromptRole.assistant, text: 'second' },
+ ]
+ const setCurrentAdvancedPrompt = jest.fn()
+ renderComponent(
+ {},
+ {
+ isAdvancedMode: true,
+ currentAdvancedPrompt,
+ modelModeType: ModelModeType.chat,
+ setCurrentAdvancedPrompt,
+ },
+ )
+
+ fireEvent.click(screen.getAllByText('change')[0])
+ expect(setCurrentAdvancedPrompt).toHaveBeenCalledWith(
+ [
+ { role: PromptRole.user, text: 'updated text' },
+ { role: PromptRole.assistant, text: 'second' },
+ ],
+ true,
+ )
+ })
+
+ it('should update chat prompt role when type changes', () => {
+ const currentAdvancedPrompt: PromptItem[] = [
+ { role: PromptRole.user, text: 'first' },
+ { role: PromptRole.user, text: 'second' },
+ ]
+ const setCurrentAdvancedPrompt = jest.fn()
+ renderComponent(
+ {},
+ {
+ isAdvancedMode: true,
+ currentAdvancedPrompt,
+ modelModeType: ModelModeType.chat,
+ setCurrentAdvancedPrompt,
+ },
+ )
+
+ fireEvent.click(screen.getAllByText('type')[1])
+ expect(setCurrentAdvancedPrompt).toHaveBeenCalledWith(
+ [
+ { role: PromptRole.user, text: 'first' },
+ { role: PromptRole.assistant, text: 'second' },
+ ],
+ )
+ })
+
+ it('should delete chat prompt item', () => {
+ const currentAdvancedPrompt: PromptItem[] = [
+ { role: PromptRole.user, text: 'first' },
+ { role: PromptRole.assistant, text: 'second' },
+ ]
+ const setCurrentAdvancedPrompt = jest.fn()
+ renderComponent(
+ {},
+ {
+ isAdvancedMode: true,
+ currentAdvancedPrompt,
+ modelModeType: ModelModeType.chat,
+ setCurrentAdvancedPrompt,
+ },
+ )
+
+ fireEvent.click(screen.getAllByText('delete')[0])
+ expect(setCurrentAdvancedPrompt).toHaveBeenCalledWith([{ role: PromptRole.assistant, text: 'second' }])
+ })
+
+ // Add message behavior
+ it('should append a mirrored role message when clicking add in chat mode', () => {
+ const currentAdvancedPrompt: PromptItem[] = [
+ { role: PromptRole.user, text: 'first' },
+ ]
+ const setCurrentAdvancedPrompt = jest.fn()
+ renderComponent(
+ {},
+ {
+ isAdvancedMode: true,
+ currentAdvancedPrompt,
+ modelModeType: ModelModeType.chat,
+ setCurrentAdvancedPrompt,
+ },
+ )
+
+ fireEvent.click(screen.getByText('appDebug.promptMode.operation.addMessage'))
+ expect(setCurrentAdvancedPrompt).toHaveBeenCalledWith([
+ { role: PromptRole.user, text: 'first' },
+ { role: PromptRole.assistant, text: '' },
+ ])
+ })
+
+ it('should append a user role when the last chat prompt is from assistant', () => {
+ const currentAdvancedPrompt: PromptItem[] = [
+ { role: PromptRole.assistant, text: 'reply' },
+ ]
+ const setCurrentAdvancedPrompt = jest.fn()
+ renderComponent(
+ {},
+ {
+ isAdvancedMode: true,
+ currentAdvancedPrompt,
+ modelModeType: ModelModeType.chat,
+ setCurrentAdvancedPrompt,
+ },
+ )
+
+ fireEvent.click(screen.getByText('appDebug.promptMode.operation.addMessage'))
+ expect(setCurrentAdvancedPrompt).toHaveBeenCalledWith([
+ { role: PromptRole.assistant, text: 'reply' },
+ { role: PromptRole.user, text: '' },
+ ])
+ })
+
+ it('should insert a system message when adding to an empty chat prompt list', () => {
+ const setCurrentAdvancedPrompt = jest.fn()
+ renderComponent(
+ {},
+ {
+ isAdvancedMode: true,
+ currentAdvancedPrompt: [],
+ modelModeType: ModelModeType.chat,
+ setCurrentAdvancedPrompt,
+ },
+ )
+
+ fireEvent.click(screen.getByText('appDebug.promptMode.operation.addMessage'))
+ expect(setCurrentAdvancedPrompt).toHaveBeenCalledWith([{ role: PromptRole.system, text: '' }])
+ })
+
+ it('should not show add button when reaching max prompt length', () => {
+ const prompts: PromptItem[] = Array.from({ length: MAX_PROMPT_MESSAGE_LENGTH }, (_, index) => ({
+ role: PromptRole.user,
+ text: `item-${index}`,
+ }))
+ renderComponent(
+ {},
+ {
+ isAdvancedMode: true,
+ currentAdvancedPrompt: prompts,
+ modelModeType: ModelModeType.chat,
+ },
+ )
+
+ expect(screen.queryByText('appDebug.promptMode.operation.addMessage')).toBeNull()
+ })
+
+ // Completion mode
+ it('should update completion prompt value and flag as user change', () => {
+ const setCurrentAdvancedPrompt = jest.fn()
+ renderComponent(
+ {},
+ {
+ isAdvancedMode: true,
+ currentAdvancedPrompt: { role: PromptRole.user, text: 'single' },
+ modelModeType: ModelModeType.completion,
+ setCurrentAdvancedPrompt,
+ },
+ )
+
+ fireEvent.click(screen.getByText('change'))
+
+ expect(setCurrentAdvancedPrompt).toHaveBeenCalledWith({ role: PromptRole.user, text: 'updated text' }, true)
+ })
+})
diff --git a/web/app/components/app/configuration/config-prompt/message-type-selector.spec.tsx b/web/app/components/app/configuration/config-prompt/message-type-selector.spec.tsx
new file mode 100644
index 0000000000..4401b7e57e
--- /dev/null
+++ b/web/app/components/app/configuration/config-prompt/message-type-selector.spec.tsx
@@ -0,0 +1,37 @@
+import React from 'react'
+import { fireEvent, render, screen } from '@testing-library/react'
+import MessageTypeSelector from './message-type-selector'
+import { PromptRole } from '@/models/debug'
+
+describe('MessageTypeSelector', () => {
+ beforeEach(() => {
+ jest.clearAllMocks()
+ })
+
+ it('should render current value and keep options hidden by default', () => {
+ render()
+
+ expect(screen.getByText(PromptRole.user)).toBeInTheDocument()
+ expect(screen.queryByText(PromptRole.system)).toBeNull()
+ })
+
+ it('should toggle option list when clicking the selector', () => {
+ render()
+
+ fireEvent.click(screen.getByText(PromptRole.system))
+
+ expect(screen.getByText(PromptRole.user)).toBeInTheDocument()
+ expect(screen.getByText(PromptRole.assistant)).toBeInTheDocument()
+ })
+
+ it('should call onChange with selected type and close the list', () => {
+ const onChange = jest.fn()
+ render()
+
+ fireEvent.click(screen.getByText(PromptRole.assistant))
+ fireEvent.click(screen.getByText(PromptRole.user))
+
+ expect(onChange).toHaveBeenCalledWith(PromptRole.user)
+ expect(screen.queryByText(PromptRole.system)).toBeNull()
+ })
+})
diff --git a/web/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.spec.tsx b/web/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.spec.tsx
new file mode 100644
index 0000000000..d6bef4cdd7
--- /dev/null
+++ b/web/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.spec.tsx
@@ -0,0 +1,66 @@
+import React from 'react'
+import { fireEvent, render, screen } from '@testing-library/react'
+import PromptEditorHeightResizeWrap from './prompt-editor-height-resize-wrap'
+
+describe('PromptEditorHeightResizeWrap', () => {
+ beforeEach(() => {
+ jest.clearAllMocks()
+ jest.useFakeTimers()
+ })
+
+ afterEach(() => {
+ jest.runOnlyPendingTimers()
+ jest.useRealTimers()
+ })
+
+ it('should render children, footer, and hide resize handler when requested', () => {
+ const { container } = render(
+ footer}
+ hideResize
+ >
+ content
+ ,
+ )
+
+ expect(screen.getByText('content')).toBeInTheDocument()
+ expect(screen.getByText('footer')).toBeInTheDocument()
+ expect(container.querySelector('.cursor-row-resize')).toBeNull()
+ })
+
+ it('should resize height with mouse events and clamp to minHeight', () => {
+ const onHeightChange = jest.fn()
+
+ const { container } = render(
+
+ content
+ ,
+ )
+
+ const handle = container.querySelector('.cursor-row-resize')
+ expect(handle).not.toBeNull()
+
+ fireEvent.mouseDown(handle as Element, { clientY: 100 })
+ expect(document.body.style.userSelect).toBe('none')
+
+ fireEvent.mouseMove(document, { clientY: 130 })
+ jest.runAllTimers()
+ expect(onHeightChange).toHaveBeenLastCalledWith(180)
+
+ onHeightChange.mockClear()
+ fireEvent.mouseMove(document, { clientY: -100 })
+ jest.runAllTimers()
+ expect(onHeightChange).toHaveBeenLastCalledWith(100)
+
+ fireEvent.mouseUp(document)
+ expect(document.body.style.userSelect).toBe('')
+ })
+})
diff --git a/web/app/components/app/configuration/debug/debug-with-multiple-model/index.spec.tsx b/web/app/components/app/configuration/debug/debug-with-multiple-model/index.spec.tsx
new file mode 100644
index 0000000000..7607a21b07
--- /dev/null
+++ b/web/app/components/app/configuration/debug/debug-with-multiple-model/index.spec.tsx
@@ -0,0 +1,480 @@
+import '@testing-library/jest-dom'
+import type { CSSProperties } from 'react'
+import { fireEvent, render, screen } from '@testing-library/react'
+import DebugWithMultipleModel from './index'
+import type { DebugWithMultipleModelContextType } from './context'
+import { APP_CHAT_WITH_MULTIPLE_MODEL } from '../types'
+import type { ModelAndParameter } from '../types'
+import type { Inputs, ModelConfig } from '@/models/debug'
+import { DEFAULT_AGENT_SETTING, DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
+import type { FeatureStoreState } from '@/app/components/base/features/store'
+import type { FileEntity } from '@/app/components/base/file-uploader/types'
+import type { InputForm } from '@/app/components/base/chat/chat/type'
+import { AppModeEnum, ModelModeType, type PromptVariable, Resolution, TransferMethod } from '@/types/app'
+
+type PromptVariableWithMeta = Omit & {
+ type: PromptVariable['type'] | 'api'
+ required?: boolean
+ hide?: boolean
+}
+
+const mockUseDebugConfigurationContext = jest.fn()
+const mockUseFeaturesSelector = jest.fn()
+const mockUseEventEmitterContext = jest.fn()
+const mockUseAppStoreSelector = jest.fn()
+const mockEventEmitter = { emit: jest.fn() }
+const mockSetShowAppConfigureFeaturesModal = jest.fn()
+let capturedChatInputProps: MockChatInputAreaProps | null = null
+let modelIdCounter = 0
+let featureState: FeatureStoreState
+
+type MockChatInputAreaProps = {
+ onSend?: (message: string, files?: FileEntity[]) => void
+ onFeatureBarClick?: (state: boolean) => void
+ showFeatureBar?: boolean
+ showFileUpload?: boolean
+ inputs?: Record
+ inputsForm?: InputForm[]
+ speechToTextConfig?: unknown
+ visionConfig?: unknown
+}
+
+const mockFiles: FileEntity[] = [
+ {
+ id: 'file-1',
+ name: 'file.txt',
+ size: 10,
+ type: 'text/plain',
+ progress: 100,
+ transferMethod: TransferMethod.remote_url,
+ supportFileType: 'text',
+ },
+]
+
+jest.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (key: string) => key,
+ }),
+}))
+
+jest.mock('@/context/debug-configuration', () => ({
+ __esModule: true,
+ useDebugConfigurationContext: () => mockUseDebugConfigurationContext(),
+}))
+
+jest.mock('@/app/components/base/features/hooks', () => ({
+ __esModule: true,
+ useFeatures: (selector: (state: FeatureStoreState) => unknown) => mockUseFeaturesSelector(selector),
+}))
+
+jest.mock('@/context/event-emitter', () => ({
+ __esModule: true,
+ useEventEmitterContextContext: () => mockUseEventEmitterContext(),
+}))
+
+jest.mock('@/app/components/app/store', () => ({
+ __esModule: true,
+ useStore: (selector: (state: { setShowAppConfigureFeaturesModal: typeof mockSetShowAppConfigureFeaturesModal }) => unknown) => mockUseAppStoreSelector(selector),
+}))
+
+jest.mock('./debug-item', () => ({
+ __esModule: true,
+ default: ({
+ modelAndParameter,
+ className,
+ style,
+ }: {
+ modelAndParameter: ModelAndParameter
+ className?: string
+ style?: CSSProperties
+ }) => (
+
+ DebugItem-{modelAndParameter.id}
+
+ ),
+}))
+
+jest.mock('@/app/components/base/chat/chat/chat-input-area', () => ({
+ __esModule: true,
+ default: (props: MockChatInputAreaProps) => {
+ capturedChatInputProps = props
+ return (
+
+
+
+
+ )
+ },
+}))
+
+const createFeatureState = (): FeatureStoreState => ({
+ features: {
+ speech2text: { enabled: true },
+ file: {
+ image: {
+ enabled: true,
+ detail: Resolution.high,
+ number_limits: 2,
+ transfer_methods: [TransferMethod.remote_url],
+ },
+ },
+ },
+ setFeatures: jest.fn(),
+ showFeaturesModal: false,
+ setShowFeaturesModal: jest.fn(),
+})
+
+const createModelConfig = (promptVariables: PromptVariableWithMeta[] = []): ModelConfig => ({
+ provider: 'OPENAI',
+ model_id: 'gpt-4',
+ mode: ModelModeType.chat,
+ configs: {
+ prompt_template: '',
+ prompt_variables: promptVariables as unknown as PromptVariable[],
+ },
+ chat_prompt_config: DEFAULT_CHAT_PROMPT_CONFIG,
+ completion_prompt_config: DEFAULT_COMPLETION_PROMPT_CONFIG,
+ opening_statement: '',
+ more_like_this: null,
+ suggested_questions: [],
+ suggested_questions_after_answer: null,
+ speech_to_text: null,
+ text_to_speech: null,
+ file_upload: null,
+ retriever_resource: null,
+ sensitive_word_avoidance: null,
+ annotation_reply: null,
+ external_data_tools: [],
+ system_parameters: {
+ audio_file_size_limit: 0,
+ file_size_limit: 0,
+ image_file_size_limit: 0,
+ video_file_size_limit: 0,
+ workflow_file_upload_limit: 0,
+ },
+ dataSets: [],
+ agentConfig: DEFAULT_AGENT_SETTING,
+})
+
+type DebugConfiguration = {
+ mode: AppModeEnum
+ inputs: Inputs
+ modelConfig: ModelConfig
+}
+
+const createDebugConfiguration = (overrides: Partial = {}): DebugConfiguration => ({
+ mode: AppModeEnum.CHAT,
+ inputs: {},
+ modelConfig: createModelConfig(),
+ ...overrides,
+})
+
+const createModelAndParameter = (overrides: Partial = {}): ModelAndParameter => ({
+ id: `model-${++modelIdCounter}`,
+ model: 'gpt-3.5-turbo',
+ provider: 'openai',
+ parameters: {},
+ ...overrides,
+})
+
+const createProps = (overrides: Partial = {}): DebugWithMultipleModelContextType => ({
+ multipleModelConfigs: [createModelAndParameter()],
+ onMultipleModelConfigsChange: jest.fn(),
+ onDebugWithMultipleModelChange: jest.fn(),
+ ...overrides,
+})
+
+const renderComponent = (props?: Partial) => {
+ const mergedProps = createProps(props)
+ return render()
+}
+
+describe('DebugWithMultipleModel', () => {
+ beforeEach(() => {
+ jest.clearAllMocks()
+ capturedChatInputProps = null
+ modelIdCounter = 0
+ featureState = createFeatureState()
+ mockUseFeaturesSelector.mockImplementation(selector => selector(featureState))
+ mockUseEventEmitterContext.mockReturnValue({ eventEmitter: mockEventEmitter })
+ mockUseAppStoreSelector.mockImplementation(selector => selector({ setShowAppConfigureFeaturesModal: mockSetShowAppConfigureFeaturesModal }))
+ mockUseDebugConfigurationContext.mockReturnValue(createDebugConfiguration())
+ })
+
+ describe('chat input rendering', () => {
+ it('should render chat input in chat mode with transformed prompt variables and feature handler', () => {
+ // Arrange
+ const promptVariables: PromptVariableWithMeta[] = [
+ { key: 'city', name: 'City', type: 'string', required: true },
+ { key: 'audience', name: 'Audience', type: 'number' },
+ { key: 'hidden', name: 'Hidden', type: 'select', hide: true },
+ { key: 'api-only', name: 'API Only', type: 'api' },
+ ]
+ const debugConfiguration = createDebugConfiguration({
+ inputs: { audience: 'engineers' },
+ modelConfig: createModelConfig(promptVariables),
+ })
+ mockUseDebugConfigurationContext.mockReturnValue(debugConfiguration)
+
+ // Act
+ renderComponent()
+ fireEvent.click(screen.getByRole('button', { name: /feature/i }))
+
+ // Assert
+ expect(screen.getByTestId('chat-input-area')).toBeInTheDocument()
+ expect(capturedChatInputProps?.inputs).toEqual({ audience: 'engineers' })
+ expect(capturedChatInputProps?.inputsForm).toEqual([
+ expect.objectContaining({ label: 'City', variable: 'city', hide: false, required: true }),
+ expect.objectContaining({ label: 'Audience', variable: 'audience', hide: false, required: false }),
+ expect.objectContaining({ label: 'Hidden', variable: 'hidden', hide: true, required: false }),
+ ])
+ expect(capturedChatInputProps?.showFeatureBar).toBe(true)
+ expect(capturedChatInputProps?.showFileUpload).toBe(false)
+ expect(capturedChatInputProps?.speechToTextConfig).toEqual(featureState.features.speech2text)
+ expect(capturedChatInputProps?.visionConfig).toEqual(featureState.features.file)
+ expect(mockSetShowAppConfigureFeaturesModal).toHaveBeenCalledWith(true)
+ })
+
+ it('should render chat input in agent chat mode', () => {
+ // Arrange
+ mockUseDebugConfigurationContext.mockReturnValue(createDebugConfiguration({
+ mode: AppModeEnum.AGENT_CHAT,
+ }))
+
+ // Act
+ renderComponent()
+
+ // Assert
+ expect(screen.getByTestId('chat-input-area')).toBeInTheDocument()
+ })
+
+ it('should hide chat input when not in chat mode', () => {
+ // Arrange
+ mockUseDebugConfigurationContext.mockReturnValue(createDebugConfiguration({
+ mode: AppModeEnum.COMPLETION,
+ }))
+ const multipleModelConfigs = [createModelAndParameter()]
+
+ // Act
+ renderComponent({ multipleModelConfigs })
+
+ // Assert
+ expect(screen.queryByTestId('chat-input-area')).not.toBeInTheDocument()
+ expect(screen.getAllByTestId('debug-item')).toHaveLength(1)
+ })
+ })
+
+ describe('sending flow', () => {
+ it('should emit chat event when allowed to send', () => {
+ // Arrange
+ const checkCanSend = jest.fn(() => true)
+ const multipleModelConfigs = [createModelAndParameter(), createModelAndParameter()]
+ renderComponent({ multipleModelConfigs, checkCanSend })
+
+ // Act
+ fireEvent.click(screen.getByRole('button', { name: /send/i }))
+
+ // Assert
+ expect(checkCanSend).toHaveBeenCalled()
+ expect(mockEventEmitter.emit).toHaveBeenCalledWith({
+ type: APP_CHAT_WITH_MULTIPLE_MODEL,
+ payload: {
+ message: 'test message',
+ files: mockFiles,
+ },
+ })
+ })
+
+ it('should emit when no checkCanSend is provided', () => {
+ renderComponent()
+
+ fireEvent.click(screen.getByRole('button', { name: /send/i }))
+
+ expect(mockEventEmitter.emit).toHaveBeenCalledWith({
+ type: APP_CHAT_WITH_MULTIPLE_MODEL,
+ payload: {
+ message: 'test message',
+ files: mockFiles,
+ },
+ })
+ })
+
+ it('should block sending when checkCanSend returns false', () => {
+ // Arrange
+ const checkCanSend = jest.fn(() => false)
+ renderComponent({ checkCanSend })
+
+ // Act
+ fireEvent.click(screen.getByRole('button', { name: /send/i }))
+
+ // Assert
+ expect(checkCanSend).toHaveBeenCalled()
+ expect(mockEventEmitter.emit).not.toHaveBeenCalled()
+ })
+
+ it('should tolerate missing event emitter without throwing', () => {
+ mockUseEventEmitterContext.mockReturnValue({ eventEmitter: null })
+ renderComponent()
+
+ expect(() => fireEvent.click(screen.getByRole('button', { name: /send/i }))).not.toThrow()
+ expect(mockEventEmitter.emit).not.toHaveBeenCalled()
+ })
+ })
+
+ describe('layout sizing and positioning', () => {
+ const expectItemLayout = (
+ element: HTMLElement,
+ expectation: {
+ width?: string
+ height?: string
+ transform: string
+ classes?: string[]
+ },
+ ) => {
+ if (expectation.width !== undefined)
+ expect(element.style.width).toBe(expectation.width)
+ else
+ expect(element.style.width).toBe('')
+
+ if (expectation.height !== undefined)
+ expect(element.style.height).toBe(expectation.height)
+ else
+ expect(element.style.height).toBe('')
+
+ expect(element.style.transform).toBe(expectation.transform)
+ expectation.classes?.forEach(cls => expect(element).toHaveClass(cls))
+ }
+
+ it('should arrange items in two-column layout for two models', () => {
+ // Arrange
+ const multipleModelConfigs = [createModelAndParameter(), createModelAndParameter()]
+
+ // Act
+ renderComponent({ multipleModelConfigs })
+ const items = screen.getAllByTestId('debug-item')
+
+ // Assert
+ expect(items).toHaveLength(2)
+ expectItemLayout(items[0], {
+ width: 'calc(50% - 4px - 24px)',
+ height: '100%',
+ transform: 'translateX(0) translateY(0)',
+ classes: ['mr-2'],
+ })
+ expectItemLayout(items[1], {
+ width: 'calc(50% - 4px - 24px)',
+ height: '100%',
+ transform: 'translateX(calc(100% + 8px)) translateY(0)',
+ classes: [],
+ })
+ })
+
+ it('should arrange items in thirds for three models', () => {
+ // Arrange
+ const multipleModelConfigs = [createModelAndParameter(), createModelAndParameter(), createModelAndParameter()]
+
+ // Act
+ renderComponent({ multipleModelConfigs })
+ const items = screen.getAllByTestId('debug-item')
+
+ // Assert
+ expect(items).toHaveLength(3)
+ expectItemLayout(items[0], {
+ width: 'calc(33.3% - 5.33px - 16px)',
+ height: '100%',
+ transform: 'translateX(0) translateY(0)',
+ classes: ['mr-2'],
+ })
+ expectItemLayout(items[1], {
+ width: 'calc(33.3% - 5.33px - 16px)',
+ height: '100%',
+ transform: 'translateX(calc(100% + 8px)) translateY(0)',
+ classes: ['mr-2'],
+ })
+ expectItemLayout(items[2], {
+ width: 'calc(33.3% - 5.33px - 16px)',
+ height: '100%',
+ transform: 'translateX(calc(200% + 16px)) translateY(0)',
+ classes: [],
+ })
+ })
+
+ it('should position items on a grid for four models', () => {
+ // Arrange
+ const multipleModelConfigs = [
+ createModelAndParameter(),
+ createModelAndParameter(),
+ createModelAndParameter(),
+ createModelAndParameter(),
+ ]
+
+ // Act
+ renderComponent({ multipleModelConfigs })
+ const items = screen.getAllByTestId('debug-item')
+
+ // Assert
+ expect(items).toHaveLength(4)
+ expectItemLayout(items[0], {
+ width: 'calc(50% - 4px - 24px)',
+ height: 'calc(50% - 4px)',
+ transform: 'translateX(0) translateY(0)',
+ classes: ['mr-2', 'mb-2'],
+ })
+ expectItemLayout(items[1], {
+ width: 'calc(50% - 4px - 24px)',
+ height: 'calc(50% - 4px)',
+ transform: 'translateX(calc(100% + 8px)) translateY(0)',
+ classes: ['mb-2'],
+ })
+ expectItemLayout(items[2], {
+ width: 'calc(50% - 4px - 24px)',
+ height: 'calc(50% - 4px)',
+ transform: 'translateX(0) translateY(calc(100% + 8px))',
+ classes: ['mr-2'],
+ })
+ expectItemLayout(items[3], {
+ width: 'calc(50% - 4px - 24px)',
+ height: 'calc(50% - 4px)',
+ transform: 'translateX(calc(100% + 8px)) translateY(calc(100% + 8px))',
+ classes: [],
+ })
+ })
+
+ it('should fall back to single column layout when only one model is provided', () => {
+ // Arrange
+ const multipleModelConfigs = [createModelAndParameter()]
+
+ // Act
+ renderComponent({ multipleModelConfigs })
+ const item = screen.getByTestId('debug-item')
+
+ // Assert
+ expectItemLayout(item, {
+ transform: 'translateX(0) translateY(0)',
+ classes: [],
+ })
+ })
+
+ it('should set scroll area height for chat modes', () => {
+ const { container } = renderComponent()
+ const scrollArea = container.querySelector('.relative.mb-3.grow.overflow-auto.px-6') as HTMLElement
+ expect(scrollArea).toBeInTheDocument()
+ expect(scrollArea.style.height).toBe('calc(100% - 60px)')
+ })
+
+ it('should set full height when chat input is hidden', () => {
+ mockUseDebugConfigurationContext.mockReturnValue(createDebugConfiguration({
+ mode: AppModeEnum.COMPLETION,
+ }))
+
+ const { container } = renderComponent()
+ const scrollArea = container.querySelector('.relative.mb-3.grow.overflow-auto.px-6') as HTMLElement
+ expect(scrollArea.style.height).toBe('100%')
+ })
+ })
+})
diff --git a/web/app/components/base/amplitude/AmplitudeProvider.tsx b/web/app/components/base/amplitude/AmplitudeProvider.tsx
index 6f2f43b614..c242326c30 100644
--- a/web/app/components/base/amplitude/AmplitudeProvider.tsx
+++ b/web/app/components/base/amplitude/AmplitudeProvider.tsx
@@ -11,13 +11,19 @@ export type IAmplitudeProps = {
sessionReplaySampleRate?: number
}
+// Check if Amplitude should be enabled
+export const isAmplitudeEnabled = () => {
+ const apiKey = process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY
+ return IS_CLOUD_EDITION && !!apiKey
+}
+
const AmplitudeProvider: FC = ({
apiKey = process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY ?? '',
sessionReplaySampleRate = 1,
}) => {
useEffect(() => {
- // Only enable in Saas edition
- if (!IS_CLOUD_EDITION)
+ // Only enable in Saas edition with valid API key
+ if (!isAmplitudeEnabled())
return
// Initialize Amplitude
diff --git a/web/app/components/base/amplitude/index.ts b/web/app/components/base/amplitude/index.ts
index e447a0c5e3..acc792339e 100644
--- a/web/app/components/base/amplitude/index.ts
+++ b/web/app/components/base/amplitude/index.ts
@@ -1,2 +1,2 @@
-export { default } from './AmplitudeProvider'
+export { default, isAmplitudeEnabled } from './AmplitudeProvider'
export { resetUser, setUserId, setUserProperties, trackEvent } from './utils'
diff --git a/web/app/components/base/amplitude/utils.ts b/web/app/components/base/amplitude/utils.ts
index 8423c43bb2..57b96243ec 100644
--- a/web/app/components/base/amplitude/utils.ts
+++ b/web/app/components/base/amplitude/utils.ts
@@ -1,4 +1,5 @@
import * as amplitude from '@amplitude/analytics-browser'
+import { isAmplitudeEnabled } from './AmplitudeProvider'
/**
* Track custom event
@@ -6,6 +7,8 @@ import * as amplitude from '@amplitude/analytics-browser'
* @param eventProperties Event properties (optional)
*/
export const trackEvent = (eventName: string, eventProperties?: Record) => {
+ if (!isAmplitudeEnabled())
+ return
amplitude.track(eventName, eventProperties)
}
@@ -14,6 +17,8 @@ export const trackEvent = (eventName: string, eventProperties?: Record {
+ if (!isAmplitudeEnabled())
+ return
amplitude.setUserId(userId)
}
@@ -22,6 +27,8 @@ export const setUserId = (userId: string) => {
* @param properties User properties
*/
export const setUserProperties = (properties: Record) => {
+ if (!isAmplitudeEnabled())
+ return
const identifyEvent = new amplitude.Identify()
Object.entries(properties).forEach(([key, value]) => {
identifyEvent.set(key, value)
@@ -33,5 +40,7 @@ export const setUserProperties = (properties: Record) => {
* Reset user (e.g., when user logs out)
*/
export const resetUser = () => {
+ if (!isAmplitudeEnabled())
+ return
amplitude.reset()
}
diff --git a/web/docker/entrypoint.sh b/web/docker/entrypoint.sh
index 3325690239..565c906624 100755
--- a/web/docker/entrypoint.sh
+++ b/web/docker/entrypoint.sh
@@ -25,6 +25,8 @@ export NEXT_PUBLIC_SENTRY_DSN=${SENTRY_DSN}
export NEXT_PUBLIC_SITE_ABOUT=${SITE_ABOUT}
export NEXT_TELEMETRY_DISABLED=${NEXT_TELEMETRY_DISABLED}
+export NEXT_PUBLIC_AMPLITUDE_API_KEY=${AMPLITUDE_API_KEY}
+
export NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS=${TEXT_GENERATION_TIMEOUT_MS}
export NEXT_PUBLIC_CSP_WHITELIST=${CSP_WHITELIST}
export NEXT_PUBLIC_ALLOW_EMBED=${ALLOW_EMBED}
diff --git a/web/i18n/de-DE/billing.ts b/web/i18n/de-DE/billing.ts
index c0626a0bb8..1b92851558 100644
--- a/web/i18n/de-DE/billing.ts
+++ b/web/i18n/de-DE/billing.ts
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'Abrechnung und Abonnements',
viewBillingDescription: 'Zahlungsmethoden, Rechnungen und Abonnementänderungen verwalten',
viewBillingAction: 'Verwalten',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Upgrade, um mehrere Dokumente gleichzeitig hochzuladen',
+ description: 'Sie haben das Upload-Limit erreicht – in Ihrem aktuellen Tarif kann jeweils nur ein Dokument ausgewählt und hochgeladen werden.',
+ },
+ uploadMultipleFiles: {
+ title: 'Upgrade, um den Massen-Upload von Dokumenten freizuschalten',
+ description: 'Lade mehrere Dokumente gleichzeitig hoch, um Zeit zu sparen und die Effizienz zu steigern.',
+ },
+ addChunks: {
+ title: 'Upgraden, um weiterhin Abschnitte hinzuzufügen',
+ description: 'Sie haben das Limit für das Hinzufügen von Abschnitten in diesem Tarif erreicht.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/en-US/billing.ts b/web/i18n/en-US/billing.ts
index 6ea37640d6..1ab4e3f0d4 100644
--- a/web/i18n/en-US/billing.ts
+++ b/web/i18n/en-US/billing.ts
@@ -232,7 +232,7 @@ const translation = {
},
addChunks: {
title: 'Upgrade to continue adding chunks',
- description: 'You’ve reached the limit of adding chunks for this plan. ',
+ description: 'You’ve reached the limit of adding chunks for this plan.',
},
},
}
diff --git a/web/i18n/es-ES/billing.ts b/web/i18n/es-ES/billing.ts
index 29652af07a..37c8701948 100644
--- a/web/i18n/es-ES/billing.ts
+++ b/web/i18n/es-ES/billing.ts
@@ -161,7 +161,7 @@ const translation = {
includesTitle: 'Todo de Community, además:',
name: 'Premium',
for: 'Para organizaciones y equipos de tamaño mediano',
- features: ['Confiabilidad Autogestionada por Diversos Proveedores de Nube', 'Espacio de trabajo único', 'Personalización de Logotipo y Marca de la Aplicación Web', 'Soporte prioritario por correo electrónico y chat'],
+ features: ['Confiabilidad autogestionada por varios proveedores de la nube', 'Espacio de trabajo único', 'Personalización de Logotipo y Marca de la Aplicación Web', 'Soporte prioritario por correo electrónico y chat'],
},
},
vectorSpace: {
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'Facturación y Suscripciones',
viewBillingDescription: 'Gestiona métodos de pago, facturas y cambios de suscripción',
viewBillingAction: 'Gestionar',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Actualiza para subir varios documentos a la vez',
+ description: 'Has alcanzado el límite de carga: solo se puede seleccionar y subir un documento a la vez en tu plan actual.',
+ },
+ uploadMultipleFiles: {
+ title: 'Actualiza para desbloquear la carga de documentos en lote',
+ description: 'Carga en lote más documentos a la vez para ahorrar tiempo y mejorar la eficiencia.',
+ },
+ addChunks: {
+ title: 'Actualiza para seguir agregando fragmentos',
+ description: 'Has alcanzado el límite de agregar fragmentos para este plan.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/fa-IR/billing.ts b/web/i18n/fa-IR/billing.ts
index 387be157e6..db050fc76d 100644
--- a/web/i18n/fa-IR/billing.ts
+++ b/web/i18n/fa-IR/billing.ts
@@ -161,7 +161,7 @@ const translation = {
name: 'پیشرفته',
priceTip: 'بر اساس بازار ابری',
comingSoon: 'پشتیبانی مایکروسافت آژور و گوگل کلود به زودی در دسترس خواهد بود',
- features: ['قابلیت اطمینان خودمدیریتی توسط ارائهدهندگان مختلف ابری', 'فضای کاری تنها', 'سفارشیسازی لوگو و برند وباپ', 'پشتیبانی اولویتدار ایمیل و چت'],
+ features: ['قابلیت اطمینان خودمدیریتی توسط ارائهدهندگان مختلف ابری', 'فضای کاری تنها', 'سفارشیسازی لوگو و برندینگ وباپ', 'پشتیبانی اولویتدار ایمیل و چت'],
},
},
vectorSpace: {
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'صورتحساب و اشتراکها',
viewBillingDescription: 'مدیریت روشهای پرداخت، صورتحسابها و تغییرات اشتراک',
viewBillingAction: 'مدیریت',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'ارتقا برای آپلود همزمان چندین سند',
+ description: 'شما به حد آپلود رسیدهاید — در طرح فعلی خود تنها میتوانید یک سند را در هر بار انتخاب و آپلود کنید.',
+ },
+ uploadMultipleFiles: {
+ title: 'ارتقا دهید تا امکان بارگذاری دستهای اسناد فعال شود',
+ description: 'بارگذاری دستهای چندین سند بهطور همزمان برای صرفهجویی در زمان و افزایش کارایی.',
+ },
+ addChunks: {
+ title: 'برای ادامه افزودن بخشها ارتقا دهید',
+ description: 'شما به حد اضافه کردن بخشها برای این طرح رسیدهاید.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/fr-FR/billing.ts b/web/i18n/fr-FR/billing.ts
index 83ea177557..7e163df86f 100644
--- a/web/i18n/fr-FR/billing.ts
+++ b/web/i18n/fr-FR/billing.ts
@@ -137,7 +137,7 @@ const translation = {
name: 'Entreprise',
description: 'Obtenez toutes les capacités et le support pour les systèmes à grande échelle et critiques pour la mission.',
includesTitle: 'Tout ce qui est inclus dans le plan Équipe, plus :',
- features: ['Solutions de déploiement évolutives de niveau entreprise', 'Autorisation de licence commerciale', 'Fonctionnalités exclusives pour les entreprises', 'Espaces de travail multiples et gestion d\'entreprise', 'SSO', 'Accords de niveau de service négociés par les partenaires de Dify', 'Sécurité et contrôles avancés', 'Mises à jour et maintenance par Dify Officiellement', 'Assistance technique professionnelle'],
+ features: ['Solutions de déploiement évolutives de niveau entreprise', 'Autorisation de licence commerciale', 'Fonctionnalités exclusives pour les entreprises', 'Espaces de travail multiples et gestion d\'entreprise', 'SSO', 'Accords sur les SLA négociés par les partenaires Dify', 'Sécurité et contrôles avancés', 'Mises à jour et maintenance par Dify Officiellement', 'Assistance technique professionnelle'],
for: 'Pour les équipes de grande taille',
btnText: 'Contacter les ventes',
priceTip: 'Facturation Annuel Seulement',
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'Facturation et abonnements',
viewBillingDescription: 'Gérer les méthodes de paiement, les factures et les modifications d\'abonnement',
viewBillingAction: 'Gérer',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Passez à la version supérieure pour télécharger plusieurs documents à la fois',
+ description: 'Vous avez atteint la limite de téléchargement — un seul document peut être sélectionné et téléchargé à la fois avec votre abonnement actuel.',
+ },
+ uploadMultipleFiles: {
+ title: 'Passez à la version supérieure pour débloquer le téléchargement de documents en lot',
+ description: 'Téléchargez plusieurs documents à la fois pour gagner du temps et améliorer l\'efficacité.',
+ },
+ addChunks: {
+ title: 'Mettez à niveau pour continuer à ajouter des morceaux',
+ description: 'Vous avez atteint la limite d\'ajout de morceaux pour ce plan.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/hi-IN/billing.ts b/web/i18n/hi-IN/billing.ts
index 52aa3a0305..19b3b96b46 100644
--- a/web/i18n/hi-IN/billing.ts
+++ b/web/i18n/hi-IN/billing.ts
@@ -213,6 +213,20 @@ const translation = {
viewBillingTitle: 'बिलिंग और सब्सक्रिप्शन',
viewBillingDescription: 'भुगतान के तरीकों, चालानों और सदस्यता में बदलावों का प्रबंधन करें',
viewBillingAction: 'प्रबंध करना',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'एक बार में कई दस्तावेज़ अपलोड करने के लिए अपग्रेड करें',
+ description: 'आपने अपलोड की सीमा तक पहुँच लिया है — आपके वर्तमान प्लान पर एक समय में केवल एक ही दस्तावेज़ चुना और अपलोड किया जा सकता है।',
+ },
+ uploadMultipleFiles: {
+ title: 'बैच दस्तावेज़ अपलोड अनलॉक करने के लिए अपग्रेड करें',
+ description: 'समय बचाने और कार्यक्षमता बढ़ाने के लिए एक बार में अधिक दस्तावेज़ बैच-अपलोड करें।',
+ },
+ addChunks: {
+ title: 'अधिक चंक्स जोड़ने के लिए अपग्रेड करें',
+ description: 'आप इस योजना के लिए टुकड़े जोड़ने की सीमा तक पहुँच चुके हैं।',
+ },
+ },
}
export default translation
diff --git a/web/i18n/id-ID/billing.ts b/web/i18n/id-ID/billing.ts
index 640bf9aeb6..527cf43996 100644
--- a/web/i18n/id-ID/billing.ts
+++ b/web/i18n/id-ID/billing.ts
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'Penagihan dan Langganan',
viewBillingDescription: 'Kelola metode pembayaran, faktur, dan perubahan langganan',
viewBillingAction: 'Kelola',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Tingkatkan untuk mengunggah beberapa dokumen sekaligus',
+ description: 'Anda telah mencapai batas unggah — hanya satu dokumen yang dapat dipilih dan diunggah sekaligus dengan paket Anda saat ini.',
+ },
+ uploadMultipleFiles: {
+ title: 'Tingkatkan untuk membuka unggahan dokumen batch',
+ description: 'Unggah lebih banyak dokumen sekaligus untuk menghemat waktu dan meningkatkan efisiensi.',
+ },
+ addChunks: {
+ title: 'Tingkatkan untuk terus menambahkan potongan',
+ description: 'Anda telah mencapai batas penambahan potongan untuk paket ini.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/it-IT/billing.ts b/web/i18n/it-IT/billing.ts
index 141cefa21f..c8888ac86a 100644
--- a/web/i18n/it-IT/billing.ts
+++ b/web/i18n/it-IT/billing.ts
@@ -164,7 +164,7 @@ const translation = {
for: 'Per utenti individuali, piccole squadre o progetti non commerciali',
},
premium: {
- features: ['Affidabilità autogestita dai vari provider cloud', 'Spazio di lavoro singolo', 'Personalizzazione del Logo e del Marchio dell\'App Web', 'Assistenza Prioritaria via Email e Chat'],
+ features: ['Affidabilità Autogestita dai Vari Provider Cloud', 'Spazio di lavoro singolo', 'Personalizzazione del Logo e del Marchio dell\'App Web', 'Assistenza Prioritaria via Email e Chat'],
name: 'Premium',
priceTip: 'Basato su Cloud Marketplace',
includesTitle: 'Tutto dalla Community, oltre a:',
@@ -213,6 +213,20 @@ const translation = {
viewBillingTitle: 'Fatturazione e Abbonamenti',
viewBillingDescription: 'Gestisci metodi di pagamento, fatture e modifiche all\'abbonamento',
viewBillingAction: 'Gestire',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Aggiorna per caricare più documenti contemporaneamente',
+ description: 'Hai raggiunto il limite di caricamento: sul tuo piano attuale può essere selezionato e caricato un solo documento alla volta.',
+ },
+ uploadMultipleFiles: {
+ title: 'Aggiorna per sbloccare il caricamento di documenti in batch',
+ description: 'Carica più documenti contemporaneamente per risparmiare tempo e migliorare l\'efficienza.',
+ },
+ addChunks: {
+ title: 'Aggiorna per continuare ad aggiungere blocchi',
+ description: 'Hai raggiunto il limite di aggiunta di blocchi per questo piano.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/ko-KR/billing.ts b/web/i18n/ko-KR/billing.ts
index c11d2bd11c..756ba53cad 100644
--- a/web/i18n/ko-KR/billing.ts
+++ b/web/i18n/ko-KR/billing.ts
@@ -152,7 +152,7 @@ const translation = {
btnText: '판매 문의하기',
for: '대규모 팀을 위해',
priceTip: '연간 청구 전용',
- features: ['기업용 확장형 배포 솔루션', '상업용 라이선스 승인', '독점 기업 기능', '여러 작업 공간 및 기업 관리', '싱글 사인온', 'Dify 파트너가 협상한 SLA', '고급 보안 및 제어', 'Dify 공식 업데이트 및 유지보수', '전문 기술 지원'],
+ features: ['기업용 확장형 배포 솔루션', '상업용 라이선스 승인', '독점 기업 기능', '여러 작업 공간 및 기업 관리', '싱글 사인온', 'Dify 파트너가 협상한 SLA', '고급 보안 및 제어', 'Dify 공식 업데이트 및 유지 관리', '전문 기술 지원'],
},
community: {
btnText: '커뮤니티 시작하기',
@@ -215,6 +215,20 @@ const translation = {
viewBillingTitle: '청구 및 구독',
viewBillingDescription: '결제 수단, 청구서 및 구독 변경 관리',
viewBillingAction: '관리하다',
+ upgrade: {
+ uploadMultiplePages: {
+ title: '한 번에 여러 문서를 업로드하려면 업그레이드하세요',
+ description: '업로드 한도에 도달했습니다 — 현재 요금제에서는 한 번에 한 개의 문서만 선택하고 업로드할 수 있습니다.',
+ },
+ uploadMultipleFiles: {
+ title: '업그레이드하여 대량 문서 업로드 기능 잠금 해제',
+ description: '한 번에 더 많은 문서를 일괄 업로드하여 시간 절약과 효율성을 높이세요.',
+ },
+ addChunks: {
+ title: '계속해서 조각을 추가하려면 업그레이드하세요',
+ description: '이 요금제에서는 더 이상 청크를 추가할 수 있는 한도에 도달했습니다.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/pl-PL/billing.ts b/web/i18n/pl-PL/billing.ts
index 8131f74b1e..6b49df928d 100644
--- a/web/i18n/pl-PL/billing.ts
+++ b/web/i18n/pl-PL/billing.ts
@@ -147,7 +147,7 @@ const translation = {
description:
'Uzyskaj pełne możliwości i wsparcie dla systemów o kluczowym znaczeniu dla misji.',
includesTitle: 'Wszystko w planie Zespołowym, plus:',
- features: ['Skalowalne rozwiązania wdrożeniowe klasy korporacyjnej', 'Autoryzacja licencji komercyjnej', 'Ekskluzywne funkcje dla przedsiębiorstw', 'Wiele przestrzeni roboczych i zarządzanie przedsiębiorstwem', 'SSO', 'Negocjowane umowy SLA przez partnerów Dify', 'Zaawansowane zabezpieczenia i kontrola', 'Aktualizacje i konserwacja przez Dify oficjalnie', 'Profesjonalne wsparcie techniczne'],
+ features: ['Rozwiązania wdrożeniowe klasy korporacyjnej, skalowalne', 'Autoryzacja licencji komercyjnej', 'Ekskluzywne funkcje dla przedsiębiorstw', 'Wiele przestrzeni roboczych i zarządzanie przedsiębiorstwem', 'SSO', 'Negocjowane umowy SLA przez partnerów Dify', 'Zaawansowane zabezpieczenia i kontrola', 'Aktualizacje i konserwacja przez Dify oficjalnie', 'Profesjonalne wsparcie techniczne'],
priceTip: 'Tylko roczne fakturowanie',
btnText: 'Skontaktuj się z działem sprzedaży',
for: 'Dla dużych zespołów',
@@ -163,7 +163,7 @@ const translation = {
for: 'Dla użytkowników indywidualnych, małych zespołów lub projektów niekomercyjnych',
},
premium: {
- features: ['Niezawodność zarządzana samodzielnie przez różnych dostawców chmury', 'Pojedyncza przestrzeń robocza', 'Dostosowywanie logo i identyfikacji wizualnej aplikacji webowej', 'Priorytetowe wsparcie e-mail i czat'],
+ features: ['Niezawodność zarządzana samodzielnie przez różnych dostawców chmury', 'Pojedyncza przestrzeń robocza', 'Dostosowywanie logo i marki aplikacji webowej', 'Priorytetowe wsparcie e-mail i czat'],
description: 'Dla średnich organizacji i zespołów',
for: 'Dla średnich organizacji i zespołów',
name: 'Premium',
@@ -212,6 +212,20 @@ const translation = {
viewBillingTitle: 'Rozliczenia i subskrypcje',
viewBillingDescription: 'Zarządzaj metodami płatności, fakturami i zmianami subskrypcji',
viewBillingAction: 'Zarządzać',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Przejdź na wyższą wersję, aby przesyłać wiele dokumentów jednocześnie',
+ description: 'Osiągnąłeś limit przesyłania — w ramach obecnego planu można wybrać i przesłać tylko jeden dokument naraz.',
+ },
+ uploadMultipleFiles: {
+ title: 'Uaktualnij, aby odblokować przesyłanie dokumentów wsadowych',
+ description: 'Przesyłaj wiele dokumentów jednocześnie, aby zaoszczędzić czas i zwiększyć wydajność.',
+ },
+ addChunks: {
+ title: 'Uaktualnij, aby kontynuować dodawanie fragmentów',
+ description: 'Osiągnąłeś limit dodawania fragmentów w tym planie.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/pt-BR/billing.ts b/web/i18n/pt-BR/billing.ts
index 08976988e6..78375fdaae 100644
--- a/web/i18n/pt-BR/billing.ts
+++ b/web/i18n/pt-BR/billing.ts
@@ -153,7 +153,7 @@ const translation = {
for: 'Para Usuários Individuais, Pequenas Equipes ou Projetos Não Comerciais',
},
premium: {
- features: ['Confiabilidade Autogerenciada por Diversos Provedores de Nuvem', 'Espaço de Trabalho Único', 'Personalização de Logo e Marca do WebApp', 'Suporte Prioritário por Email e Chat'],
+ features: ['Confiabilidade Autogerenciada por Diversos Provedores de Nuvem', 'Espaço de Trabalho Único', 'Personalização de Logo e Branding do WebApp', 'Suporte Prioritário por E-mail e Chat'],
includesTitle: 'Tudo da Comunidade, além de:',
for: 'Para organizações e equipes de médio porte',
price: 'Escalável',
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'Faturamento e Assinaturas',
viewBillingDescription: 'Gerencie métodos de pagamento, faturas e alterações de assinatura',
viewBillingAction: 'Gerenciar',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Atualize para enviar vários documentos de uma vez',
+ description: 'Você atingiu o limite de upload — apenas um documento pode ser selecionado e enviado por vez no seu plano atual.',
+ },
+ uploadMultipleFiles: {
+ title: 'Atualize para desbloquear o envio de documentos em lote',
+ description: 'Faça upload de mais documentos de uma vez para economizar tempo e aumentar a eficiência.',
+ },
+ addChunks: {
+ title: 'Faça upgrade para continuar adicionando blocos',
+ description: 'Você atingiu o limite de adição de blocos para este plano.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/ro-RO/billing.ts b/web/i18n/ro-RO/billing.ts
index 79bee0b320..3c57f4dfa9 100644
--- a/web/i18n/ro-RO/billing.ts
+++ b/web/i18n/ro-RO/billing.ts
@@ -137,14 +137,14 @@ const translation = {
name: 'Întreprindere',
description: 'Obțineți capacități și asistență complete pentru sisteme critice la scară largă.',
includesTitle: 'Tot ce este în planul Echipă, plus:',
- features: ['Soluții de implementare scalabile la nivel de întreprindere', 'Autorizație de licență comercială', 'Funcții Exclusive pentru Afaceri', 'Multiple spații de lucru și gestionarea întreprinderii', 'Autentificare unică', 'SLA-uri negociate de partenerii Dify', 'Securitate și Control Avansate', 'Actualizări și întreținere de către Dify Oficial', 'Asistență Tehnică Profesională'],
+ features: ['Soluții de implementare scalabile la nivel de întreprindere', 'Autorizație de licență comercială', 'Funcții Exclusive pentru Afaceri', 'Mai multe spații de lucru și managementul întreprinderii', 'Autentificare unică', 'SLA-uri negociate de partenerii Dify', 'Securitate și Control Avansate', 'Actualizări și întreținere de către Dify Oficial', 'Asistență Tehnică Profesională'],
for: 'Pentru echipe de mari dimensiuni',
price: 'Personalizat',
priceTip: 'Facturare anuală doar',
btnText: 'Contactați Vânzări',
},
community: {
- features: ['Toate Funcțiile Principale Lansate în Repositorul Public', 'Spațiu de lucru unic', 'Respectă Licența Open Source Dify'],
+ features: ['Toate Funcționalitățile de Bază Lansate în Repositorul Public', 'Spațiu de lucru unic', 'Respectă Licența Open Source Dify'],
description: 'Pentru utilizatori individuali, echipe mici sau proiecte necomerciale',
btnText: 'Începe cu Comunitatea',
price: 'Gratuit',
@@ -153,7 +153,7 @@ const translation = {
includesTitle: 'Funcții gratuite:',
},
premium: {
- features: ['Fiabilitate autogestionată de diferiți furnizori de cloud', 'Spațiu de lucru unic', 'Personalizare Logo și Branding pentru WebApp', 'Asistență prioritară prin email și chat'],
+ features: ['Fiabilitate autogestionată de diferiți furnizori de cloud', 'Spațiu de lucru unic', 'Personalizare logo și branding pentru aplicația web', 'Asistență prioritară prin e-mail și chat'],
btnText: 'Obține Premium în',
description: 'Pentru organizații și echipe de dimensiuni medii',
includesTitle: 'Totul din Comunitate, plus:',
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'Facturare și abonamente',
viewBillingDescription: 'Gestionează metodele de plată, facturile și modificările abonamentului',
viewBillingAction: 'Gestiona',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Actualizează pentru a încărca mai multe documente odată',
+ description: 'Ați atins limita de încărcare — poate fi selectat și încărcat doar un singur document odată în planul dvs. actual.',
+ },
+ uploadMultipleFiles: {
+ title: 'Fă upgrade pentru a debloca încărcarea documentelor în masă',
+ description: 'Încărcați mai multe documente simultan pentru a economisi timp și a îmbunătăți eficiența.',
+ },
+ addChunks: {
+ title: 'Actualizează pentru a continua să adaugi segmente',
+ description: 'Ai atins limita de adăugare a segmentelor pentru acest plan.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/ru-RU/billing.ts b/web/i18n/ru-RU/billing.ts
index 8f5c6dcf38..694e848f04 100644
--- a/web/i18n/ru-RU/billing.ts
+++ b/web/i18n/ru-RU/billing.ts
@@ -137,14 +137,14 @@ const translation = {
name: 'Корпоративный',
description: 'Получите полный набор возможностей и поддержку для крупномасштабных критически важных систем.',
includesTitle: 'Все в командном плане, плюс:',
- features: ['Масштабируемые решения для развертывания корпоративного уровня', 'Разрешение на коммерческую лицензию', 'Эксклюзивные корпоративные функции', 'Несколько рабочих пространств и корпоративное управление', 'Единый вход (SSO)', 'Договоренные SLA с партнёрами Dify', 'Расширенные функции безопасности и управления', 'Обновления и обслуживание от Dify официально', 'Профессиональная техническая поддержка'],
+ features: ['Масштабируемые решения для развертывания корпоративного уровня', 'Разрешение на коммерческую лицензию', 'Эксклюзивные корпоративные функции', 'Несколько рабочих пространств и корпоративное управление', 'Единый вход (SSO)', 'Согласованные SLA с партнёрами Dify', 'Расширенные функции безопасности и управления', 'Обновления и обслуживание от Dify официально', 'Профессиональная техническая поддержка'],
price: 'Пользовательский',
priceTip: 'Только годовая подписка',
for: 'Для команд большого размера',
btnText: 'Связаться с отделом продаж',
},
community: {
- features: ['Все основные функции выпущены в публичный репозиторий', 'Одиночное рабочее пространство', 'Соответствует лицензии с открытым исходным кодом Dify'],
+ features: ['Все основные функции выпущены в публичный репозиторий', 'Единое рабочее пространство', 'Соответствует лицензии с открытым исходным кодом Dify'],
name: 'Сообщество',
btnText: 'Начните с сообщества',
price: 'Свободно',
@@ -153,7 +153,7 @@ const translation = {
for: 'Для отдельных пользователей, малых команд или некоммерческих проектов',
},
premium: {
- features: ['Самоуправляемая надежность у различных облачных провайдеров', 'Одиночное рабочее пространство', 'Настройка логотипа и брендинга веб-приложения', 'Приоритетная поддержка по электронной почте и в чате'],
+ features: ['Самоуправляемая надежность у различных облачных провайдеров', 'Единое рабочее пространство', 'Настройка логотипа и брендинга веб-приложения', 'Приоритетная поддержка по электронной почте и в чате'],
description: 'Для средних организаций и команд',
includesTitle: 'Всё из Сообщества, плюс:',
priceTip: 'На основе облачного маркетплейса',
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'Платежи и подписки',
viewBillingDescription: 'Управляйте способами оплаты, счетами и изменениями подписки',
viewBillingAction: 'Управлять',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Обновите версию, чтобы загружать несколько документов одновременно',
+ description: 'Вы достигли лимита загрузки — на вашем текущем тарифном плане можно выбрать и загрузить только один документ за раз.',
+ },
+ uploadMultipleFiles: {
+ title: 'Обновите версию, чтобы включить массовую загрузку документов',
+ description: 'Загружайте больше документов одновременно, чтобы сэкономить время и повысить эффективность.',
+ },
+ addChunks: {
+ title: 'Обновите версию, чтобы продолжить добавление блоков',
+ description: 'Вы достигли предела добавления чанков по этому тарифному плану.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/sl-SI/billing.ts b/web/i18n/sl-SI/billing.ts
index fa6b4b7bc6..5dabcc9f02 100644
--- a/web/i18n/sl-SI/billing.ts
+++ b/web/i18n/sl-SI/billing.ts
@@ -137,14 +137,14 @@ const translation = {
name: 'Podjetje',
description: 'Pridobite vse zmogljivosti in podporo za velike sisteme kritične za misijo.',
includesTitle: 'Vse v načrtu Ekipa, plus:',
- features: ['Razširljive rešitve za uvajanje na ravni podjetja', 'Pooblastilo za komercialno licenco', 'Ekskluzivne funkcije za podjetja', 'Več delovnih prostorov in upravljanje podjetja', 'SSO', 'Pogajani SLA-ji s strani partnerjev Dify', 'Napredna varnost in nadzor', 'Posodobitve in vzdrževanje uradno s strani Dify', 'Strokovna tehnična podpora'],
+ features: ['Razširljive rešitve za uvajanje na ravni podjetja', 'Pooblastilo za komercialno licenco', 'Ekskluzivne funkcije za podjetja', 'Več delovnih prostorov in upravljanje podjetja', 'SSO', 'Pogajani SLA-ji s strani partnerjev Dify', 'Napredna varnost in nadzor', 'Posodobitve in vzdrževanje s strani Dify uradno', 'Strokovna tehnična podpora'],
priceTip: 'Letno zaračunavanje samo',
price: 'Po meri',
btnText: 'Kontaktirajte prodajo',
for: 'Za velike ekipe',
},
community: {
- features: ['Vse osnovne funkcije so izdane v javnem repozitoriju', 'Enotno delovno okolje', 'V skladu z Dify licenco odprte kode'],
+ features: ['Vse osnovne funkcije so izdane v javni repozitorij', 'Enotno delovno okolje', 'V skladu z Dify licenco odprte kode'],
includesTitle: 'Brezplačne funkcije:',
price: 'Brezplačno',
name: 'Skupnost',
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'Fakturiranje in naročnine',
viewBillingDescription: 'Upravljajte načine plačila, račune in spremembe naročnin',
viewBillingAction: 'Upravljaj',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Nadgradite za nalaganje več dokumentov hkrati',
+ description: 'Dosegli ste omejitev nalaganja — na vašem trenutnem načrtu je mogoče izbrati in naložiti le en dokument naenkrat.',
+ },
+ uploadMultipleFiles: {
+ title: 'Nadgradite za odklep nalaganja dokumentov v skupkih',
+ description: 'Naložite več dokumentov hkrati, da prihranite čas in izboljšate učinkovitost.',
+ },
+ addChunks: {
+ title: 'Nadgradite, da nadaljujete z dodajanjem delov',
+ description: 'Dosegli ste omejitev dodajanja delov za ta načrt.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/th-TH/billing.ts b/web/i18n/th-TH/billing.ts
index ce8d3bbbef..8d8273bdce 100644
--- a/web/i18n/th-TH/billing.ts
+++ b/web/i18n/th-TH/billing.ts
@@ -137,14 +137,14 @@ const translation = {
name: 'กิจการ',
description: 'รับความสามารถและการสนับสนุนเต็มรูปแบบสําหรับระบบที่สําคัญต่อภารกิจขนาดใหญ่',
includesTitle: 'ทุกอย่างในแผนทีม รวมถึง:',
- features: ['โซลูชันการปรับใช้ที่ปรับขนาดได้สำหรับองค์กร', 'การอนุญาตใบอนุญาตเชิงพาณิชย์', 'ฟีเจอร์สำหรับองค์กรแบบพิเศษ', 'หลายพื้นที่ทำงานและการจัดการองค์กร', 'ระบบลงชื่อเพียงครั้งเดียว', 'ข้อตกลงระดับการให้บริการที่เจรจาโดยพันธมิตร Dify', 'ระบบความปลอดภัยและการควบคุมขั้นสูง', 'การอัปเดตและการบำรุงรักษาโดย Dify อย่างเป็นทางการ', 'การสนับสนุนทางเทคนิคระดับมืออาชีพ'],
+ features: ['โซลูชันการปรับใช้ที่ปรับขนาดได้สำหรับองค์กร', 'การอนุญาตใบอนุญาตเชิงพาณิชย์', 'ฟีเจอร์เฉพาะสำหรับองค์กร', 'หลายพื้นที่ทำงานและการจัดการองค์กร', 'ประกันสังคม', 'ข้อตกลง SLA ที่เจรจากับพันธมิตร Dify', 'ระบบความปลอดภัยและการควบคุมขั้นสูง', 'การอัปเดตและการบำรุงรักษาโดย Dify อย่างเป็นทางการ', 'การสนับสนุนทางเทคนิคระดับมืออาชีพ'],
btnText: 'ติดต่อฝ่ายขาย',
price: 'ที่กำหนดเอง',
for: 'สำหรับทีมขนาดใหญ่',
priceTip: 'การเรียกเก็บเงินประจำปีเท่านั้น',
},
community: {
- features: ['คุณลักษณะหลักทั้งหมดถูกปล่อยภายใต้ที่เก็บสาธารณะ', 'พื้นที่ทำงานเดียว', 'เป็นไปตามใบอนุญาตแบบเปิดของ Dify'],
+ features: ['คุณลักษณะหลักทั้งหมดถูกปล่อยภายใต้ที่เก็บสาธารณะ', 'พื้นที่ทำงานเดียว', 'เป็นไปตามใบอนุญาตแบบโอเพ่นซอร์สของ Dify'],
name: 'ชุมชน',
price: 'ฟรี',
includesTitle: 'คุณสมบัติเสรี:',
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'การเรียกเก็บเงินและการสมัครสมาชิก',
viewBillingDescription: 'จัดการวิธีการชำระเงิน ใบแจ้งหนี้ และการเปลี่ยนแปลงการสมัครสมาชิก',
viewBillingAction: 'จัดการ',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'อัปเกรดเพื่ออัปโหลดเอกสารหลายฉบับพร้อมกัน',
+ description: 'คุณได้ถึงขีดจำกัดการอัปโหลดแล้ว — สามารถเลือกและอัปโหลดเอกสารได้เพียงไฟล์เดียวต่อครั้งในแผนปัจจุบันของคุณ',
+ },
+ uploadMultipleFiles: {
+ title: 'อัปเกรดเพื่อปลดล็อกการอัปโหลดเอกสารเป็นชุด',
+ description: 'อัปโหลดเอกสารหลายชิ้นพร้อมกันในครั้งเดียวเพื่อประหยัดเวลาและเพิ่มประสิทธิภาพ',
+ },
+ addChunks: {
+ title: 'อัปเกรดเพื่อเพิ่มชิ้นส่วนต่อ',
+ description: 'คุณได้ถึงขีดจำกัดในการเพิ่มชิ้นส่วนสำหรับแผนนี้แล้ว',
+ },
+ },
}
export default translation
diff --git a/web/i18n/tr-TR/billing.ts b/web/i18n/tr-TR/billing.ts
index 06570c9818..716a350c7b 100644
--- a/web/i18n/tr-TR/billing.ts
+++ b/web/i18n/tr-TR/billing.ts
@@ -144,7 +144,7 @@ const translation = {
price: 'Özel',
},
community: {
- features: ['Tüm Temel Özellikler Açık Kaynak Depoda Yayınlandı', 'Tek Çalışma Alanı', 'Dify Açık Kaynak Lisansına uygundur'],
+ features: ['Tüm Temel Özellikler Açık Depoda Yayınlandı', 'Tek Çalışma Alanı', 'Dify Açık Kaynak Lisansına uygundur'],
price: 'Ücretsiz',
includesTitle: 'Ücretsiz Özellikler:',
name: 'Topluluk',
@@ -153,7 +153,7 @@ const translation = {
description: 'Bireysel Kullanıcılar, Küçük Ekipler veya Ticari Olmayan Projeler İçin',
},
premium: {
- features: ['Çeşitli Bulut Sağlayıcıları Tarafından Kendi Kendine Yönetilen Güvenilirlik', 'Tek Çalışma Alanı', 'Web Uygulaması Logo ve Marka Özelleştirme', 'Öncelikli E-posta ve Sohbet Desteği'],
+ features: ['Çeşitli Bulut Sağlayıcıları Tarafından Kendi Kendine Yönetilen Güvenilirlik', 'Tek Çalışma Alanı', 'Web Uygulama Logo ve Marka Özelleştirme', 'Öncelikli E-posta ve Sohbet Desteği'],
name: 'Premium',
includesTitle: 'Topluluktan her şey, artı:',
for: 'Orta Büyüklükteki Organizasyonlar ve Ekipler için',
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'Faturalama ve Abonelikler',
viewBillingDescription: 'Ödeme yöntemlerini, faturaları ve abonelik değişikliklerini yönetin',
viewBillingAction: 'Yönet',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Aynı anda birden fazla belge yüklemek için yükseltin',
+ description: 'Yükleme sınırına ulaştınız — mevcut planınızda aynı anda yalnızca bir belge seçip yükleyebilirsiniz.',
+ },
+ uploadMultipleFiles: {
+ title: 'Toplu belge yüklemeyi açmak için yükseltin',
+ description: 'Zaman kazanmak ve verimliliği artırmak için bir kerede daha fazla belgeyi toplu olarak yükleyin.',
+ },
+ addChunks: {
+ title: 'Parçalar eklemeye devam etmek için yükseltin',
+ description: 'Bu plan için parça ekleme sınırına ulaştınız.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/uk-UA/billing.ts b/web/i18n/uk-UA/billing.ts
index 5502afd608..76207fcec5 100644
--- a/web/i18n/uk-UA/billing.ts
+++ b/web/i18n/uk-UA/billing.ts
@@ -137,7 +137,7 @@ const translation = {
name: 'Ентерпрайз',
description: 'Отримайте повні можливості та підтримку для масштабних критично важливих систем.',
includesTitle: 'Все, що входить до плану Team, плюс:',
- features: ['Масштабовані рішення для розгортання корпоративного рівня', 'Авторизація комерційної ліцензії', 'Ексклюзивні корпоративні функції', 'Кілька робочих просторів та управління підприємством', 'ЄДИНА СИСТЕМА АВТОРИЗАЦІЇ', 'Узгоджені SLA партнерами Dify', 'Розширена безпека та контроль', 'Оновлення та обслуговування від Dify офіційно', 'Професійна технічна підтримка'],
+ features: ['Масштабовані рішення для розгортання корпоративного рівня', 'Авторизація комерційної ліцензії', 'Ексклюзивні корпоративні функції', 'Кілька робочих просторів та управління підприємством', 'ЄД', 'Узгоджені SLA партнерами Dify', 'Розширена безпека та контроль', 'Оновлення та обслуговування від Dify офіційно', 'Професійна технічна підтримка'],
btnText: 'Зв\'язатися з відділом продажу',
priceTip: 'Тільки річна оплата',
for: 'Для великих команд',
@@ -153,7 +153,7 @@ const translation = {
name: 'Спільнота',
},
premium: {
- features: ['Самокерована надійність від різних хмарних постачальників', 'Один робочий простір', 'Налаштування логотипу та бренду WebApp', 'Пріоритетна підтримка електронної пошти та чату'],
+ features: ['Самокерована надійність від різних хмарних постачальників', 'Один робочий простір', 'Налаштування логотипу та бренду веб-додатку', 'Пріоритетна підтримка електронної пошти та чату'],
description: 'Для середніх підприємств та команд',
btnText: 'Отримайте Преміум у',
price: 'Масштабований',
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'Білінг та підписки',
viewBillingDescription: 'Керуйте способами оплати, рахунками та змінами підписки',
viewBillingAction: 'Керувати',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Оновіть, щоб завантажувати кілька документів одночасно',
+ description: 'Ви досягли ліміту завантаження — на вашому поточному плані можна вибрати та завантажити лише один документ одночасно.',
+ },
+ uploadMultipleFiles: {
+ title: 'Оновіть, щоб розблокувати пакетне завантаження документів',
+ description: 'Завантажуйте кілька документів одночасно, щоб заощадити час і підвищити ефективність.',
+ },
+ addChunks: {
+ title: 'Оновіть, щоб продовжити додавати частини',
+ description: 'Ви досягли межі додавання фрагментів для цього плану.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/vi-VN/billing.ts b/web/i18n/vi-VN/billing.ts
index 04e8385347..405c35b703 100644
--- a/web/i18n/vi-VN/billing.ts
+++ b/web/i18n/vi-VN/billing.ts
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: 'Thanh toán và Đăng ký',
viewBillingDescription: 'Quản lý phương thức thanh toán, hóa đơn và thay đổi đăng ký',
viewBillingAction: 'Quản lý',
+ upgrade: {
+ uploadMultiplePages: {
+ title: 'Nâng cấp để tải lên nhiều tài liệu cùng lúc',
+ description: 'Bạn đã đạt đến giới hạn tải lên — chỉ có thể chọn và tải lên một tài liệu trong một lần với gói hiện tại của bạn.',
+ },
+ uploadMultipleFiles: {
+ title: 'Nâng cấp để mở khóa tải lên nhiều tài liệu',
+ description: 'Tải lên nhiều tài liệu cùng lúc để tiết kiệm thời gian và nâng cao hiệu quả.',
+ },
+ addChunks: {
+ title: 'Nâng cấp để tiếp tục thêm các phần',
+ description: 'Bạn đã đạt đến giới hạn thêm phần cho gói này.',
+ },
+ },
}
export default translation
diff --git a/web/i18n/zh-Hant/billing.ts b/web/i18n/zh-Hant/billing.ts
index 42534756a5..010a91a5e5 100644
--- a/web/i18n/zh-Hant/billing.ts
+++ b/web/i18n/zh-Hant/billing.ts
@@ -202,6 +202,20 @@ const translation = {
viewBillingTitle: '帳單與訂閱',
viewBillingDescription: '管理付款方式、發票和訂閱變更',
viewBillingAction: '管理',
+ upgrade: {
+ uploadMultiplePages: {
+ title: '升級以一次上傳多個文件',
+ description: '您已達到上傳限制 — 在您目前的方案下,每次只能選擇並上傳一個文件。',
+ },
+ uploadMultipleFiles: {
+ title: '升級以解鎖批量上傳文件功能',
+ description: '一次批量上傳更多文件,以節省時間並提高效率。',
+ },
+ addChunks: {
+ title: '升級以繼續添加區塊',
+ description: '您已達到此方案可新增區塊的上限。',
+ },
+ },
}
export default translation
diff --git a/web/package.json b/web/package.json
index f118dff25b..391324395d 100644
--- a/web/package.json
+++ b/web/package.json
@@ -112,9 +112,9 @@
"pinyin-pro": "^3.27.0",
"qrcode.react": "^4.2.0",
"qs": "^6.14.0",
- "react": "19.2.1",
+ "react": "19.2.3",
"react-18-input-autosize": "^3.0.0",
- "react-dom": "19.2.1",
+ "react-dom": "19.2.3",
"react-easy-crop": "^5.5.3",
"react-hook-form": "^7.65.0",
"react-hotkeys-hook": "^4.6.2",
diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml
index 0cf7524573..0aa388a2d1 100644
--- a/web/pnpm-lock.yaml
+++ b/web/pnpm-lock.yaml
@@ -69,19 +69,19 @@ importers:
version: 1.2.1
'@floating-ui/react':
specifier: ^0.26.28
- version: 0.26.28(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 0.26.28(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@formatjs/intl-localematcher':
specifier: ^0.5.10
version: 0.5.10
'@headlessui/react':
specifier: 2.2.1
- version: 2.2.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 2.2.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@heroicons/react':
specifier: ^2.2.0
- version: 2.2.0(react@19.2.1)
+ version: 2.2.0(react@19.2.3)
'@hookform/resolvers':
specifier: ^3.10.0
- version: 3.10.0(react-hook-form@7.68.0(react@19.2.1))
+ version: 3.10.0(react-hook-form@7.68.0(react@19.2.3))
'@lexical/code':
specifier: ^0.38.2
version: 0.38.2
@@ -93,7 +93,7 @@ importers:
version: 0.38.2
'@lexical/react':
specifier: ^0.38.2
- version: 0.38.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(yjs@13.6.27)
+ version: 0.38.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(yjs@13.6.27)
'@lexical/selection':
specifier: ^0.38.2
version: 0.38.2
@@ -105,7 +105,7 @@ importers:
version: 0.38.2
'@monaco-editor/react':
specifier: ^4.7.0
- version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@octokit/core':
specifier: ^6.1.6
version: 6.1.6
@@ -114,10 +114,10 @@ importers:
version: 6.1.8
'@remixicon/react':
specifier: ^4.7.0
- version: 4.7.0(react@19.2.1)
+ version: 4.7.0(react@19.2.3)
'@sentry/react':
specifier: ^8.55.0
- version: 8.55.0(react@19.2.1)
+ version: 8.55.0(react@19.2.3)
'@svgdotjs/svg.js':
specifier: ^3.2.5
version: 3.2.5
@@ -126,19 +126,19 @@ importers:
version: 0.5.19(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.2))
'@tanstack/react-form':
specifier: ^1.23.7
- version: 1.27.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 1.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@tanstack/react-query':
specifier: ^5.90.5
- version: 5.90.12(react@19.2.1)
+ version: 5.90.12(react@19.2.3)
'@tanstack/react-query-devtools':
specifier: ^5.90.2
- version: 5.91.1(@tanstack/react-query@5.90.12(react@19.2.1))(react@19.2.1)
+ version: 5.91.1(@tanstack/react-query@5.90.12(react@19.2.3))(react@19.2.3)
abcjs:
specifier: ^6.5.2
version: 6.5.2
ahooks:
specifier: ^3.9.5
- version: 3.9.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 3.9.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -147,7 +147,7 @@ importers:
version: 2.5.1
cmdk:
specifier: ^1.1.1
- version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
copy-to-clipboard:
specifier: ^3.3.3
version: 3.3.3
@@ -168,7 +168,7 @@ importers:
version: 5.6.0
echarts-for-react:
specifier: ^3.0.5
- version: 3.0.5(echarts@5.6.0)(react@19.2.1)
+ version: 3.0.5(echarts@5.6.0)(react@19.2.3)
elkjs:
specifier: ^0.9.3
version: 0.9.3
@@ -237,73 +237,73 @@ importers:
version: 1.0.0
next:
specifier: ~15.5.7
- version: 15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0)
+ version: 15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0)
next-pwa:
specifier: ^5.6.0
- version: 5.6.0(@babel/core@7.28.5)(@types/babel__core@7.20.5)(esbuild@0.25.0)(next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0))(uglify-js@3.19.3)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
+ version: 5.6.0(@babel/core@7.28.5)(@types/babel__core@7.20.5)(esbuild@0.25.0)(next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0))(uglify-js@3.19.3)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
next-themes:
specifier: ^0.4.6
- version: 0.4.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
pinyin-pro:
specifier: ^3.27.0
version: 3.27.0
qrcode.react:
specifier: ^4.2.0
- version: 4.2.0(react@19.2.1)
+ version: 4.2.0(react@19.2.3)
qs:
specifier: ^6.14.0
version: 6.14.0
react:
- specifier: 19.2.1
- version: 19.2.1
+ specifier: 19.2.3
+ version: 19.2.3
react-18-input-autosize:
specifier: ^3.0.0
- version: 3.0.0(react@19.2.1)
+ version: 3.0.0(react@19.2.3)
react-dom:
- specifier: 19.2.1
- version: 19.2.1(react@19.2.1)
+ specifier: 19.2.3
+ version: 19.2.3(react@19.2.3)
react-easy-crop:
specifier: ^5.5.3
- version: 5.5.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 5.5.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react-hook-form:
specifier: ^7.65.0
- version: 7.68.0(react@19.2.1)
+ version: 7.68.0(react@19.2.3)
react-hotkeys-hook:
specifier: ^4.6.2
- version: 4.6.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 4.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react-i18next:
specifier: ^15.7.4
- version: 15.7.4(i18next@23.16.8)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3)
+ version: 15.7.4(i18next@23.16.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)
react-markdown:
specifier: ^9.1.0
- version: 9.1.0(@types/react@19.2.7)(react@19.2.1)
+ version: 9.1.0(@types/react@19.2.7)(react@19.2.3)
react-multi-email:
specifier: ^1.0.25
- version: 1.0.25(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 1.0.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react-papaparse:
specifier: ^4.4.0
version: 4.4.0
react-pdf-highlighter:
specifier: 8.0.0-rc.0
- version: 8.0.0-rc.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 8.0.0-rc.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
react-slider:
specifier: ^2.0.6
- version: 2.0.6(react@19.2.1)
+ version: 2.0.6(react@19.2.3)
react-sortablejs:
specifier: ^6.1.4
- version: 6.1.4(@types/sortablejs@1.15.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sortablejs@1.15.6)
+ version: 6.1.4(@types/sortablejs@1.15.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sortablejs@1.15.6)
react-syntax-highlighter:
specifier: ^15.6.6
- version: 15.6.6(react@19.2.1)
+ version: 15.6.6(react@19.2.3)
react-textarea-autosize:
specifier: ^8.5.9
- version: 8.5.9(@types/react@19.2.7)(react@19.2.1)
+ version: 8.5.9(@types/react@19.2.7)(react@19.2.3)
react-window:
specifier: ^1.8.11
- version: 1.8.11(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 1.8.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
reactflow:
specifier: ^11.11.4
- version: 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
rehype-katex:
specifier: ^7.0.1
version: 7.0.1
@@ -333,7 +333,7 @@ importers:
version: 1.15.6
swr:
specifier: ^2.3.6
- version: 2.3.7(react@19.2.1)
+ version: 2.3.7(react@19.2.3)
tailwind-merge:
specifier: ^2.6.0
version: 2.6.0
@@ -342,7 +342,7 @@ importers:
version: 7.0.19
use-context-selector:
specifier: ^2.0.0
- version: 2.0.0(react@19.2.1)(scheduler@0.26.0)
+ version: 2.0.0(react@19.2.3)(scheduler@0.26.0)
uuid:
specifier: ^10.0.0
version: 10.0.0
@@ -351,10 +351,10 @@ importers:
version: 3.25.76
zundo:
specifier: ^2.3.0
- version: 2.3.0(zustand@5.0.9(@types/react@19.2.7)(immer@10.2.0)(react@19.2.1)(use-sync-external-store@1.6.0(react@19.2.1)))
+ version: 2.3.0(zustand@5.0.9(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)))
zustand:
specifier: ^5.0.9
- version: 5.0.9(@types/react@19.2.7)(immer@10.2.0)(react@19.2.1)(use-sync-external-store@1.6.0(react@19.2.1))
+ version: 5.0.9(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))
devDependencies:
'@antfu/eslint-config':
specifier: ^5.4.1
@@ -376,7 +376,7 @@ importers:
version: 3.1.1(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
'@mdx-js/react':
specifier: ^3.1.1
- version: 3.1.1(@types/react@19.2.7)(react@19.2.1)
+ version: 3.1.1(@types/react@19.2.7)(react@19.2.3)
'@next/bundle-analyzer':
specifier: 15.5.7
version: 15.5.7
@@ -385,7 +385,7 @@ importers:
version: 15.5.7
'@next/mdx':
specifier: 15.5.7
- version: 15.5.7(@mdx-js/loader@3.1.1(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3)))(@mdx-js/react@3.1.1(@types/react@19.2.7)(react@19.2.1))
+ version: 15.5.7(@mdx-js/loader@3.1.1(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3)))(@mdx-js/react@3.1.1(@types/react@19.2.7)(react@19.2.3))
'@rgrove/parse-xml':
specifier: ^4.2.0
version: 4.2.0
@@ -394,7 +394,7 @@ importers:
version: 9.1.13(@types/react@19.2.7)(storybook@9.1.13(@testing-library/dom@10.4.1))
'@storybook/addon-links':
specifier: 9.1.13
- version: 9.1.13(react@19.2.1)(storybook@9.1.13(@testing-library/dom@10.4.1))
+ version: 9.1.13(react@19.2.3)(storybook@9.1.13(@testing-library/dom@10.4.1))
'@storybook/addon-onboarding':
specifier: 9.1.13
version: 9.1.13(storybook@9.1.13(@testing-library/dom@10.4.1))
@@ -403,10 +403,10 @@ importers:
version: 9.1.13(storybook@9.1.13(@testing-library/dom@10.4.1))
'@storybook/nextjs':
specifier: 9.1.13
- version: 9.1.13(esbuild@0.25.0)(next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0)(storybook@9.1.13(@testing-library/dom@10.4.1))(type-fest@4.2.0)(typescript@5.9.3)(uglify-js@3.19.3)(webpack-hot-middleware@2.26.1)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
+ version: 9.1.13(esbuild@0.25.0)(next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0)(storybook@9.1.13(@testing-library/dom@10.4.1))(type-fest@4.2.0)(typescript@5.9.3)(uglify-js@3.19.3)(webpack-hot-middleware@2.26.1)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
'@storybook/react':
specifier: 9.1.13
- version: 9.1.13(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@9.1.13(@testing-library/dom@10.4.1))(typescript@5.9.3)
+ version: 9.1.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@9.1.13(@testing-library/dom@10.4.1))(typescript@5.9.3)
'@testing-library/dom':
specifier: ^10.4.1
version: 10.4.1
@@ -415,7 +415,7 @@ importers:
version: 6.9.1
'@testing-library/react':
specifier: ^16.3.0
- version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@testing-library/user-event':
specifier: ^14.6.1
version: 14.6.1(@testing-library/dom@10.4.1)
@@ -538,7 +538,7 @@ importers:
version: 8.5.6
react-scan:
specifier: ^0.4.3
- version: 0.4.3(@types/react@19.2.7)(next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(rollup@2.79.2)
+ version: 0.4.3(@types/react@19.2.7)(next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@2.79.2)
sass:
specifier: ^1.93.2
version: 1.95.0
@@ -7463,10 +7463,10 @@ packages:
resolution: {integrity: sha512-hlSJDQ2synMPKFZOsKo9Hi8WWZTC7POR8EmWvTSjow+VDgKzkmjQvFm2fk0tmRw+f0vTOIYKlarR0iL4996pdg==}
engines: {node: '>=16.14.0'}
- react-dom@19.2.1:
- resolution: {integrity: sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==}
+ react-dom@19.2.3:
+ resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==}
peerDependencies:
- react: ^19.2.1
+ react: ^19.2.3
react-draggable@4.4.6:
resolution: {integrity: sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==}
@@ -7638,8 +7638,8 @@ packages:
react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
- react@19.2.1:
- resolution: {integrity: sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==}
+ react@19.2.3:
+ resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==}
engines: {node: '>=0.10.0'}
reactflow@11.11.4:
@@ -10447,26 +10447,26 @@ snapshots:
'@floating-ui/core': 1.7.3
'@floating-ui/utils': 0.2.10
- '@floating-ui/react-dom@2.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@floating-ui/react-dom@2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@floating-ui/dom': 1.7.4
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
- '@floating-ui/react@0.26.28(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@floating-ui/react@0.26.28(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@floating-ui/react-dom': 2.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@floating-ui/react-dom': 2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@floating-ui/utils': 0.2.10
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
tabbable: 6.3.0
- '@floating-ui/react@0.27.16(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@floating-ui/react@0.27.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@floating-ui/react-dom': 2.1.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@floating-ui/react-dom': 2.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@floating-ui/utils': 0.2.10
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
tabbable: 6.3.0
'@floating-ui/utils@0.2.10': {}
@@ -10484,22 +10484,22 @@ snapshots:
jest-mock: 29.7.0
jest-util: 29.7.0
- '@headlessui/react@2.2.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@headlessui/react@2.2.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@floating-ui/react': 0.26.28(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@react-aria/focus': 3.21.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@react-aria/interactions': 3.25.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@tanstack/react-virtual': 3.13.13(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ '@floating-ui/react': 0.26.28(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@react-aria/focus': 3.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@react-aria/interactions': 3.25.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@tanstack/react-virtual': 3.13.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
- '@heroicons/react@2.2.0(react@19.2.1)':
+ '@heroicons/react@2.2.0(react@19.2.3)':
dependencies:
- react: 19.2.1
+ react: 19.2.3
- '@hookform/resolvers@3.10.0(react-hook-form@7.68.0(react@19.2.1))':
+ '@hookform/resolvers@3.10.0(react-hook-form@7.68.0(react@19.2.3))':
dependencies:
- react-hook-form: 7.68.0(react@19.2.1)
+ react-hook-form: 7.68.0(react@19.2.3)
'@humanfs/core@0.19.1': {}
@@ -10913,7 +10913,7 @@ snapshots:
lexical: 0.38.2
prismjs: 1.30.0
- '@lexical/devtools-core@0.38.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@lexical/devtools-core@0.38.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@lexical/html': 0.38.2
'@lexical/link': 0.38.2
@@ -10921,8 +10921,8 @@ snapshots:
'@lexical/table': 0.38.2
'@lexical/utils': 0.38.2
lexical: 0.38.2
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
'@lexical/dragon@0.38.2':
dependencies:
@@ -10997,10 +10997,10 @@ snapshots:
'@lexical/utils': 0.38.2
lexical: 0.38.2
- '@lexical/react@0.38.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(yjs@13.6.27)':
+ '@lexical/react@0.38.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(yjs@13.6.27)':
dependencies:
- '@floating-ui/react': 0.27.16(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@lexical/devtools-core': 0.38.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@floating-ui/react': 0.27.16(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@lexical/devtools-core': 0.38.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@lexical/dragon': 0.38.2
'@lexical/extension': 0.38.2
'@lexical/hashtag': 0.38.2
@@ -11017,9 +11017,9 @@ snapshots:
'@lexical/utils': 0.38.2
'@lexical/yjs': 0.38.2(yjs@13.6.27)
lexical: 0.38.2
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- react-error-boundary: 6.0.0(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ react-error-boundary: 6.0.0(react@19.2.3)
transitivePeerDependencies:
- yjs
@@ -11099,11 +11099,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@mdx-js/react@3.1.1(@types/react@19.2.7)(react@19.2.1)':
+ '@mdx-js/react@3.1.1(@types/react@19.2.7)(react@19.2.3)':
dependencies:
'@types/mdx': 2.0.13
'@types/react': 19.2.7
- react: 19.2.1
+ react: 19.2.3
'@mermaid-js/parser@0.6.3':
dependencies:
@@ -11113,12 +11113,12 @@ snapshots:
dependencies:
state-local: 1.0.7
- '@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@monaco-editor/loader': 1.5.0
monaco-editor: 0.55.1
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
'@mswjs/interceptors@0.39.8':
dependencies:
@@ -11151,12 +11151,12 @@ snapshots:
dependencies:
fast-glob: 3.3.1
- '@next/mdx@15.5.7(@mdx-js/loader@3.1.1(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3)))(@mdx-js/react@3.1.1(@types/react@19.2.7)(react@19.2.1))':
+ '@next/mdx@15.5.7(@mdx-js/loader@3.1.1(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3)))(@mdx-js/react@3.1.1(@types/react@19.2.7)(react@19.2.3))':
dependencies:
source-map: 0.7.6
optionalDependencies:
'@mdx-js/loader': 3.1.1(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
- '@mdx-js/react': 3.1.1(@types/react@19.2.7)(react@19.2.1)
+ '@mdx-js/react': 3.1.1(@types/react@19.2.7)(react@19.2.3)
'@next/swc-darwin-arm64@15.5.7':
optional: true
@@ -11441,10 +11441,10 @@ snapshots:
'@parcel/watcher-win32-x64': 2.5.1
optional: true
- '@pivanov/utils@0.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@pivanov/utils@0.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
'@pkgr/core@0.2.9': {}
@@ -11479,235 +11479,235 @@ snapshots:
'@radix-ui/primitive@1.1.3': {}
- '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.7)(react@19.2.1)':
+ '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.7)(react@19.2.3)':
dependencies:
- react: 19.2.1
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- '@radix-ui/react-context@1.1.2(@types/react@19.2.7)(react@19.2.1)':
+ '@radix-ui/react-context@1.1.2(@types/react@19.2.7)(react@19.2.3)':
dependencies:
- react: 19.2.1
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@radix-ui/primitive': 1.1.3
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1)
- '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.1)
- '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.7)(react@19.2.1)
- '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@radix-ui/react-id': 1.1.1(@types/react@19.2.7)(react@19.2.1)
- '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.1)
- '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@19.2.1)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.7)(react@19.2.3)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.7)(react@19.2.3)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.7)(react@19.2.3)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.3)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.7)(react@19.2.3)
aria-hidden: 1.2.6
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- react-remove-scroll: 2.7.2(@types/react@19.2.7)(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ react-remove-scroll: 2.7.2(@types/react@19.2.7)(react@19.2.3)
optionalDependencies:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
- '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@radix-ui/primitive': 1.1.3
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.1)
- '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.7)(react@19.2.1)
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.3)
+ '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.7)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
optionalDependencies:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
- '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.7)(react@19.2.1)':
+ '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.7)(react@19.2.3)':
dependencies:
- react: 19.2.1
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1)
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.1)
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
optionalDependencies:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
- '@radix-ui/react-id@1.1.1(@types/react@19.2.7)(react@19.2.1)':
+ '@radix-ui/react-id@1.1.1(@types/react@19.2.7)(react@19.2.3)':
dependencies:
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.1)
- react: 19.2.1
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3)
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.1)
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
optionalDependencies:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
- '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1)
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.1)
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
optionalDependencies:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
- '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.1)
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.7)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
optionalDependencies:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
- '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@radix-ui/react-slot': 1.2.4(@types/react@19.2.7)(react@19.2.1)
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ '@radix-ui/react-slot': 1.2.4(@types/react@19.2.7)(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
optionalDependencies:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
- '@radix-ui/react-slot@1.2.3(@types/react@19.2.7)(react@19.2.1)':
+ '@radix-ui/react-slot@1.2.3(@types/react@19.2.7)(react@19.2.3)':
dependencies:
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1)
- react: 19.2.1
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3)
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- '@radix-ui/react-slot@1.2.4(@types/react@19.2.7)(react@19.2.1)':
+ '@radix-ui/react-slot@1.2.4(@types/react@19.2.7)(react@19.2.3)':
dependencies:
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1)
- react: 19.2.1
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3)
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.7)(react@19.2.1)':
+ '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.7)(react@19.2.3)':
dependencies:
- react: 19.2.1
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.7)(react@19.2.1)':
+ '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.7)(react@19.2.3)':
dependencies:
- '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.7)(react@19.2.1)
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.1)
- react: 19.2.1
+ '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.7)(react@19.2.3)
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3)
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.7)(react@19.2.1)':
+ '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.7)(react@19.2.3)':
dependencies:
- '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.1)
- react: 19.2.1
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.7)(react@19.2.3)
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.7)(react@19.2.1)':
+ '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.7)(react@19.2.3)':
dependencies:
- '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.1)
- react: 19.2.1
+ '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.7)(react@19.2.3)
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.7)(react@19.2.1)':
+ '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.7)(react@19.2.3)':
dependencies:
- react: 19.2.1
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- '@react-aria/focus@3.21.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@react-aria/focus@3.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@react-aria/interactions': 3.25.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@react-aria/utils': 3.31.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@react-types/shared': 3.32.1(react@19.2.1)
+ '@react-aria/interactions': 3.25.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@react-aria/utils': 3.31.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@react-types/shared': 3.32.1(react@19.2.3)
'@swc/helpers': 0.5.17
clsx: 2.1.1
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
- '@react-aria/interactions@3.25.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@react-aria/interactions@3.25.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@react-aria/ssr': 3.9.10(react@19.2.1)
- '@react-aria/utils': 3.31.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@react-aria/ssr': 3.9.10(react@19.2.3)
+ '@react-aria/utils': 3.31.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@react-stately/flags': 3.1.2
- '@react-types/shared': 3.32.1(react@19.2.1)
+ '@react-types/shared': 3.32.1(react@19.2.3)
'@swc/helpers': 0.5.17
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
- '@react-aria/ssr@3.9.10(react@19.2.1)':
+ '@react-aria/ssr@3.9.10(react@19.2.3)':
dependencies:
'@swc/helpers': 0.5.17
- react: 19.2.1
+ react: 19.2.3
- '@react-aria/utils@3.31.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@react-aria/utils@3.31.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@react-aria/ssr': 3.9.10(react@19.2.1)
+ '@react-aria/ssr': 3.9.10(react@19.2.3)
'@react-stately/flags': 3.1.2
- '@react-stately/utils': 3.10.8(react@19.2.1)
- '@react-types/shared': 3.32.1(react@19.2.1)
+ '@react-stately/utils': 3.10.8(react@19.2.3)
+ '@react-types/shared': 3.32.1(react@19.2.3)
'@swc/helpers': 0.5.17
clsx: 2.1.1
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
'@react-stately/flags@3.1.2':
dependencies:
'@swc/helpers': 0.5.17
- '@react-stately/utils@3.10.8(react@19.2.1)':
+ '@react-stately/utils@3.10.8(react@19.2.3)':
dependencies:
'@swc/helpers': 0.5.17
- react: 19.2.1
+ react: 19.2.3
- '@react-types/shared@3.32.1(react@19.2.1)':
+ '@react-types/shared@3.32.1(react@19.2.3)':
dependencies:
- react: 19.2.1
+ react: 19.2.3
- '@reactflow/background@11.3.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@reactflow/background@11.3.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@reactflow/core': 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@reactflow/core': 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
classcat: 5.0.5
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- zustand: 4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ zustand: 4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3)
transitivePeerDependencies:
- '@types/react'
- immer
- '@reactflow/controls@11.2.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@reactflow/controls@11.2.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@reactflow/core': 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@reactflow/core': 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
classcat: 5.0.5
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- zustand: 4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ zustand: 4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3)
transitivePeerDependencies:
- '@types/react'
- immer
- '@reactflow/core@11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@reactflow/core@11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@types/d3': 7.4.3
'@types/d3-drag': 3.0.7
@@ -11717,55 +11717,55 @@ snapshots:
d3-drag: 3.0.0
d3-selection: 3.0.0
d3-zoom: 3.0.0
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- zustand: 4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ zustand: 4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3)
transitivePeerDependencies:
- '@types/react'
- immer
- '@reactflow/minimap@11.7.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@reactflow/minimap@11.7.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@reactflow/core': 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@reactflow/core': 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@types/d3-selection': 3.0.11
'@types/d3-zoom': 3.0.8
classcat: 5.0.5
d3-selection: 3.0.0
d3-zoom: 3.0.0
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- zustand: 4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ zustand: 4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3)
transitivePeerDependencies:
- '@types/react'
- immer
- '@reactflow/node-resizer@2.2.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@reactflow/node-resizer@2.2.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@reactflow/core': 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@reactflow/core': 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
classcat: 5.0.5
d3-drag: 3.0.0
d3-selection: 3.0.0
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- zustand: 4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ zustand: 4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3)
transitivePeerDependencies:
- '@types/react'
- immer
- '@reactflow/node-toolbar@1.3.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@reactflow/node-toolbar@1.3.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- '@reactflow/core': 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@reactflow/core': 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
classcat: 5.0.5
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- zustand: 4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ zustand: 4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3)
transitivePeerDependencies:
- '@types/react'
- immer
- '@remixicon/react@4.7.0(react@19.2.1)':
+ '@remixicon/react@4.7.0(react@19.2.3)':
dependencies:
- react: 19.2.1
+ react: 19.2.3
'@rgrove/parse-xml@4.2.0': {}
@@ -11846,12 +11846,12 @@ snapshots:
'@sentry/core@8.55.0': {}
- '@sentry/react@8.55.0(react@19.2.1)':
+ '@sentry/react@8.55.0(react@19.2.3)':
dependencies:
'@sentry/browser': 8.55.0
'@sentry/core': 8.55.0
hoist-non-react-statics: 3.3.2
- react: 19.2.1
+ react: 19.2.3
'@sinclair/typebox@0.27.8': {}
@@ -11867,23 +11867,23 @@ snapshots:
'@storybook/addon-docs@9.1.13(@types/react@19.2.7)(storybook@9.1.13(@testing-library/dom@10.4.1))':
dependencies:
- '@mdx-js/react': 3.1.1(@types/react@19.2.7)(react@19.2.1)
+ '@mdx-js/react': 3.1.1(@types/react@19.2.7)(react@19.2.3)
'@storybook/csf-plugin': 9.1.13(storybook@9.1.13(@testing-library/dom@10.4.1))
- '@storybook/icons': 1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@storybook/react-dom-shim': 9.1.13(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@9.1.13(@testing-library/dom@10.4.1))
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ '@storybook/icons': 1.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@storybook/react-dom-shim': 9.1.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@9.1.13(@testing-library/dom@10.4.1))
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
storybook: 9.1.13(@testing-library/dom@10.4.1)
ts-dedent: 2.2.0
transitivePeerDependencies:
- '@types/react'
- '@storybook/addon-links@9.1.13(react@19.2.1)(storybook@9.1.13(@testing-library/dom@10.4.1))':
+ '@storybook/addon-links@9.1.13(react@19.2.3)(storybook@9.1.13(@testing-library/dom@10.4.1))':
dependencies:
'@storybook/global': 5.0.0
storybook: 9.1.13(@testing-library/dom@10.4.1)
optionalDependencies:
- react: 19.2.1
+ react: 19.2.3
'@storybook/addon-onboarding@9.1.13(storybook@9.1.13(@testing-library/dom@10.4.1))':
dependencies:
@@ -11933,12 +11933,12 @@ snapshots:
'@storybook/global@5.0.0': {}
- '@storybook/icons@1.6.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@storybook/icons@1.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
- '@storybook/nextjs@9.1.13(esbuild@0.25.0)(next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0)(storybook@9.1.13(@testing-library/dom@10.4.1))(type-fest@4.2.0)(typescript@5.9.3)(uglify-js@3.19.3)(webpack-hot-middleware@2.26.1)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))':
+ '@storybook/nextjs@9.1.13(esbuild@0.25.0)(next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0)(storybook@9.1.13(@testing-library/dom@10.4.1))(type-fest@4.2.0)(typescript@5.9.3)(uglify-js@3.19.3)(webpack-hot-middleware@2.26.1)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))':
dependencies:
'@babel/core': 7.28.5
'@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.5)
@@ -11955,26 +11955,26 @@ snapshots:
'@babel/runtime': 7.28.4
'@pmmmwh/react-refresh-webpack-plugin': 0.5.17(react-refresh@0.14.2)(type-fest@4.2.0)(webpack-hot-middleware@2.26.1)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
'@storybook/builder-webpack5': 9.1.13(esbuild@0.25.0)(storybook@9.1.13(@testing-library/dom@10.4.1))(typescript@5.9.3)(uglify-js@3.19.3)
- '@storybook/preset-react-webpack': 9.1.13(esbuild@0.25.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@9.1.13(@testing-library/dom@10.4.1))(typescript@5.9.3)(uglify-js@3.19.3)
- '@storybook/react': 9.1.13(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@9.1.13(@testing-library/dom@10.4.1))(typescript@5.9.3)
+ '@storybook/preset-react-webpack': 9.1.13(esbuild@0.25.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@9.1.13(@testing-library/dom@10.4.1))(typescript@5.9.3)(uglify-js@3.19.3)
+ '@storybook/react': 9.1.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@9.1.13(@testing-library/dom@10.4.1))(typescript@5.9.3)
'@types/semver': 7.7.1
babel-loader: 9.2.1(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
css-loader: 6.11.0(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
image-size: 2.0.2
loader-utils: 3.3.1
- next: 15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0)
+ next: 15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0)
node-polyfill-webpack-plugin: 2.0.1(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
postcss: 8.5.6
postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
react-refresh: 0.14.2
resolve-url-loader: 5.0.0
sass-loader: 16.0.6(sass@1.95.0)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
semver: 7.7.3
storybook: 9.1.13(@testing-library/dom@10.4.1)
style-loader: 3.3.4(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
- styled-jsx: 5.1.7(@babel/core@7.28.5)(react@19.2.1)
+ styled-jsx: 5.1.7(@babel/core@7.28.5)(react@19.2.3)
tsconfig-paths: 4.2.0
tsconfig-paths-webpack-plugin: 4.2.0
optionalDependencies:
@@ -11998,16 +11998,16 @@ snapshots:
- webpack-hot-middleware
- webpack-plugin-serve
- '@storybook/preset-react-webpack@9.1.13(esbuild@0.25.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@9.1.13(@testing-library/dom@10.4.1))(typescript@5.9.3)(uglify-js@3.19.3)':
+ '@storybook/preset-react-webpack@9.1.13(esbuild@0.25.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@9.1.13(@testing-library/dom@10.4.1))(typescript@5.9.3)(uglify-js@3.19.3)':
dependencies:
'@storybook/core-webpack': 9.1.13(storybook@9.1.13(@testing-library/dom@10.4.1))
'@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.9.3)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
'@types/semver': 7.7.1
find-up: 7.0.0
magic-string: 0.30.21
- react: 19.2.1
+ react: 19.2.3
react-docgen: 7.1.1
- react-dom: 19.2.1(react@19.2.1)
+ react-dom: 19.2.3(react@19.2.3)
resolve: 1.22.11
semver: 7.7.3
storybook: 9.1.13(@testing-library/dom@10.4.1)
@@ -12036,18 +12036,18 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@storybook/react-dom-shim@9.1.13(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@9.1.13(@testing-library/dom@10.4.1))':
+ '@storybook/react-dom-shim@9.1.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@9.1.13(@testing-library/dom@10.4.1))':
dependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
storybook: 9.1.13(@testing-library/dom@10.4.1)
- '@storybook/react@9.1.13(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@9.1.13(@testing-library/dom@10.4.1))(typescript@5.9.3)':
+ '@storybook/react@9.1.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@9.1.13(@testing-library/dom@10.4.1))(typescript@5.9.3)':
dependencies:
'@storybook/global': 5.0.0
- '@storybook/react-dom-shim': 9.1.13(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(storybook@9.1.13(@testing-library/dom@10.4.1))
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ '@storybook/react-dom-shim': 9.1.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(storybook@9.1.13(@testing-library/dom@10.4.1))
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
storybook: 9.1.13(@testing-library/dom@10.4.1)
optionalDependencies:
typescript: 5.9.3
@@ -12105,37 +12105,37 @@ snapshots:
'@tanstack/query-devtools@5.91.1': {}
- '@tanstack/react-form@1.27.1(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@tanstack/react-form@1.27.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@tanstack/form-core': 1.27.1
- '@tanstack/react-store': 0.8.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- react: 19.2.1
+ '@tanstack/react-store': 0.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ react: 19.2.3
transitivePeerDependencies:
- react-dom
- '@tanstack/react-query-devtools@5.91.1(@tanstack/react-query@5.90.12(react@19.2.1))(react@19.2.1)':
+ '@tanstack/react-query-devtools@5.91.1(@tanstack/react-query@5.90.12(react@19.2.3))(react@19.2.3)':
dependencies:
'@tanstack/query-devtools': 5.91.1
- '@tanstack/react-query': 5.90.12(react@19.2.1)
- react: 19.2.1
+ '@tanstack/react-query': 5.90.12(react@19.2.3)
+ react: 19.2.3
- '@tanstack/react-query@5.90.12(react@19.2.1)':
+ '@tanstack/react-query@5.90.12(react@19.2.3)':
dependencies:
'@tanstack/query-core': 5.90.12
- react: 19.2.1
+ react: 19.2.3
- '@tanstack/react-store@0.8.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@tanstack/react-store@0.8.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@tanstack/store': 0.8.0
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- use-sync-external-store: 1.6.0(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ use-sync-external-store: 1.6.0(react@19.2.3)
- '@tanstack/react-virtual@3.13.13(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@tanstack/react-virtual@3.13.13(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@tanstack/virtual-core': 3.13.13
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
'@tanstack/store@0.7.7': {}
@@ -12163,12 +12163,12 @@ snapshots:
picocolors: 1.1.1
redent: 3.0.0
- '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)':
+ '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
dependencies:
'@babel/runtime': 7.28.4
'@testing-library/dom': 10.4.1
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
optionalDependencies:
'@types/react': 19.2.7
'@types/react-dom': 19.2.3(@types/react@19.2.7)
@@ -12813,7 +12813,7 @@ snapshots:
loader-utils: 2.0.4
regex-parser: 2.3.1
- ahooks@3.9.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ ahooks@3.9.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
'@babel/runtime': 7.28.4
'@types/js-cookie': 3.0.6
@@ -12821,8 +12821,8 @@ snapshots:
intersection-observer: 0.12.2
js-cookie: 3.0.5
lodash: 4.17.21
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
react-fast-compare: 3.2.2
resize-observer-polyfill: 1.5.1
screenfull: 5.2.0
@@ -13071,10 +13071,10 @@ snapshots:
dependencies:
got: 11.8.6
- bippy@0.3.34(@types/react@19.2.7)(react@19.2.1):
+ bippy@0.3.34(@types/react@19.2.7)(react@19.2.3):
dependencies:
'@types/react-reconciler': 0.28.9(@types/react@19.2.7)
- react: 19.2.1
+ react: 19.2.3
transitivePeerDependencies:
- '@types/react'
@@ -13358,14 +13358,14 @@ snapshots:
clsx@2.1.1: {}
- cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
- '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.1)
- '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@radix-ui/react-id': 1.1.1(@types/react@19.2.7)(react@19.2.1)
- '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.7)(react@19.2.3)
+ '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.7)(react@19.2.3)
+ '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
transitivePeerDependencies:
- '@types/react'
- '@types/react-dom'
@@ -13908,11 +13908,11 @@ snapshots:
duplexer@0.1.2: {}
- echarts-for-react@3.0.5(echarts@5.6.0)(react@19.2.1):
+ echarts-for-react@3.0.5(echarts@5.6.0)(react@19.2.3):
dependencies:
echarts: 5.6.0
fast-deep-equal: 3.1.3
- react: 19.2.1
+ react: 19.2.3
size-sensor: 1.0.2
echarts@5.6.0:
@@ -16527,12 +16527,12 @@ snapshots:
neo-async@2.6.2: {}
- next-pwa@5.6.0(@babel/core@7.28.5)(@types/babel__core@7.20.5)(esbuild@0.25.0)(next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0))(uglify-js@3.19.3)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3)):
+ next-pwa@5.6.0(@babel/core@7.28.5)(@types/babel__core@7.20.5)(esbuild@0.25.0)(next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0))(uglify-js@3.19.3)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3)):
dependencies:
babel-loader: 8.4.1(@babel/core@7.28.5)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
clean-webpack-plugin: 4.0.0(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
globby: 11.1.0
- next: 15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0)
+ next: 15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0)
terser-webpack-plugin: 5.3.15(esbuild@0.25.0)(uglify-js@3.19.3)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
workbox-webpack-plugin: 6.6.0(@types/babel__core@7.20.5)(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3))
workbox-window: 6.6.0
@@ -16545,20 +16545,20 @@ snapshots:
- uglify-js
- webpack
- next-themes@0.4.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ next-themes@0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
- next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0):
+ next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0):
dependencies:
'@next/env': 15.5.7
'@swc/helpers': 0.5.15
caniuse-lite: 1.0.30001760
postcss: 8.4.31
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.3)
optionalDependencies:
'@next/swc-darwin-arm64': 15.5.7
'@next/swc-darwin-x64': 15.5.7
@@ -17108,9 +17108,9 @@ snapshots:
pure-rand@6.1.0: {}
- qrcode.react@4.2.0(react@19.2.1):
+ qrcode.react@4.2.0(react@19.2.3):
dependencies:
- react: 19.2.1
+ react: 19.2.3
qs@6.14.0:
dependencies:
@@ -17143,15 +17143,15 @@ snapshots:
strip-json-comments: 2.0.1
optional: true
- re-resizable@6.11.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ re-resizable@6.11.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
- react-18-input-autosize@3.0.0(react@19.2.1):
+ react-18-input-autosize@3.0.0(react@19.2.3):
dependencies:
prop-types: 15.8.1
- react: 19.2.1
+ react: 19.2.3
react-docgen-typescript@2.4.0(typescript@5.9.3):
dependencies:
@@ -17172,49 +17172,49 @@ snapshots:
transitivePeerDependencies:
- supports-color
- react-dom@19.2.1(react@19.2.1):
+ react-dom@19.2.3(react@19.2.3):
dependencies:
- react: 19.2.1
+ react: 19.2.3
scheduler: 0.27.0
- react-draggable@4.4.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ react-draggable@4.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
clsx: 1.2.1
prop-types: 15.8.1
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
- react-easy-crop@5.5.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ react-easy-crop@5.5.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
normalize-wheel: 1.0.1
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
tslib: 2.8.1
- react-error-boundary@6.0.0(react@19.2.1):
+ react-error-boundary@6.0.0(react@19.2.3):
dependencies:
'@babel/runtime': 7.28.4
- react: 19.2.1
+ react: 19.2.3
react-fast-compare@3.2.2: {}
- react-hook-form@7.68.0(react@19.2.1):
+ react-hook-form@7.68.0(react@19.2.3):
dependencies:
- react: 19.2.1
+ react: 19.2.3
- react-hotkeys-hook@4.6.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ react-hotkeys-hook@4.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
- react-i18next@15.7.4(i18next@23.16.8)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.3):
+ react-i18next@15.7.4(i18next@23.16.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3):
dependencies:
'@babel/runtime': 7.28.4
html-parse-stringify: 3.0.1
i18next: 23.16.8
- react: 19.2.1
+ react: 19.2.3
optionalDependencies:
- react-dom: 19.2.1(react@19.2.1)
+ react-dom: 19.2.3(react@19.2.3)
typescript: 5.9.3
react-is@16.13.1: {}
@@ -17223,7 +17223,7 @@ snapshots:
react-is@18.3.1: {}
- react-markdown@9.1.0(@types/react@19.2.7)(react@19.2.1):
+ react-markdown@9.1.0(@types/react@19.2.7)(react@19.2.3):
dependencies:
'@types/hast': 3.0.4
'@types/mdast': 4.0.4
@@ -17232,7 +17232,7 @@ snapshots:
hast-util-to-jsx-runtime: 2.3.6
html-url-attributes: 3.0.1
mdast-util-to-hast: 13.2.1
- react: 19.2.1
+ react: 19.2.3
remark-parse: 11.0.0
remark-rehype: 11.1.2
unified: 11.0.5
@@ -17241,142 +17241,142 @@ snapshots:
transitivePeerDependencies:
- supports-color
- react-multi-email@1.0.25(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ react-multi-email@1.0.25(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
react-papaparse@4.4.0:
dependencies:
'@types/papaparse': 5.5.1
papaparse: 5.5.3
- react-pdf-highlighter@8.0.0-rc.0(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ react-pdf-highlighter@8.0.0-rc.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
pdfjs-dist: 4.4.168
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- react-rnd: 10.5.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ react-rnd: 10.5.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
ts-debounce: 4.0.0
react-refresh@0.14.2: {}
- react-remove-scroll-bar@2.3.8(@types/react@19.2.7)(react@19.2.1):
+ react-remove-scroll-bar@2.3.8(@types/react@19.2.7)(react@19.2.3):
dependencies:
- react: 19.2.1
- react-style-singleton: 2.2.3(@types/react@19.2.7)(react@19.2.1)
+ react: 19.2.3
+ react-style-singleton: 2.2.3(@types/react@19.2.7)(react@19.2.3)
tslib: 2.8.1
optionalDependencies:
'@types/react': 19.2.7
- react-remove-scroll@2.7.2(@types/react@19.2.7)(react@19.2.1):
+ react-remove-scroll@2.7.2(@types/react@19.2.7)(react@19.2.3):
dependencies:
- react: 19.2.1
- react-remove-scroll-bar: 2.3.8(@types/react@19.2.7)(react@19.2.1)
- react-style-singleton: 2.2.3(@types/react@19.2.7)(react@19.2.1)
+ react: 19.2.3
+ react-remove-scroll-bar: 2.3.8(@types/react@19.2.7)(react@19.2.3)
+ react-style-singleton: 2.2.3(@types/react@19.2.7)(react@19.2.3)
tslib: 2.8.1
- use-callback-ref: 1.3.3(@types/react@19.2.7)(react@19.2.1)
- use-sidecar: 1.1.3(@types/react@19.2.7)(react@19.2.1)
+ use-callback-ref: 1.3.3(@types/react@19.2.7)(react@19.2.3)
+ use-sidecar: 1.1.3(@types/react@19.2.7)(react@19.2.3)
optionalDependencies:
'@types/react': 19.2.7
- react-rnd@10.5.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ react-rnd@10.5.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
- re-resizable: 6.11.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
- react-draggable: 4.4.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ re-resizable: 6.11.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+ react-draggable: 4.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
tslib: 2.6.2
- react-scan@0.4.3(@types/react@19.2.7)(next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0))(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(rollup@2.79.2):
+ react-scan@0.4.3(@types/react@19.2.7)(next@15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(rollup@2.79.2):
dependencies:
'@babel/core': 7.28.5
'@babel/generator': 7.28.5
'@babel/types': 7.28.5
'@clack/core': 0.3.5
'@clack/prompts': 0.8.2
- '@pivanov/utils': 0.0.2(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
+ '@pivanov/utils': 0.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
'@preact/signals': 1.3.2(preact@10.28.0)
'@rollup/pluginutils': 5.3.0(rollup@2.79.2)
'@types/node': 20.19.26
- bippy: 0.3.34(@types/react@19.2.7)(react@19.2.1)
+ bippy: 0.3.34(@types/react@19.2.7)(react@19.2.3)
esbuild: 0.25.12
estree-walker: 3.0.3
kleur: 4.1.5
mri: 1.2.0
playwright: 1.57.0
preact: 10.28.0
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
tsx: 4.21.0
optionalDependencies:
- next: 15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sass@1.95.0)
+ next: 15.5.7(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.95.0)
unplugin: 2.1.0
transitivePeerDependencies:
- '@types/react'
- rollup
- supports-color
- react-slider@2.0.6(react@19.2.1):
+ react-slider@2.0.6(react@19.2.3):
dependencies:
prop-types: 15.8.1
- react: 19.2.1
+ react: 19.2.3
- react-sortablejs@6.1.4(@types/sortablejs@1.15.9)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(sortablejs@1.15.6):
+ react-sortablejs@6.1.4(@types/sortablejs@1.15.9)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sortablejs@1.15.6):
dependencies:
'@types/sortablejs': 1.15.9
classnames: 2.3.1
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
sortablejs: 1.15.6
tiny-invariant: 1.2.0
- react-style-singleton@2.2.3(@types/react@19.2.7)(react@19.2.1):
+ react-style-singleton@2.2.3(@types/react@19.2.7)(react@19.2.3):
dependencies:
get-nonce: 1.0.1
- react: 19.2.1
+ react: 19.2.3
tslib: 2.8.1
optionalDependencies:
'@types/react': 19.2.7
- react-syntax-highlighter@15.6.6(react@19.2.1):
+ react-syntax-highlighter@15.6.6(react@19.2.3):
dependencies:
'@babel/runtime': 7.28.4
highlight.js: 10.7.3
highlightjs-vue: 1.0.0
lowlight: 1.20.0
prismjs: 1.30.0
- react: 19.2.1
+ react: 19.2.3
refractor: 3.6.0
- react-textarea-autosize@8.5.9(@types/react@19.2.7)(react@19.2.1):
+ react-textarea-autosize@8.5.9(@types/react@19.2.7)(react@19.2.3):
dependencies:
'@babel/runtime': 7.28.4
- react: 19.2.1
- use-composed-ref: 1.4.0(@types/react@19.2.7)(react@19.2.1)
- use-latest: 1.3.0(@types/react@19.2.7)(react@19.2.1)
+ react: 19.2.3
+ use-composed-ref: 1.4.0(@types/react@19.2.7)(react@19.2.3)
+ use-latest: 1.3.0(@types/react@19.2.7)(react@19.2.3)
transitivePeerDependencies:
- '@types/react'
- react-window@1.8.11(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ react-window@1.8.11(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
'@babel/runtime': 7.28.4
memoize-one: 5.2.1
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
- react@19.2.1: {}
+ react@19.2.3: {}
- reactflow@11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
+ reactflow@11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
- '@reactflow/background': 11.3.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@reactflow/controls': 11.2.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@reactflow/core': 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@reactflow/minimap': 11.7.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@reactflow/node-resizer': 2.2.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- '@reactflow/node-toolbar': 1.3.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
- react: 19.2.1
- react-dom: 19.2.1(react@19.2.1)
+ '@reactflow/background': 11.3.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@reactflow/controls': 11.2.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@reactflow/core': 11.11.4(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@reactflow/minimap': 11.7.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@reactflow/node-resizer': 2.2.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ '@reactflow/node-toolbar': 1.3.14(@types/react@19.2.7)(immer@10.2.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
transitivePeerDependencies:
- '@types/react'
- immer
@@ -18025,17 +18025,17 @@ snapshots:
dependencies:
inline-style-parser: 0.2.7
- styled-jsx@5.1.6(@babel/core@7.28.5)(react@19.2.1):
+ styled-jsx@5.1.6(@babel/core@7.28.5)(react@19.2.3):
dependencies:
client-only: 0.0.1
- react: 19.2.1
+ react: 19.2.3
optionalDependencies:
'@babel/core': 7.28.5
- styled-jsx@5.1.7(@babel/core@7.28.5)(react@19.2.1):
+ styled-jsx@5.1.7(@babel/core@7.28.5)(react@19.2.3):
dependencies:
client-only: 0.0.1
- react: 19.2.1
+ react: 19.2.3
optionalDependencies:
'@babel/core': 7.28.5
@@ -18061,11 +18061,11 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
- swr@2.3.7(react@19.2.1):
+ swr@2.3.7(react@19.2.3):
dependencies:
dequal: 2.0.3
- react: 19.2.1
- use-sync-external-store: 1.6.0(react@19.2.1)
+ react: 19.2.3
+ use-sync-external-store: 1.6.0(react@19.2.3)
synckit@0.11.11:
dependencies:
@@ -18403,50 +18403,50 @@ snapshots:
punycode: 1.4.1
qs: 6.14.0
- use-callback-ref@1.3.3(@types/react@19.2.7)(react@19.2.1):
+ use-callback-ref@1.3.3(@types/react@19.2.7)(react@19.2.3):
dependencies:
- react: 19.2.1
+ react: 19.2.3
tslib: 2.8.1
optionalDependencies:
'@types/react': 19.2.7
- use-composed-ref@1.4.0(@types/react@19.2.7)(react@19.2.1):
+ use-composed-ref@1.4.0(@types/react@19.2.7)(react@19.2.3):
dependencies:
- react: 19.2.1
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- use-context-selector@2.0.0(react@19.2.1)(scheduler@0.26.0):
+ use-context-selector@2.0.0(react@19.2.3)(scheduler@0.26.0):
dependencies:
- react: 19.2.1
+ react: 19.2.3
scheduler: 0.26.0
- use-isomorphic-layout-effect@1.2.1(@types/react@19.2.7)(react@19.2.1):
+ use-isomorphic-layout-effect@1.2.1(@types/react@19.2.7)(react@19.2.3):
dependencies:
- react: 19.2.1
+ react: 19.2.3
optionalDependencies:
'@types/react': 19.2.7
- use-latest@1.3.0(@types/react@19.2.7)(react@19.2.1):
+ use-latest@1.3.0(@types/react@19.2.7)(react@19.2.3):
dependencies:
- react: 19.2.1
- use-isomorphic-layout-effect: 1.2.1(@types/react@19.2.7)(react@19.2.1)
+ react: 19.2.3
+ use-isomorphic-layout-effect: 1.2.1(@types/react@19.2.7)(react@19.2.3)
optionalDependencies:
'@types/react': 19.2.7
- use-sidecar@1.1.3(@types/react@19.2.7)(react@19.2.1):
+ use-sidecar@1.1.3(@types/react@19.2.7)(react@19.2.3):
dependencies:
detect-node-es: 1.1.0
- react: 19.2.1
+ react: 19.2.3
tslib: 2.8.1
optionalDependencies:
'@types/react': 19.2.7
use-strict@1.0.1: {}
- use-sync-external-store@1.6.0(react@19.2.1):
+ use-sync-external-store@1.6.0(react@19.2.3):
dependencies:
- react: 19.2.1
+ react: 19.2.3
util-deprecate@1.0.2: {}
@@ -18829,23 +18829,23 @@ snapshots:
dependencies:
tslib: 2.3.0
- zundo@2.3.0(zustand@5.0.9(@types/react@19.2.7)(immer@10.2.0)(react@19.2.1)(use-sync-external-store@1.6.0(react@19.2.1))):
+ zundo@2.3.0(zustand@5.0.9(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))):
dependencies:
- zustand: 5.0.9(@types/react@19.2.7)(immer@10.2.0)(react@19.2.1)(use-sync-external-store@1.6.0(react@19.2.1))
+ zustand: 5.0.9(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3))
- zustand@4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.1):
+ zustand@4.5.7(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3):
dependencies:
- use-sync-external-store: 1.6.0(react@19.2.1)
+ use-sync-external-store: 1.6.0(react@19.2.3)
optionalDependencies:
'@types/react': 19.2.7
immer: 10.2.0
- react: 19.2.1
+ react: 19.2.3
- zustand@5.0.9(@types/react@19.2.7)(immer@10.2.0)(react@19.2.1)(use-sync-external-store@1.6.0(react@19.2.1)):
+ zustand@5.0.9(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)):
optionalDependencies:
'@types/react': 19.2.7
immer: 10.2.0
- react: 19.2.1
- use-sync-external-store: 1.6.0(react@19.2.1)
+ react: 19.2.3
+ use-sync-external-store: 1.6.0(react@19.2.3)
zwitch@2.0.4: {}