Merge branch 'feat/datasource' into feat/r2

# Conflicts:
#	api/services/rag_pipeline/rag_pipeline.py
#	web/app/components/workflow/constants.ts
#	web/app/components/workflow/header/run-and-history.tsx
#	web/app/components/workflow/hooks/use-nodes-interactions.ts
#	web/app/components/workflow/hooks/use-workflow-interactions.ts
#	web/app/components/workflow/hooks/use-workflow.ts
#	web/app/components/workflow/index.tsx
#	web/app/components/workflow/nodes/_base/components/panel-operator/panel-operator-popup.tsx
#	web/app/components/workflow/nodes/_base/panel.tsx
#	web/app/components/workflow/nodes/code/use-config.ts
#	web/app/components/workflow/nodes/llm/default.ts
#	web/app/components/workflow/panel/index.tsx
#	web/app/components/workflow/panel/version-history-panel/index.tsx
#	web/app/components/workflow/store/workflow/index.ts
#	web/app/components/workflow/types.ts
#	web/config/index.ts
#	web/types/workflow.ts
This commit is contained in:
jyong 2025-07-02 14:01:59 +08:00
commit e23d7e39ec
753 changed files with 28675 additions and 3917 deletions

View File

@ -6,7 +6,7 @@ on:
- "main"
- "deploy/dev"
- "deploy/enterprise"
- "feat/r2"
- "deploy/rag-dev"
tags:
- "*"

View File

@ -4,7 +4,7 @@ on:
workflow_run:
workflows: ["Build and Push API & Web"]
branches:
- "deploy/dev"
- "deploy/rag-dev"
types:
- completed
@ -12,12 +12,13 @@ jobs:
deploy:
runs-on: ubuntu-latest
if: |
github.event.workflow_run.conclusion == 'success'
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_branch == 'deploy/rag-dev'
steps:
- name: Deploy to server
uses: appleboy/ssh-action@v0.1.8
with:
host: ${{ secrets.SSH_HOST }}
host: ${{ secrets.RAG_SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |

View File

@ -86,6 +86,7 @@ from .datasets import (
)
from .datasets.rag_pipeline import (
datasource_auth,
datasource_content_preview,
rag_pipeline,
rag_pipeline_datasets,
rag_pipeline_import,

View File

@ -0,0 +1,54 @@
from flask_restful import ( # type: ignore
Resource, # type: ignore
reqparse,
)
from werkzeug.exceptions import Forbidden
from controllers.console import api
from controllers.console.datasets.wraps import get_rag_pipeline
from controllers.console.wraps import account_initialization_required, setup_required
from libs.login import current_user, login_required
from models import Account
from models.dataset import Pipeline
from services.rag_pipeline.rag_pipeline import RagPipelineService
class DataSourceContentPreviewApi(Resource):
@setup_required
@login_required
@account_initialization_required
@get_rag_pipeline
def post(self, pipeline: Pipeline, node_id: str):
"""
Run datasource content preview
"""
if not isinstance(current_user, Account):
raise Forbidden()
parser = reqparse.RequestParser()
parser.add_argument("inputs", type=dict, required=True, nullable=False, location="json")
parser.add_argument("datasource_type", type=str, required=True, location="json")
args = parser.parse_args()
inputs = args.get("inputs")
if inputs is None:
raise ValueError("missing inputs")
datasource_type = args.get("datasource_type")
if datasource_type is None:
raise ValueError("missing datasource_type")
rag_pipeline_service = RagPipelineService()
preview_content = rag_pipeline_service.run_datasource_node_preview(
pipeline=pipeline,
node_id=node_id,
user_inputs=inputs,
account=current_user,
datasource_type=datasource_type,
is_published=True,
)
return preview_content, 200
api.add_resource(
DataSourceContentPreviewApi,
"/rag/pipelines/<uuid:pipeline_id>/workflows/published/datasource/nodes/<string:node_id>/preview"
)

View File

@ -414,17 +414,19 @@ class RagPipelinePublishedDatasourceNodeRunApi(Resource):
raise ValueError("missing datasource_type")
rag_pipeline_service = RagPipelineService()
result = rag_pipeline_service.run_datasource_workflow_node(
pipeline=pipeline,
node_id=node_id,
user_inputs=inputs,
account=current_user,
datasource_type=datasource_type,
is_published=True,
return helper.compact_generate_response(
PipelineGenerator.convert_to_event_stream(
rag_pipeline_service.run_datasource_workflow_node(
pipeline=pipeline,
node_id=node_id,
user_inputs=inputs,
account=current_user,
datasource_type=datasource_type,
is_published=False,
)
)
)
return result
class RagPipelineDraftDatasourceNodeRunApi(Resource):
@setup_required
@ -455,21 +457,18 @@ class RagPipelineDraftDatasourceNodeRunApi(Resource):
raise ValueError("missing datasource_type")
rag_pipeline_service = RagPipelineService()
try:
return helper.compact_generate_response(
PipelineGenerator.convert_to_event_stream(
rag_pipeline_service.run_datasource_workflow_node(
pipeline=pipeline,
node_id=node_id,
user_inputs=inputs,
account=current_user,
datasource_type=datasource_type,
is_published=False,
)
return helper.compact_generate_response(
PipelineGenerator.convert_to_event_stream(
rag_pipeline_service.run_datasource_workflow_node(
pipeline=pipeline,
node_id=node_id,
user_inputs=inputs,
account=current_user,
datasource_type=datasource_type,
is_published=False,
)
)
except Exception as e:
print(e)
)
class RagPipelinePublishedNodeRunApi(Resource):

View File

@ -2,14 +2,14 @@ import contextvars
import datetime
import json
import logging
import random
import secrets
import threading
import time
import uuid
from collections.abc import Generator, Mapping
from typing import Any, Literal, Optional, Union, overload
from flask import Flask, copy_current_request_context, current_app, has_request_context
from flask import Flask, current_app
from pydantic import ValidationError
from sqlalchemy.orm import sessionmaker
@ -110,7 +110,7 @@ class PipelineGenerator(BaseAppGenerator):
start_node_id: str = args["start_node_id"]
datasource_type: str = args["datasource_type"]
datasource_info_list: list[Mapping[str, Any]] = args["datasource_info_list"]
batch = time.strftime("%Y%m%d%H%M%S") + str(random.randint(100000, 999999))
batch = time.strftime("%Y%m%d%H%M%S") + str(secrets.randbelow(900000) + 100000)
documents = []
if invoke_from == InvokeFrom.PUBLISHED:
for datasource_info in datasource_info_list:
@ -589,7 +589,7 @@ class PipelineGenerator(BaseAppGenerator):
if datasource_type == "local_file":
name = datasource_info["name"]
elif datasource_type == "online_document":
name = datasource_info["page_title"]
name = datasource_info["page"]["page_name"]
elif datasource_type == "website_crawl":
name = datasource_info["title"]
else:

View File

@ -214,10 +214,11 @@ class OnlineDocumentPage(BaseModel):
"""
page_id: str = Field(..., description="The page id")
page_title: str = Field(..., description="The page title")
page_name: str = Field(..., description="The page title")
page_icon: Optional[dict] = Field(None, description="The page icon")
type: str = Field(..., description="The type of the page")
last_edited_time: str = Field(..., description="The last edited time")
parent_id: Optional[str] = Field(None, description="The parent page id")
class OnlineDocumentInfo(BaseModel):

View File

@ -141,7 +141,7 @@ class PluginDatasourceManager(BasePluginClient):
datasource_provider_id = GenericProviderID(datasource_provider)
response = self._request_with_plugin_daemon_response_stream(
return self._request_with_plugin_daemon_response_stream(
"POST",
f"plugin/{tenant_id}/dispatch/datasource/get_online_document_pages",
OnlineDocumentPagesMessage,
@ -159,7 +159,6 @@ class PluginDatasourceManager(BasePluginClient):
"Content-Type": "application/json",
},
)
yield from response
def get_online_document_page_content(
self,
@ -177,7 +176,7 @@ class PluginDatasourceManager(BasePluginClient):
datasource_provider_id = GenericProviderID(datasource_provider)
response = self._request_with_plugin_daemon_response_stream(
return self._request_with_plugin_daemon_response_stream(
"POST",
f"plugin/{tenant_id}/dispatch/datasource/get_online_document_page_content",
DatasourceMessage,
@ -195,7 +194,6 @@ class PluginDatasourceManager(BasePluginClient):
"Content-Type": "application/json",
},
)
yield from response
def online_drive_browse_files(
self,

View File

@ -9,22 +9,30 @@ class DatasourceStreamEvent(Enum):
"""
Datasource Stream event
"""
PROCESSING = "datasource_processing"
COMPLETED = "datasource_completed"
ERROR = "datasource_error"
class BaseDatasourceEvent(BaseModel):
pass
class DatasourceErrorEvent(BaseDatasourceEvent):
event: str = DatasourceStreamEvent.ERROR.value
error: str = Field(..., description="error message")
class DatasourceCompletedEvent(BaseDatasourceEvent):
event: str = DatasourceStreamEvent.COMPLETED.value
data: Mapping[str,Any] | list = Field(..., description="result")
total: Optional[int] = Field(..., description="total")
completed: Optional[int] = Field(..., description="completed")
time_consuming: Optional[float] = Field(..., description="time consuming")
data: Mapping[str, Any] | list = Field(..., description="result")
total: Optional[int] = Field(default=0, description="total")
completed: Optional[int] = Field(default=0, description="completed")
time_consuming: Optional[float] = Field(default=0.0, description="time consuming")
class DatasourceProcessingEvent(BaseDatasourceEvent):
event: str = DatasourceStreamEvent.PROCESSING.value
total: Optional[int] = Field(..., description="total")
completed: Optional[int] = Field(..., description="completed")

View File

@ -68,12 +68,15 @@ class QAChunk(BaseModel):
question: str
answer: str
class QAStructureChunk(BaseModel):
"""
QAStructureChunk.
"""
qa_chunks: list[QAChunk]
class BaseDocumentTransformer(ABC):
"""Abstract base class for document transformation systems.

View File

@ -275,5 +275,3 @@ class AgentLogEvent(BaseAgentEvent):
InNodeEvent = BaseNodeEvent | BaseParallelBranchEvent | BaseIterationEvent | BaseAgentEvent | BaseLoopEvent

View File

@ -1,8 +1,9 @@
import json
import logging
import re
import threading
import time
from collections.abc import Callable, Generator, Sequence
from collections.abc import Callable, Generator, Mapping, Sequence
from datetime import UTC, datetime
from typing import Any, Optional, cast
from uuid import uuid4
@ -15,16 +16,20 @@ import contexts
from configs import dify_config
from core.app.entities.app_invoke_entities import InvokeFrom
from core.datasource.entities.datasource_entities import (
DatasourceMessage,
DatasourceProviderType,
GetOnlineDocumentPageContentRequest,
OnlineDocumentPagesMessage,
OnlineDriveBrowseFilesRequest,
OnlineDriveBrowseFilesResponse,
WebsiteCrawlMessage,
)
from core.datasource.online_document.online_document_plugin import OnlineDocumentDatasourcePlugin
from core.datasource.online_drive.online_drive_plugin import OnlineDriveDatasourcePlugin
from core.datasource.website_crawl.website_crawl_plugin import WebsiteCrawlDatasourcePlugin
from core.rag.entities.event import BaseDatasourceEvent, DatasourceCompletedEvent, DatasourceProcessingEvent
from core.rag.entities.event import (
BaseDatasourceEvent,
DatasourceCompletedEvent,
DatasourceErrorEvent,
DatasourceProcessingEvent,
)
from core.repositories.sqlalchemy_workflow_node_execution_repository import SQLAlchemyWorkflowNodeExecutionRepository
from core.variables.variables import Variable
from core.workflow.entities.node_entities import NodeRunResult
@ -64,6 +69,8 @@ from services.entities.knowledge_entities.rag_pipeline_entities import (
from services.errors.app import WorkflowHashNotEqualError
from services.rag_pipeline.pipeline_template.pipeline_template_factory import PipelineTemplateRetrievalFactory
logger = logging.getLogger(__name__)
class RagPipelineService:
@classmethod
@ -116,14 +123,6 @@ class RagPipelineService:
)
if not customized_template:
raise ValueError("Customized pipeline template not found.")
# check template name is exist
template_name = template_info.name
if template_name:
template = db.session.query(PipelineCustomizedTemplate).filter(PipelineCustomizedTemplate.name == template_name,
PipelineCustomizedTemplate.tenant_id == current_user.current_tenant_id,
PipelineCustomizedTemplate.id != template_id).first()
if template:
raise ValueError("Template name is already exists")
customized_template.name = template_info.name
customized_template.description = template_info.description
customized_template.icon = template_info.icon_info.model_dump()
@ -434,157 +433,210 @@ class RagPipelineService:
return workflow_node_execution
# def run_datasource_workflow_node_status(
# self, pipeline: Pipeline, node_id: str, job_id: str, account: Account,
# datasource_type: str, is_published: bool
# ) -> dict:
# """
# Run published workflow datasource
# """
# if is_published:
# # fetch published workflow by app_model
# workflow = self.get_published_workflow(pipeline=pipeline)
# else:
# workflow = self.get_draft_workflow(pipeline=pipeline)
# if not workflow:
# raise ValueError("Workflow not initialized")
#
# # run draft workflow node
# datasource_node_data = None
# start_at = time.perf_counter()
# datasource_nodes = workflow.graph_dict.get("nodes", [])
# for datasource_node in datasource_nodes:
# if datasource_node.get("id") == node_id:
# datasource_node_data = datasource_node.get("data", {})
# break
# if not datasource_node_data:
# raise ValueError("Datasource node data not found")
#
# from core.datasource.datasource_manager import DatasourceManager
#
# datasource_runtime = DatasourceManager.get_datasource_runtime(
# provider_id=f"{datasource_node_data.get('plugin_id')}/{datasource_node_data.get('provider_name')}",
# datasource_name=datasource_node_data.get("datasource_name"),
# tenant_id=pipeline.tenant_id,
# datasource_type=DatasourceProviderType(datasource_type),
# )
# datasource_provider_service = DatasourceProviderService()
# credentials = datasource_provider_service.get_real_datasource_credentials(
# tenant_id=pipeline.tenant_id,
# provider=datasource_node_data.get('provider_name'),
# plugin_id=datasource_node_data.get('plugin_id'),
# )
# if credentials:
# datasource_runtime.runtime.credentials = credentials[0].get("credentials")
# match datasource_type:
#
# case DatasourceProviderType.WEBSITE_CRAWL:
# datasource_runtime = cast(WebsiteCrawlDatasourcePlugin, datasource_runtime)
# website_crawl_results: list[WebsiteCrawlMessage] = []
# for website_message in datasource_runtime.get_website_crawl(
# user_id=account.id,
# datasource_parameters={"job_id": job_id},
# provider_type=datasource_runtime.datasource_provider_type(),
# ):
# website_crawl_results.append(website_message)
# return {
# "result": [result for result in website_crawl_results.result],
# "status": website_crawl_results.result.status,
# "provider_type": datasource_node_data.get("provider_type"),
# }
# case _:
# raise ValueError(f"Unsupported datasource provider: {datasource_runtime.datasource_provider_type}")
def run_datasource_workflow_node(
self, pipeline: Pipeline, node_id: str, user_inputs: dict, account: Account, datasource_type: str,
is_published: bool
self,
pipeline: Pipeline,
node_id: str,
user_inputs: dict,
account: Account,
datasource_type: str,
is_published: bool,
) -> Generator[BaseDatasourceEvent, None, None]:
"""
Run published workflow datasource
"""
if is_published:
# fetch published workflow by app_model
workflow = self.get_published_workflow(pipeline=pipeline)
else:
workflow = self.get_draft_workflow(pipeline=pipeline)
if not workflow:
raise ValueError("Workflow not initialized")
try:
if is_published:
# fetch published workflow by app_model
workflow = self.get_published_workflow(pipeline=pipeline)
else:
workflow = self.get_draft_workflow(pipeline=pipeline)
if not workflow:
raise ValueError("Workflow not initialized")
# run draft workflow node
datasource_node_data = None
start_at = time.perf_counter()
datasource_nodes = workflow.graph_dict.get("nodes", [])
for datasource_node in datasource_nodes:
if datasource_node.get("id") == node_id:
datasource_node_data = datasource_node.get("data", {})
break
if not datasource_node_data:
raise ValueError("Datasource node data not found")
# run draft workflow node
datasource_node_data = None
datasource_nodes = workflow.graph_dict.get("nodes", [])
for datasource_node in datasource_nodes:
if datasource_node.get("id") == node_id:
datasource_node_data = datasource_node.get("data", {})
break
if not datasource_node_data:
raise ValueError("Datasource node data not found")
datasource_parameters = datasource_node_data.get("datasource_parameters", {})
for key, value in datasource_parameters.items():
if not user_inputs.get(key):
user_inputs[key] = value["value"]
datasource_parameters = datasource_node_data.get("datasource_parameters", {})
for key, value in datasource_parameters.items():
if not user_inputs.get(key):
user_inputs[key] = value["value"]
from core.datasource.datasource_manager import DatasourceManager
from core.datasource.datasource_manager import DatasourceManager
datasource_runtime = DatasourceManager.get_datasource_runtime(
provider_id=f"{datasource_node_data.get('plugin_id')}/{datasource_node_data.get('provider_name')}",
datasource_name=datasource_node_data.get("datasource_name"),
tenant_id=pipeline.tenant_id,
datasource_type=DatasourceProviderType(datasource_type),
)
datasource_provider_service = DatasourceProviderService()
credentials = datasource_provider_service.get_real_datasource_credentials(
tenant_id=pipeline.tenant_id,
provider=datasource_node_data.get("provider_name"),
plugin_id=datasource_node_data.get("plugin_id"),
)
if credentials:
datasource_runtime.runtime.credentials = credentials[0].get("credentials")
match datasource_type:
case DatasourceProviderType.ONLINE_DOCUMENT:
datasource_runtime = cast(OnlineDocumentDatasourcePlugin, datasource_runtime)
online_document_result: Generator[OnlineDocumentPagesMessage, None, None] = (
datasource_runtime.get_online_document_pages(
user_id=account.id,
datasource_parameters=user_inputs,
provider_type=datasource_runtime.datasource_provider_type(),
)
)
start_time = time.time()
for message in online_document_result:
end_time = time.time()
online_document_event = DatasourceCompletedEvent(
data=message.result,
time_consuming=round(end_time - start_time, 2),
)
yield online_document_event.model_dump()
case DatasourceProviderType.WEBSITE_CRAWL:
datasource_runtime = cast(WebsiteCrawlDatasourcePlugin, datasource_runtime)
website_crawl_result: Generator[WebsiteCrawlMessage, None, None] = datasource_runtime.get_website_crawl(
user_id=account.id,
datasource_parameters=user_inputs,
provider_type=datasource_runtime.datasource_provider_type(),
)
start_time = time.time()
for message in website_crawl_result:
end_time = time.time()
if message.result.status == "completed":
crawl_event = DatasourceCompletedEvent(
data=message.result.web_info_list,
total=message.result.total,
completed=message.result.completed,
time_consuming=round(end_time - start_time, 2)
datasource_runtime = DatasourceManager.get_datasource_runtime(
provider_id=f"{datasource_node_data.get('plugin_id')}/{datasource_node_data.get('provider_name')}",
datasource_name=datasource_node_data.get("datasource_name"),
tenant_id=pipeline.tenant_id,
datasource_type=DatasourceProviderType(datasource_type),
)
datasource_provider_service = DatasourceProviderService()
credentials = datasource_provider_service.get_real_datasource_credentials(
tenant_id=pipeline.tenant_id,
provider=datasource_node_data.get("provider_name"),
plugin_id=datasource_node_data.get("plugin_id"),
)
if credentials:
datasource_runtime.runtime.credentials = credentials[0].get("credentials")
match datasource_type:
case DatasourceProviderType.ONLINE_DOCUMENT:
datasource_runtime = cast(OnlineDocumentDatasourcePlugin, datasource_runtime)
online_document_result: Generator[OnlineDocumentPagesMessage, None, None] = (
datasource_runtime.get_online_document_pages(
user_id=account.id,
datasource_parameters=user_inputs,
provider_type=datasource_runtime.datasource_provider_type(),
)
else:
crawl_event = DatasourceProcessingEvent(
total=message.result.total,
completed=message.result.completed,
)
start_time = time.time()
start_event = DatasourceProcessingEvent(
total=0,
completed=0,
)
yield start_event.model_dump()
try:
for message in online_document_result:
end_time = time.time()
online_document_event = DatasourceCompletedEvent(
data=message.result, time_consuming=round(end_time - start_time, 2)
)
yield online_document_event.model_dump()
except Exception as e:
logger.exception("Error during online document.")
yield DatasourceErrorEvent(error=str(e)).model_dump()
case DatasourceProviderType.WEBSITE_CRAWL:
datasource_runtime = cast(WebsiteCrawlDatasourcePlugin, datasource_runtime)
website_crawl_result: Generator[WebsiteCrawlMessage, None, None] = (
datasource_runtime.get_website_crawl(
user_id=account.id,
datasource_parameters=user_inputs,
provider_type=datasource_runtime.datasource_provider_type(),
)
yield crawl_event.model_dump()
case _:
raise ValueError(f"Unsupported datasource provider: {datasource_runtime.datasource_provider_type}")
)
start_time = time.time()
try:
for message in website_crawl_result:
end_time = time.time()
if message.result.status == "completed":
crawl_event = DatasourceCompletedEvent(
data=message.result.web_info_list,
total=message.result.total,
completed=message.result.completed,
time_consuming=round(end_time - start_time, 2),
)
else:
crawl_event = DatasourceProcessingEvent(
total=message.result.total,
completed=message.result.completed,
)
yield crawl_event.model_dump()
except Exception as e:
logger.exception("Error during website crawl.")
yield DatasourceErrorEvent(error=str(e)).model_dump()
case _:
raise ValueError(f"Unsupported datasource provider: {datasource_runtime.datasource_provider_type}")
except Exception as e:
logger.exception("Error in run_datasource_workflow_node.")
yield DatasourceErrorEvent(error=str(e)).model_dump()
def run_datasource_node_preview(
self,
pipeline: Pipeline,
node_id: str,
user_inputs: dict,
account: Account,
datasource_type: str,
is_published: bool,
) -> Mapping[str, Any]:
"""
Run published workflow datasource
"""
try:
if is_published:
# fetch published workflow by app_model
workflow = self.get_published_workflow(pipeline=pipeline)
else:
workflow = self.get_draft_workflow(pipeline=pipeline)
if not workflow:
raise ValueError("Workflow not initialized")
# run draft workflow node
datasource_node_data = None
datasource_nodes = workflow.graph_dict.get("nodes", [])
for datasource_node in datasource_nodes:
if datasource_node.get("id") == node_id:
datasource_node_data = datasource_node.get("data", {})
break
if not datasource_node_data:
raise ValueError("Datasource node data not found")
datasource_parameters = datasource_node_data.get("datasource_parameters", {})
for key, value in datasource_parameters.items():
if not user_inputs.get(key):
user_inputs[key] = value["value"]
from core.datasource.datasource_manager import DatasourceManager
datasource_runtime = DatasourceManager.get_datasource_runtime(
provider_id=f"{datasource_node_data.get('plugin_id')}/{datasource_node_data.get('provider_name')}",
datasource_name=datasource_node_data.get("datasource_name"),
tenant_id=pipeline.tenant_id,
datasource_type=DatasourceProviderType(datasource_type),
)
datasource_provider_service = DatasourceProviderService()
credentials = datasource_provider_service.get_real_datasource_credentials(
tenant_id=pipeline.tenant_id,
provider=datasource_node_data.get("provider_name"),
plugin_id=datasource_node_data.get("plugin_id"),
)
if credentials:
datasource_runtime.runtime.credentials = credentials[0].get("credentials")
match datasource_type:
case DatasourceProviderType.ONLINE_DOCUMENT:
datasource_runtime = cast(OnlineDocumentDatasourcePlugin, datasource_runtime)
online_document_result: Generator[DatasourceMessage, None, None] = (
datasource_runtime.get_online_document_page_content(
user_id=account.id,
datasource_parameters=GetOnlineDocumentPageContentRequest(
workspace_id=user_inputs.get("workspace_id"),
page_id=user_inputs.get("page_id"),
type=user_inputs.get("type"),
),
provider_type=datasource_type,
)
)
try:
variables: dict[str, Any] = {}
for message in online_document_result:
if message.type == DatasourceMessage.MessageType.VARIABLE:
assert isinstance(message.message, DatasourceMessage.VariableMessage)
variable_name = message.message.variable_name
variable_value = message.message.variable_value
if message.message.stream:
if not isinstance(variable_value, str):
raise ValueError("When 'stream' is True, 'variable_value' must be a string.")
if variable_name not in variables:
variables[variable_name] = ""
variables[variable_name] += variable_value
else:
variables[variable_name] = variable_value
return variables
except Exception as e:
logger.exception("Error during get online document content.")
raise RuntimeError(str(e))
#TODO Online Drive
case _:
raise ValueError(f"Unsupported datasource provider: {datasource_runtime.datasource_provider_type}")
except Exception as e:
logger.exception("Error in run_datasource_node_preview.")
raise RuntimeError(str(e))
def run_free_workflow_node(
self, node_data: dict, tenant_id: str, user_id: str, node_id: str, user_inputs: dict[str, Any]
@ -755,24 +807,77 @@ class RagPipelineService:
return workflow
def get_first_step_parameters(self, pipeline: Pipeline, node_id: str, is_draft: bool = False) -> list[dict]:
def get_published_second_step_parameters(self, pipeline: Pipeline, node_id: str) -> list[dict]:
"""
Get second step parameters of rag pipeline
"""
workflow = self.get_published_workflow(pipeline=pipeline)
if not workflow:
raise ValueError("Workflow not initialized")
# get second step node
rag_pipeline_variables = workflow.rag_pipeline_variables
if not rag_pipeline_variables:
return []
# get datasource provider
datasource_provider_variables = [
item
for item in rag_pipeline_variables
if item.get("belong_to_node_id") == node_id or item.get("belong_to_node_id") == "shared"
]
return datasource_provider_variables
def get_published_first_step_parameters(self, pipeline: Pipeline, node_id: str) -> list[dict]:
"""
Get first step parameters of rag pipeline
"""
workflow = self.get_draft_workflow(pipeline=pipeline) if is_draft else self.get_published_workflow(pipeline=pipeline)
if not workflow:
published_workflow = self.get_published_workflow(pipeline=pipeline)
if not published_workflow:
raise ValueError("Workflow not initialized")
# get second step node
datasource_node_data = None
datasource_nodes = workflow.graph_dict.get("nodes", [])
datasource_nodes = published_workflow.graph_dict.get("nodes", [])
for datasource_node in datasource_nodes:
if datasource_node.get("id") == node_id:
datasource_node_data = datasource_node.get("data", {})
break
if not datasource_node_data:
raise ValueError("Datasource node data not found")
variables = workflow.rag_pipeline_variables
variables = datasource_node_data.get("variables", {})
if variables:
variables_map = {item["variable"]: item for item in variables}
else:
return []
datasource_parameters = datasource_node_data.get("datasource_parameters", {})
user_input_variables = []
for key, value in datasource_parameters.items():
if not re.match(r"\{\{#([a-zA-Z0-9_]{1,50}(?:\.[a-zA-Z_][a-zA-Z0-9_]{0,29}){1,10})#\}\}", value["value"]):
user_input_variables.append(variables_map.get(key, {}))
return user_input_variables
def get_draft_first_step_parameters(self, pipeline: Pipeline, node_id: str) -> list[dict]:
"""
Get first step parameters of rag pipeline
"""
draft_workflow = self.get_draft_workflow(pipeline=pipeline)
if not draft_workflow:
raise ValueError("Workflow not initialized")
# get second step node
datasource_node_data = None
datasource_nodes = draft_workflow.graph_dict.get("nodes", [])
for datasource_node in datasource_nodes:
if datasource_node.get("id") == node_id:
datasource_node_data = datasource_node.get("data", {})
break
if not datasource_node_data:
raise ValueError("Datasource node data not found")
variables = datasource_node_data.get("variables", {})
if variables:
variables_map = {item["variable"]: item for item in variables}
else:
@ -781,21 +886,16 @@ class RagPipelineService:
user_input_variables = []
for key, value in datasource_parameters.items():
if value.get("value") and isinstance(value.get("value"), str):
pattern = r"\{\{#([a-zA-Z0-9_]{1,50}(?:\.[a-zA-Z0-9_][a-zA-Z0-9_]{0,29}){1,10})#\}\}"
match = re.match(pattern, value["value"])
if match:
full_path = match.group(1)
last_part = full_path.split('.')[-1]
user_input_variables.append(variables_map.get(last_part, {}))
if not re.match(r"\{\{#([a-zA-Z0-9_]{1,50}(?:\.[a-zA-Z_][a-zA-Z0-9_]{0,29}){1,10})#\}\}", value["value"]):
user_input_variables.append(variables_map.get(key, {}))
return user_input_variables
def get_second_step_parameters(self, pipeline: Pipeline, node_id: str, is_draft: bool = False) -> list[dict]:
def get_draft_second_step_parameters(self, pipeline: Pipeline, node_id: str) -> list[dict]:
"""
Get second step parameters of rag pipeline
"""
workflow = self.get_draft_workflow(pipeline=pipeline) if is_draft else self.get_published_workflow(pipeline=pipeline)
workflow = self.get_draft_workflow(pipeline=pipeline)
if not workflow:
raise ValueError("Workflow not initialized")
@ -803,32 +903,13 @@ class RagPipelineService:
rag_pipeline_variables = workflow.rag_pipeline_variables
if not rag_pipeline_variables:
return []
variables_map = {item["variable"]: item for item in rag_pipeline_variables}
# get datasource node data
datasource_node_data = None
datasource_nodes = workflow.graph_dict.get("nodes", [])
for datasource_node in datasource_nodes:
if datasource_node.get("id") == node_id:
datasource_node_data = datasource_node.get("data", {})
break
if datasource_node_data:
datasource_parameters = datasource_node_data.get("datasource_parameters", {})
for key, value in datasource_parameters.items():
if value.get("value") and isinstance(value.get("value"), str):
pattern = r"\{\{#([a-zA-Z0-9_]{1,50}(?:\.[a-zA-Z0-9_][a-zA-Z0-9_]{0,29}){1,10})#\}\}"
match = re.match(pattern, value["value"])
if match:
full_path = match.group(1)
last_part = full_path.split('.')[-1]
variables_map.pop(last_part)
all_second_step_variables = list(variables_map.values())
# get datasource provider
datasource_provider_variables = [
item
for item in all_second_step_variables
if item.get("belong_to_node_id") == node_id or item.get("belong_to_node_id") == "shared"
]
item
for item in rag_pipeline_variables
if item.get("belong_to_node_id") == node_id or item.get("belong_to_node_id") == "shared"
]
return datasource_provider_variables
def get_rag_pipeline_paginate_workflow_runs(self, pipeline: Pipeline, args: dict) -> InfiniteScrollPagination:
@ -950,16 +1031,6 @@ class RagPipelineService:
if not dataset:
raise ValueError("Dataset not found")
# check template name is exist
template_name = args.get("name")
if template_name:
template = db.session.query(PipelineCustomizedTemplate).filter(
PipelineCustomizedTemplate.name == template_name,
PipelineCustomizedTemplate.tenant_id == pipeline.tenant_id,
).first()
if template:
raise ValueError("Template name is already exists")
max_position = (
db.session.query(func.max(PipelineCustomizedTemplate.position))
.filter(PipelineCustomizedTemplate.tenant_id == pipeline.tenant_id)

View File

@ -43,10 +43,10 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
const { isCurrentWorkspaceEditor, isLoadingCurrentWorkspace } = useAppContext()
const { appDetail, setAppDetail, setAppSiderbarExpand } = useStore(useShallow(state => ({
const { appDetail, setAppDetail, setAppSidebarExpand } = useStore(useShallow(state => ({
appDetail: state.appDetail,
setAppDetail: state.setAppDetail,
setAppSiderbarExpand: state.setAppSiderbarExpand,
setAppSidebarExpand: state.setAppSidebarExpand,
})))
const [isLoadingAppDetail, setIsLoadingAppDetail] = useState(false)
const [appDetailRes, setAppDetailRes] = useState<App | null>(null)
@ -57,8 +57,8 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
selectedIcon: NavIcon
}>>([])
const getNavigations = useCallback((appId: string, isCurrentWorkspaceEditor: boolean, mode: string) => {
const navs = [
const getNavigationConfig = useCallback((appId: string, isCurrentWorkspaceEditor: boolean, mode: string) => {
const navConfig = [
...(isCurrentWorkspaceEditor
? [{
name: t('common.appMenus.promptEng'),
@ -92,8 +92,8 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
selectedIcon: RiDashboard2Fill,
},
]
return navs
}, [])
return navConfig
}, [t])
useDocumentTitle(appDetail?.name || t('common.menus.appDetail'))
@ -101,10 +101,10 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
if (appDetail) {
const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand'
const mode = isMobile ? 'collapse' : 'expand'
setAppSiderbarExpand(isMobile ? mode : localeMode)
setAppSidebarExpand(isMobile ? mode : localeMode)
// TODO: consider screen size and mode
// if ((appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (pathname).endsWith('workflow'))
// setAppSiderbarExpand('collapse')
// setAppSidebarExpand('collapse')
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [appDetail, isMobile])
@ -141,7 +141,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
}
else {
setAppDetail({ ...res, enable_sso: false })
setNavigation(getNavigations(appId, isCurrentWorkspaceEditor, res.mode))
setNavigation(getNavigationConfig(appId, isCurrentWorkspaceEditor, res.mode))
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [appDetailRes, isCurrentWorkspaceEditor, isLoadingAppDetail, isLoadingCurrentWorkspace])
@ -161,7 +161,9 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
return (
<div className={cn(s.app, 'relative flex', 'overflow-hidden')}>
{appDetail && (
<AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background as string} desc={appDetail.mode} navigation={navigation} />
<AppSideBar
navigation={navigation}
/>
)}
<div className="grow overflow-hidden bg-components-panel-bg">
{children}

View File

@ -0,0 +1,10 @@
import React from 'react'
import CreateFromPipeline from '@/app/components/datasets/documents/create-from-pipeline'
const CreateFromPipelinePage = async () => {
return (
<CreateFromPipeline />
)
}
export default CreateFromPipelinePage

View File

@ -2,10 +2,10 @@
import type { FC } from 'react'
import React, { useEffect, useMemo } from 'react'
import { usePathname } from 'next/navigation'
import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import type { RemixiconComponentType } from '@remixicon/react'
import {
RiAttachmentLine,
RiEqualizer2Fill,
RiEqualizer2Line,
RiFileTextFill,
@ -13,12 +13,8 @@ import {
RiFocus2Fill,
RiFocus2Line,
} from '@remixicon/react'
import {
PaperClipIcon,
} from '@heroicons/react/24/outline'
import { RiApps2AddLine, RiBookOpenLine, RiInformation2Line } from '@remixicon/react'
import { RiInformation2Line } from '@remixicon/react'
import classNames from '@/utils/classnames'
import { fetchDatasetDetail, fetchDatasetRelatedApps } from '@/service/datasets'
import type { RelatedAppResponse } from '@/models/datasets'
import AppSideBar from '@/app/components/app-sidebar'
import Loading from '@/app/components/base/loading'
@ -30,6 +26,10 @@ import { useDocLink } from '@/context/i18n'
import { useAppContext } from '@/context/app-context'
import Tooltip from '@/app/components/base/tooltip'
import LinkedAppsPanel from '@/app/components/base/linked-apps-panel'
import { PipelineFill, PipelineLine } from '@/app/components/base/icons/src/vender/pipeline'
import { Divider } from '@/app/components/base/icons/src/vender/knowledge'
import NoLinkedAppsPanel from '@/app/components/datasets/no-linked-apps-panel'
import { useDatasetDetail, useDatasetRelatedApps } from '@/service/knowledge/use-dataset'
import useDocumentTitle from '@/hooks/use-document-title'
export type IAppDetailLayoutProps = {
@ -38,81 +38,72 @@ export type IAppDetailLayoutProps = {
}
type IExtraInfoProps = {
isMobile: boolean
relatedApps?: RelatedAppResponse
documentCount?: number
expand: boolean
}
const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => {
const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile)
const ExtraInfo = React.memo(({
relatedApps,
documentCount,
expand,
}: IExtraInfoProps) => {
const { t } = useTranslation()
const docLink = useDocLink()
const hasRelatedApps = relatedApps?.data && relatedApps?.data?.length > 0
const relatedAppsTotal = relatedApps?.data?.length || 0
useEffect(() => {
setShowTips(!isMobile)
}, [isMobile, setShowTips])
return <div>
{hasRelatedApps && (
<>
{!isMobile && (
<Tooltip
position='right'
noDecoration
needsDelay
popupContent={
<LinkedAppsPanel
relatedApps={relatedApps.data}
isMobile={isMobile}
/>
}
>
<div className='system-xs-medium-uppercase inline-flex cursor-pointer items-center space-x-1 text-text-secondary'>
<span>{relatedAppsTotal || '--'} {t('common.datasetMenus.relatedApp')}</span>
<RiInformation2Line className='h-4 w-4' />
return (
<>
{!expand && (
<div className='flex items-center gap-x-0.5'>
<div className='flex grow flex-col px-2 pb-1.5 pt-1'>
<div className='system-md-semibold-uppercase text-text-secondary'>
{documentCount ?? '--'}
</div>
</Tooltip>
)}
{isMobile && <div className={classNames('uppercase text-xs text-text-tertiary font-medium pb-2 pt-4', 'flex items-center justify-center !px-0 gap-1')}>
{relatedAppsTotal || '--'}
<PaperClipIcon className='h-4 w-4 text-text-secondary' />
</div>}
</>
)}
{!hasRelatedApps && !expand && (
<Tooltip
position='right'
noDecoration
needsDelay
popupContent={
<div className='w-[240px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-4'>
<div className='inline-flex rounded-lg border-[0.5px] border-components-panel-border-subtle bg-background-default-subtle p-2'>
<RiApps2AddLine className='h-4 w-4 text-text-tertiary' />
<div className='system-2xs-medium-uppercase text-text-tertiary'>
{t('common.datasetMenus.documents')}
</div>
<div className='my-2 text-xs text-text-tertiary'>{t('common.datasetMenus.emptyTip')}</div>
<a
className='mt-2 inline-flex cursor-pointer items-center text-xs text-text-accent'
href={docLink('/guides/knowledge-base/integrate-knowledge-within-application')}
target='_blank' rel='noopener noreferrer'
>
<RiBookOpenLine className='mr-1 text-text-accent' />
{t('common.datasetMenus.viewDoc')}
</a>
</div>
}
>
<div className='system-xs-medium-uppercase inline-flex cursor-pointer items-center space-x-1 text-text-secondary'>
<span>{t('common.datasetMenus.noRelatedApp')}</span>
<RiInformation2Line className='h-4 w-4' />
<div className='py-2 pl-0.5 pr-1.5'>
<Divider className='text-test-divider-regular h-full w-fit' />
</div>
<div className='flex grow flex-col px-2 pb-1.5 pt-1'>
<div className='system-md-semibold-uppercase text-text-secondary'>
{relatedAppsTotal ?? '--'}
</div>
<Tooltip
position='bottom-start'
noDecoration
needsDelay
popupContent={
hasRelatedApps ? (
<LinkedAppsPanel
relatedApps={relatedApps.data}
isMobile={expand}
/>
) : <NoLinkedAppsPanel />
}
>
<div className='system-2xs-medium-uppercase flex cursor-pointer items-center gap-x-0.5 text-text-tertiary'>
<span>{t('common.datasetMenus.relatedApp')}</span>
<RiInformation2Line className='size-3' />
</div>
</Tooltip>
</div>
</div>
</Tooltip>
)}
</div>
}
)}
{expand && (
<div className={classNames('uppercase text-xs text-text-tertiary font-medium pb-2 pt-4', 'flex items-center justify-center !px-0 gap-1')}>
{relatedAppsTotal ?? '--'}
<RiAttachmentLine className='size-4 text-text-secondary' />
</div>
)}
</>
)
})
const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
const {
@ -120,70 +111,98 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
params: { datasetId },
} = props
const pathname = usePathname()
const hideSideBar = /documents\/create$/.test(pathname)
const hideSideBar = pathname.endsWith('documents/create') || pathname.endsWith('documents/create-from-pipeline')
const { t } = useTranslation()
const { isCurrentWorkspaceDatasetOperator } = useAppContext()
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
const { data: datasetRes, error, mutate: mutateDatasetRes } = useSWR({
url: 'fetchDatasetDetail',
datasetId,
}, apiParams => fetchDatasetDetail(apiParams.datasetId))
const { data: datasetRes, error, refetch: mutateDatasetRes } = useDatasetDetail(datasetId)
const { data: relatedApps } = useSWR({
action: 'fetchDatasetRelatedApps',
datasetId,
}, apiParams => fetchDatasetRelatedApps(apiParams.datasetId))
const { data: relatedApps } = useDatasetRelatedApps(datasetId)
const isButtonDisabledWithPipeline = useMemo(() => {
if (!datasetRes)
return true
if (datasetRes.provider === 'external')
return false
if (datasetRes.runtime_mode === 'general')
return false
return !datasetRes.is_published
}, [datasetRes])
const navigation = useMemo(() => {
const baseNavigation = [
{ name: t('common.datasetMenus.hitTesting'), href: `/datasets/${datasetId}/hitTesting`, icon: RiFocus2Line, selectedIcon: RiFocus2Fill },
{ name: t('common.datasetMenus.settings'), href: `/datasets/${datasetId}/settings`, icon: RiEqualizer2Line, selectedIcon: RiEqualizer2Fill },
{
name: t('common.datasetMenus.hitTesting'),
href: `/datasets/${datasetId}/hitTesting`,
icon: RiFocus2Line,
selectedIcon: RiFocus2Fill,
disabled: isButtonDisabledWithPipeline,
},
{
name: t('common.datasetMenus.settings'),
href: `/datasets/${datasetId}/settings`,
icon: RiEqualizer2Line,
selectedIcon: RiEqualizer2Fill,
disabled: false,
},
]
if (datasetRes?.provider !== 'external') {
if (datasetRes?.runtime_mode === 'rag_pipeline') {
baseNavigation.unshift({
name: t('common.datasetMenus.pipeline'),
href: `/datasets/${datasetId}/pipeline`,
icon: PipelineLine as RemixiconComponentType,
selectedIcon: PipelineFill as RemixiconComponentType,
disabled: false,
})
}
baseNavigation.unshift({
name: t('common.datasetMenus.documents'),
href: `/datasets/${datasetId}/documents`,
icon: RiFileTextLine,
selectedIcon: RiFileTextFill,
disabled: isButtonDisabledWithPipeline,
})
}
return baseNavigation
}, [datasetRes?.provider, datasetId, t])
}, [t, datasetId, isButtonDisabledWithPipeline, datasetRes?.provider, datasetRes?.runtime_mode])
useDocumentTitle(datasetRes?.name || t('common.menus.datasets'))
const setAppSiderbarExpand = useStore(state => state.setAppSiderbarExpand)
const setAppSidebarExpand = useStore(state => state.setAppSidebarExpand)
useEffect(() => {
const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand'
const mode = isMobile ? 'collapse' : 'expand'
setAppSiderbarExpand(isMobile ? mode : localeMode)
}, [isMobile, setAppSiderbarExpand])
setAppSidebarExpand(isMobile ? mode : localeMode)
}, [isMobile, setAppSidebarExpand])
if (!datasetRes && !error)
return <Loading type='app' />
return (
<div className='flex grow overflow-hidden'>
{!hideSideBar && <AppSideBar
title={datasetRes?.name || '--'}
icon={datasetRes?.icon || 'https://static.dify.ai/images/dataset-default-icon.png'}
icon_background={datasetRes?.icon_background || '#F5F5F5'}
desc={datasetRes?.description || '--'}
isExternal={datasetRes?.provider === 'external'}
navigation={navigation}
extraInfo={!isCurrentWorkspaceDatasetOperator ? mode => <ExtraInfo isMobile={mode === 'collapse'} relatedApps={relatedApps} expand={mode === 'collapse'} /> : undefined}
iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'}
/>}
<DatasetDetailContext.Provider value={{
indexingTechnique: datasetRes?.indexing_technique,
dataset: datasetRes,
mutateDatasetRes: () => mutateDatasetRes(),
mutateDatasetRes,
}}>
{!hideSideBar && (
<AppSideBar
navigation={navigation}
extraInfo={
!isCurrentWorkspaceDatasetOperator
? mode => <ExtraInfo relatedApps={relatedApps} expand={mode === 'collapse'} documentCount={datasetRes?.document_count} />
: undefined
}
iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'}
/>
)}
<div className="grow overflow-hidden bg-background-default-subtle">{children}</div>
</DatasetDetailContext.Provider>
</div>

View File

@ -0,0 +1,11 @@
'use client'
import RagPipeline from '@/app/components/rag-pipeline'
const PipelinePage = () => {
return (
<div className='h-full w-full overflow-x-auto'>
<RagPipeline />
</div>
)
}
export default PipelinePage

View File

@ -8,8 +8,8 @@ const Settings = async () => {
return (
<div className='h-full overflow-y-auto'>
<div className='px-6 py-3'>
<div className='system-xl-semibold mb-1 text-text-primary'>{t('title')}</div>
<div className='flex flex-col gap-y-0.5 px-6 pb-2 pt-3'>
<div className='system-xl-semibold text-text-primary'>{t('title')}</div>
<div className='system-sm-regular text-text-tertiary'>{t('desc')}</div>
</div>
<Form />

View File

@ -1,96 +0,0 @@
'use client'
import { useCallback, useEffect, useRef } from 'react'
import useSWRInfinite from 'swr/infinite'
import { debounce } from 'lodash-es'
import NewDatasetCard from './NewDatasetCard'
import DatasetCard from './DatasetCard'
import type { DataSetListResponse, FetchDatasetsParams } from '@/models/datasets'
import { fetchDatasets } from '@/service/datasets'
import { useAppContext } from '@/context/app-context'
import { useTranslation } from 'react-i18next'
const getKey = (
pageIndex: number,
previousPageData: DataSetListResponse,
tags: string[],
keyword: string,
includeAll: boolean,
) => {
if (!pageIndex || previousPageData.has_more) {
const params: FetchDatasetsParams = {
url: 'datasets',
params: {
page: pageIndex + 1,
limit: 30,
include_all: includeAll,
},
}
if (tags.length)
params.params.tag_ids = tags
if (keyword)
params.params.keyword = keyword
return params
}
return null
}
type Props = {
containerRef: React.RefObject<HTMLDivElement>
tags: string[]
keywords: string
includeAll: boolean
}
const Datasets = ({
containerRef,
tags,
keywords,
includeAll,
}: Props) => {
const { t } = useTranslation()
const { isCurrentWorkspaceEditor } = useAppContext()
const { data, isLoading, setSize, mutate } = useSWRInfinite(
(pageIndex: number, previousPageData: DataSetListResponse) => getKey(pageIndex, previousPageData, tags, keywords, includeAll),
fetchDatasets,
{ revalidateFirstPage: false, revalidateAll: true },
)
const loadingStateRef = useRef(false)
const anchorRef = useRef<HTMLAnchorElement>(null)
useEffect(() => {
loadingStateRef.current = isLoading
}, [isLoading, t])
const onScroll = useCallback(
debounce(() => {
if (!loadingStateRef.current && containerRef.current && anchorRef.current) {
const { scrollTop, clientHeight } = containerRef.current
const anchorOffset = anchorRef.current.offsetTop
if (anchorOffset - scrollTop - clientHeight < 100)
setSize(size => size + 1)
}
}, 50),
[setSize],
)
useEffect(() => {
const currentContainer = containerRef.current
currentContainer?.addEventListener('scroll', onScroll)
return () => {
currentContainer?.removeEventListener('scroll', onScroll)
onScroll.cancel()
}
}, [containerRef, onScroll])
return (
<nav className='grid shrink-0 grow grid-cols-1 content-start gap-4 px-12 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4'>
{isCurrentWorkspaceEditor && <NewDatasetCard ref={anchorRef} />}
{data?.map(({ data: datasets }) => datasets.map(dataset => (
<DatasetCard key={dataset.id} dataset={dataset} onSuccess={mutate} />),
))}
</nav>
)
}
export default Datasets

View File

@ -1,42 +0,0 @@
'use client'
import { useTranslation } from 'react-i18next'
import { basePath } from '@/utils/var'
import {
RiAddLine,
RiArrowRightLine,
} from '@remixicon/react'
import Link from 'next/link'
type CreateAppCardProps = {
ref?: React.Ref<HTMLAnchorElement>
}
const CreateAppCard = ({ ref }: CreateAppCardProps) => {
const { t } = useTranslation()
return (
<div className='bg-background-default-dimm flex min-h-[160px] flex-col rounded-xl border-[0.5px]
border-components-panel-border transition-all duration-200 ease-in-out'
>
<Link ref={ref} className='group flex grow cursor-pointer items-start p-4' href={`${basePath}/datasets/create`}>
<div className='flex items-center gap-3'>
<div className='flex h-10 w-10 items-center justify-center rounded-lg border border-dashed border-divider-regular bg-background-default-lighter
p-2 group-hover:border-solid group-hover:border-effects-highlight group-hover:bg-background-default-dodge'
>
<RiAddLine className='h-4 w-4 text-text-tertiary group-hover:text-text-accent' />
</div>
<div className='system-md-semibold text-text-secondary group-hover:text-text-accent'>{t('dataset.createDataset')}</div>
</div>
</Link>
<div className='system-xs-regular p-4 pt-0 text-text-tertiary'>{t('dataset.createDatasetIntro')}</div>
<Link className='group flex cursor-pointer items-center gap-1 rounded-b-xl border-t-[0.5px] border-divider-subtle p-4' href={`${basePath}/datasets/connect`}>
<div className='system-xs-medium text-text-tertiary group-hover:text-text-accent'>{t('dataset.connectDataset')}</div>
<RiArrowRightLine className='h-3.5 w-3.5 text-text-tertiary group-hover:text-text-accent' />
</Link>
</div>
)
}
CreateAppCard.displayName = 'CreateAppCard'
export default CreateAppCard

View File

@ -0,0 +1,10 @@
import React from 'react'
import CreateFromPipeline from '@/app/components/datasets/create-from-pipeline'
const DatasetCreation = async () => {
return (
<CreateFromPipeline />
)
}
export default DatasetCreation

View File

@ -1,12 +1,7 @@
'use client'
import { useTranslation } from 'react-i18next'
import Container from './Container'
import useDocumentTitle from '@/hooks/use-document-title'
import List from '../../components/datasets/list'
const AppList = () => {
const { t } = useTranslation()
useDocumentTitle(t('common.menus.datasets'))
return <Container />
const DatasetList = async () => {
return <List />
}
export default AppList
export default DatasetList

View File

@ -1,11 +0,0 @@
import { create } from 'zustand'
type DatasetStore = {
showExternalApiPanel: boolean
setShowExternalApiPanel: (show: boolean) => void
}
export const useDatasetStore = create<DatasetStore>(set => ({
showExternalApiPanel: false,
setShowExternalApiPanel: show => set({ showExternalApiPanel: show }),
}))

View File

@ -3,40 +3,89 @@ import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import AppIcon from '../base/app-icon'
const DatasetSvg = <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M0.833497 5.13481C0.833483 4.69553 0.83347 4.31654 0.858973 4.0044C0.88589 3.67495 0.94532 3.34727 1.10598 3.03195C1.34567 2.56155 1.72812 2.17909 2.19852 1.93941C2.51384 1.77875 2.84152 1.71932 3.17097 1.6924C3.48312 1.6669 3.86209 1.66691 4.30137 1.66693L7.62238 1.66684C8.11701 1.66618 8.55199 1.66561 8.95195 1.80356C9.30227 1.92439 9.62134 2.12159 9.88607 2.38088C10.1883 2.67692 10.3823 3.06624 10.603 3.50894L11.3484 5.00008H14.3679C15.0387 5.00007 15.5924 5.00006 16.0434 5.03691C16.5118 5.07518 16.9424 5.15732 17.3468 5.36339C17.974 5.68297 18.4839 6.19291 18.8035 6.82011C19.0096 7.22456 19.0917 7.65515 19.13 8.12356C19.1668 8.57455 19.1668 9.12818 19.1668 9.79898V13.5345C19.1668 14.2053 19.1668 14.7589 19.13 15.2099C19.0917 15.6784 19.0096 16.1089 18.8035 16.5134C18.4839 17.1406 17.974 17.6505 17.3468 17.9701C16.9424 18.1762 16.5118 18.2583 16.0434 18.2966C15.5924 18.3334 15.0387 18.3334 14.3679 18.3334H5.63243C4.96163 18.3334 4.40797 18.3334 3.95698 18.2966C3.48856 18.2583 3.05798 18.1762 2.65353 17.9701C2.02632 17.6505 1.51639 17.1406 1.19681 16.5134C0.990734 16.1089 0.908597 15.6784 0.870326 15.2099C0.833478 14.7589 0.833487 14.2053 0.833497 13.5345V5.13481ZM7.51874 3.33359C8.17742 3.33359 8.30798 3.34447 8.4085 3.37914C8.52527 3.41942 8.63163 3.48515 8.71987 3.57158C8.79584 3.64598 8.86396 3.7579 9.15852 4.34704L9.48505 5.00008L2.50023 5.00008C2.50059 4.61259 2.50314 4.34771 2.5201 4.14012C2.5386 3.91374 2.57 3.82981 2.59099 3.7886C2.67089 3.6318 2.79837 3.50432 2.95517 3.42442C2.99638 3.40343 3.08031 3.37203 3.30669 3.35353C3.54281 3.33424 3.85304 3.33359 4.3335 3.33359H7.51874Z" fill="#444CE7" />
</svg>
import Effect from '../base/effect'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import type { DataSet } from '@/models/datasets'
import { DOC_FORM_ICON_WITH_BG, DOC_FORM_TEXT } from '@/models/datasets'
import { useKnowledge } from '@/hooks/use-knowledge'
import Badge from '../base/badge'
import cn from '@/utils/classnames'
type Props = {
isExternal?: boolean
name: string
description: string
expand: boolean
extraInfo?: React.ReactNode
}
const DatasetInfo: FC<Props> = ({
name,
description,
isExternal,
expand,
extraInfo,
}) => {
const { t } = useTranslation()
const dataset = useDatasetDetailContextWithSelector(state => state.dataset) as DataSet
const iconInfo = dataset.icon_info || {
icon: '📙',
icon_type: 'emoji',
icon_background: '#FFF4ED',
icon_url: '',
}
const isExternalProvider = dataset.provider === 'external'
const { formatIndexingTechniqueAndMethod } = useKnowledge()
const chunkingModeIcon = dataset.doc_form ? DOC_FORM_ICON_WITH_BG[dataset.doc_form] : React.Fragment
const Icon = isExternalProvider ? DOC_FORM_ICON_WITH_BG.external : chunkingModeIcon
return (
<div className='pl-1 pt-1'>
<div className='mr-3 shrink-0'>
<AppIcon innerIcon={DatasetSvg} className='!border-[0.5px] !border-indigo-100 !bg-indigo-25' />
</div>
<div className={cn('relative flex flex-col', expand ? '' : 'p-1')}>
{expand && (
<div className='mt-2'>
<div className='system-md-semibold text-text-secondary'>
{name}
<>
<Effect className='-left-5 top-[-22px] opacity-15' />
<div className='flex flex-col gap-y-2 p-2'>
<div className='relative w-fit'>
<AppIcon
size='medium'
iconType={iconInfo.icon_type}
icon={iconInfo.icon}
background={iconInfo.icon_background}
imageUrl={iconInfo.icon_url}
/>
{(dataset.doc_form || isExternalProvider) && (
<div className='absolute -bottom-1 -right-1 z-10'>
<Icon className='size-4' />
</div>
)}
</div>
<>
<div className='flex flex-col gap-y-1'>
<div
className='system-md-semibold truncate text-text-secondary'
title={dataset.name}
>
{dataset.name}
</div>
<div className='system-2xs-medium-uppercase text-text-tertiary'>
{isExternalProvider && t('dataset.externalTag')}
{!isExternalProvider && dataset.doc_form && dataset.indexing_technique && (
<div className='flex items-center gap-x-1'>
<Badge>{t(`dataset.chunkingMode.${DOC_FORM_TEXT[dataset.doc_form]}`)}</Badge>
<Badge>{formatIndexingTechniqueAndMethod(dataset.indexing_technique, dataset.retrieval_model_dict?.search_method)}</Badge>
</div>
)}
</div>
</div>
<p className='system-xs-regular line-clamp-3 text-text-tertiary first-letter:capitalize'>
{dataset.description}
</p>
</>
</div>
<div className='system-2xs-medium-uppercase mt-1 text-text-tertiary'>{isExternal ? t('dataset.externalTag') : t('dataset.localDocs')}</div>
<div className='system-xs-regular my-3 text-text-tertiary first-letter:capitalize'>{description}</div>
</div>
</>
)}
{!expand && (
<AppIcon
size='medium'
iconType={iconInfo.icon_type}
icon={iconInfo.icon}
background={iconInfo.icon_background}
imageUrl={iconInfo.icon_url}
/>
)}
{extraInfo}
</div>

View File

@ -4,7 +4,6 @@ import { useShallow } from 'zustand/react/shallow'
import { RiLayoutLeft2Line, RiLayoutRight2Line } from '@remixicon/react'
import NavLink from './navLink'
import type { NavIcon } from './navLink'
import AppBasic from './basic'
import AppInfo from './app-info'
import DatasetInfo from './dataset-info'
import AppSidebarDropdown from './app-sidebar-dropdown'
@ -15,31 +14,31 @@ import cn from '@/utils/classnames'
export type IAppDetailNavProps = {
iconType?: 'app' | 'dataset' | 'notion'
title: string
desc: string
isExternal?: boolean
icon: string
icon_background: string | null
navigation: Array<{
name: string
href: string
icon: NavIcon
selectedIcon: NavIcon
disabled?: boolean
}>
extraInfo?: (modeState: string) => React.ReactNode
}
const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigation, extraInfo, iconType = 'app' }: IAppDetailNavProps) => {
const { appSidebarExpand, setAppSiderbarExpand } = useAppStore(useShallow(state => ({
const AppDetailNav = ({
navigation,
extraInfo,
iconType = 'app',
}: IAppDetailNavProps) => {
const { appSidebarExpand, setAppSidebarExpand } = useAppStore(useShallow(state => ({
appSidebarExpand: state.appSidebarExpand,
setAppSiderbarExpand: state.setAppSiderbarExpand,
setAppSidebarExpand: state.setAppSidebarExpand,
})))
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
const expand = appSidebarExpand === 'expand'
const handleToggle = (state: string) => {
setAppSiderbarExpand(state === 'expand' ? 'collapse' : 'expand')
setAppSidebarExpand(state === 'expand' ? 'collapse' : 'expand')
}
// // Check if the current path is a workflow canvas & fullscreen
@ -57,9 +56,9 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
useEffect(() => {
if (appSidebarExpand) {
localStorage.setItem('app-detail-collapse-or-expand', appSidebarExpand)
setAppSiderbarExpand(appSidebarExpand)
setAppSidebarExpand(appSidebarExpand)
}
}, [appSidebarExpand, setAppSiderbarExpand])
}, [appSidebarExpand, setAppSidebarExpand])
if (inWorkflowCanvas && hideHeader) {
return (
@ -85,26 +84,12 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
{iconType === 'app' && (
<AppInfo expand={expand} />
)}
{iconType === 'dataset' && (
{iconType !== 'app' && (
<DatasetInfo
name={title}
description={desc}
isExternal={isExternal}
expand={expand}
extraInfo={extraInfo && extraInfo(appSidebarExpand)}
/>
)}
{!['app', 'dataset'].includes(iconType) && (
<AppBasic
mode={appSidebarExpand}
iconType={iconType}
icon={icon}
icon_background={icon_background}
name={title}
type={desc}
isExternal={isExternal}
/>
)}
</div>
<div className='px-4'>
<div className={cn('mx-auto mt-1 h-[1px] bg-divider-subtle', !expand && 'w-6')} />
@ -117,7 +102,14 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
>
{navigation.map((item, index) => {
return (
<NavLink key={index} mode={appSidebarExpand} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} />
<NavLink
key={index}
mode={appSidebarExpand}
iconMap={{ selected: item.selectedIcon, normal: item.icon }}
name={item.name}
href={item.href}
disabled={!!item.disabled}
/>
)
})}
</nav>

View File

@ -6,10 +6,10 @@ import classNames from '@/utils/classnames'
import type { RemixiconComponentType } from '@remixicon/react'
export type NavIcon = React.ComponentType<
React.PropsWithoutRef<React.ComponentProps<'svg'>> & {
title?: string | undefined
titleId?: string | undefined
}> | RemixiconComponentType
React.PropsWithoutRef<React.ComponentProps<'svg'>> & {
title?: string | undefined
titleId?: string | undefined
}> | RemixiconComponentType
export type NavLinkProps = {
name: string
@ -19,6 +19,7 @@ export type NavLinkProps = {
normal: NavIcon
}
mode?: string
disabled?: boolean
}
export default function NavLink({
@ -26,6 +27,7 @@ export default function NavLink({
href,
iconMap,
mode = 'expand',
disabled = false,
}: NavLinkProps) {
const segment = useSelectedLayoutSegment()
const formattedSegment = (() => {
@ -39,13 +41,38 @@ export default function NavLink({
const isActive = href.toLowerCase().split('/')?.pop() === formattedSegment
const NavIcon = isActive ? iconMap.selected : iconMap.normal
if (disabled) {
return (
<button
key={name}
type='button'
disabled
className={classNames(
'opacity-30 text-components-menu-item-text hover:bg-state-base-hover group flex items-center h-9 rounded-md py-2 system-sm-medium cursor-not-allowed',
mode === 'expand' ? 'px-3' : 'px-2.5',
)}
title={mode === 'collapse' ? name : ''}
aria-disabled
>
<NavIcon
className={classNames(
'h-4 w-4 flex-shrink-0',
mode === 'expand' ? 'mr-2' : 'mr-0',
)}
aria-hidden="true"
/>
{mode === 'expand' && name}
</button>
)
}
return (
<Link
key={name}
href={href}
className={classNames(
isActive ? 'bg-state-accent-active text-text-accent font-semibold' : 'text-components-menu-item-text hover:bg-state-base-hover hover:text-components-menu-item-text-hover',
'group flex items-center h-9 rounded-md py-2 text-sm font-normal',
'group flex items-center h-9 rounded-md py-2 system-sm-medium',
mode === 'expand' ? 'px-3' : 'px-2.5',
)}
title={mode === 'collapse' ? name : ''}

View File

@ -8,16 +8,13 @@ import {
import { useTranslation } from 'react-i18next'
import SettingsModal from '../settings-modal'
import type { DataSet } from '@/models/datasets'
import { DataSourceType } from '@/models/datasets'
import FileIcon from '@/app/components/base/file-icon'
import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
import { Globe06 } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import Drawer from '@/app/components/base/drawer'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Badge from '@/app/components/base/badge'
import { useKnowledge } from '@/hooks/use-knowledge'
import cn from '@/utils/classnames'
import AppIcon from '@/app/components/base/app-icon'
type ItemProps = {
className?: string
@ -47,33 +44,26 @@ const Item: FC<ItemProps> = ({
const [isDeleting, setIsDeleting] = useState(false)
const iconInfo = config.icon_info || {
icon: '📙',
icon_type: 'emoji',
icon_background: '#FFF4ED',
icon_url: '',
}
return (
<div className={cn(
'group relative mb-1 flex h-10 w-full cursor-pointer items-center justify-between rounded-lg border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg px-2 last-of-type:mb-0 hover:bg-components-panel-on-panel-item-bg-hover',
isDeleting && 'border-state-destructive-border hover:bg-state-destructive-hover',
)}>
<div className='flex w-0 grow items-center space-x-1.5'>
{
config.data_source_type === DataSourceType.FILE && (
<div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#E0EAFF] bg-[#F5F8FF]'>
<Folder className='h-4 w-4 text-[#444CE7]' />
</div>
)
}
{
config.data_source_type === DataSourceType.NOTION && (
<div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#EAECF5]'>
<FileIcon type='notion' className='h-4 w-4' />
</div>
)
}
{
config.data_source_type === DataSourceType.WEB && (
<div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-blue-100 bg-[#F5FAFF]'>
<Globe06 className='h-4 w-4 text-blue-600' />
</div>
)
}
<AppIcon
size='tiny'
iconType={iconInfo.icon_type}
icon={iconInfo.icon}
background={iconInfo.icon_type === 'image' ? undefined : iconInfo.icon_background}
imageUrl={iconInfo.icon_type === 'image' ? iconInfo.icon_url : undefined}
/>
<div className='system-sm-medium w-0 grow truncate text-text-secondary' title={config.name}>{config.name}</div>
</div>
<div className='ml-2 hidden shrink-0 items-center space-x-1 group-hover:flex'>

View File

@ -21,10 +21,12 @@ type Value = {
type WeightedScoreProps = {
value: Value
onChange: (value: Value) => void
readonly?: boolean
}
const WeightedScore = ({
value,
onChange = noop,
readonly = false,
}: WeightedScoreProps) => {
const { t } = useTranslation()
@ -37,8 +39,9 @@ const WeightedScore = ({
min={0}
step={0.1}
value={value.value[0]}
onChange={v => onChange({ value: [v, (10 - v * 10) / 10] })}
onChange={v => !readonly && onChange({ value: [v, (10 - v * 10) / 10] })}
trackClassName='weightedScoreSliderTrack'
disabled={readonly}
/>
<div className='mt-3 flex justify-between'>
<div className='system-xs-semibold-uppercase flex w-[90px] shrink-0 items-center text-util-colors-blue-light-blue-light-500'>

View File

@ -5,7 +5,6 @@ import { useGetState, useInfiniteScroll } from 'ahooks'
import { useTranslation } from 'react-i18next'
import Link from 'next/link'
import produce from 'immer'
import TypeIcon from '../type-icon'
import Modal from '@/app/components/base/modal'
import type { DataSet } from '@/models/datasets'
import Button from '@/app/components/base/button'
@ -15,6 +14,7 @@ import Badge from '@/app/components/base/badge'
import { useKnowledge } from '@/hooks/use-knowledge'
import cn from '@/utils/classnames'
import { basePath } from '@/utils/var'
import AppIcon from '@/app/components/base/app-icon'
export type ISelectDataSetProps = {
isShow: boolean
@ -91,6 +91,7 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
const handleSelect = () => {
onSelect(selected)
}
return (
<Modal
isShow={isShow}
@ -135,7 +136,13 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
>
<div className='mr-1 flex items-center overflow-hidden'>
<div className={cn('mr-2', !item.embedding_available && 'opacity-30')}>
<TypeIcon type="upload_file" size='md' />
<AppIcon
size='tiny'
iconType={item.icon_info.icon_type}
icon={item.icon_info.icon}
background={item.icon_info.icon_type === 'image' ? undefined : item.icon_info.icon_background}
imageUrl={item.icon_info.icon_type === 'image' ? item.icon_info.icon_url : undefined}
/>
</div>
<div className={cn('max-w-[200px] truncate text-[13px] font-medium text-text-secondary', !item.embedding_available && '!max-w-[120px] opacity-30')}>{item.name}</div>
{!item.embedding_available && (

View File

@ -6,7 +6,7 @@ import { isEqual } from 'lodash-es'
import { RiCloseLine } from '@remixicon/react'
import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/development'
import cn from '@/utils/classnames'
import IndexMethodRadio from '@/app/components/datasets/settings/index-method-radio'
import IndexMethod from '@/app/components/datasets/settings/index-method'
import Divider from '@/app/components/base/divider'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
@ -31,6 +31,7 @@ import {
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { fetchMembers } from '@/service/common'
import type { Member } from '@/models/common'
import { IndexingType } from '@/app/components/datasets/create/step-two'
import { useDocLink } from '@/context/i18n'
type SettingsModalProps = {
@ -55,8 +56,6 @@ const SettingsModal: FC<SettingsModalProps> = ({
const { data: embeddingsModelList } = useModelList(ModelTypeEnum.textEmbedding)
const {
modelList: rerankModelList,
defaultModel: rerankDefaultModel,
currentModel: isRerankDefaultModelValid,
} = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank)
const { t } = useTranslation()
const docLink = useDocLink()
@ -75,6 +74,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
const [indexMethod, setIndexMethod] = useState(currentDataset.indexing_technique)
const [retrievalConfig, setRetrievalConfig] = useState(localeCurrentDataset?.retrieval_model_dict as RetrievalConfig)
const [keywordNumber, setKeywordNumber] = useState(currentDataset.keyword_number ?? 10)
const handleValueChange = (type: string, value: string) => {
setLocaleCurrentDataset({ ...localeCurrentDataset, [type]: value })
@ -126,6 +126,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
description,
permission,
indexing_technique: indexMethod,
keyword_number: keywordNumber,
retrieval_model: {
...retrievalConfig,
score_threshold: retrievalConfig.score_threshold_enabled ? retrievalConfig.score_threshold : 0,
@ -247,17 +248,18 @@ const SettingsModal: FC<SettingsModalProps> = ({
<div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.indexMethod')}</div>
</div>
<div className='grow'>
<IndexMethodRadio
disable={!localeCurrentDataset?.embedding_available}
<IndexMethod
disabled={!localeCurrentDataset?.embedding_available}
value={indexMethod}
onChange={v => setIndexMethod(v!)}
docForm={currentDataset.doc_form}
onChange={setIndexMethod}
currentValue={currentDataset.indexing_technique}
keywordNumber={keywordNumber}
onKeywordNumberChange={setKeywordNumber}
/>
</div>
</div>
)}
{indexMethod === 'high_quality' && (
{indexMethod === IndexingType.QUALIFIED && (
<div className={cn(rowClass)}>
<div className={labelClass}>
<div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.embeddingModel')}</div>
@ -336,7 +338,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
</div>
</div>
<div>
{indexMethod === 'high_quality'
{indexMethod === IndexingType.QUALIFIED
? (
<RetrievalMethodConfig
value={retrievalConfig}

View File

@ -89,9 +89,9 @@ type PublishConfig = {
const Configuration: FC = () => {
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const { appDetail, showAppConfigureFeaturesModal, setAppSiderbarExpand, setShowAppConfigureFeaturesModal } = useAppStore(useShallow(state => ({
const { appDetail, showAppConfigureFeaturesModal, setAppSidebarExpand, setShowAppConfigureFeaturesModal } = useAppStore(useShallow(state => ({
appDetail: state.appDetail,
setAppSiderbarExpand: state.setAppSiderbarExpand,
setAppSidebarExpand: state.setAppSidebarExpand,
showAppConfigureFeaturesModal: state.showAppConfigureFeaturesModal,
setShowAppConfigureFeaturesModal: state.setShowAppConfigureFeaturesModal,
})))
@ -823,7 +823,7 @@ const Configuration: FC = () => {
{ id: `${Date.now()}-no-repeat`, model: '', provider: '', parameters: {} },
],
)
setAppSiderbarExpand('collapse')
setAppSidebarExpand('collapse')
}
if (isLoading) {

View File

@ -15,7 +15,7 @@ type State = {
type Action = {
setAppDetail: (appDetail?: App & Partial<AppSSO>) => void
setAppSiderbarExpand: (state: string) => void
setAppSidebarExpand: (state: string) => void
setCurrentLogItem: (item?: IChatItem) => void
setCurrentLogModalActiveTab: (tab: string) => void
setShowPromptLogModal: (showPromptLogModal: boolean) => void
@ -28,7 +28,7 @@ export const useStore = create<State & Action>(set => ({
appDetail: undefined,
setAppDetail: appDetail => set(() => ({ appDetail })),
appSidebarExpand: '',
setAppSiderbarExpand: appSidebarExpand => set(() => ({ appSidebarExpand })),
setAppSidebarExpand: appSidebarExpand => set(() => ({ appSidebarExpand })),
currentLogItem: undefined,
currentLogModalActiveTab: 'DETAIL',
setCurrentLogItem: currentLogItem => set(() => ({ currentLogItem })),

View File

@ -3,6 +3,7 @@ import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { RiCloseLine } from '@remixicon/react'
import Run from '@/app/components/workflow/run'
import { useStore } from '@/app/components/app/store'
type ILogDetail = {
runID: string
@ -11,6 +12,7 @@ type ILogDetail = {
const DetailPanel: FC<ILogDetail> = ({ runID, onClose }) => {
const { t } = useTranslation()
const appDetail = useStore(state => state.appDetail)
return (
<div className='relative flex grow flex-col pt-3'>
@ -18,7 +20,10 @@ const DetailPanel: FC<ILogDetail> = ({ runID, onClose }) => {
<RiCloseLine className='h-4 w-4 text-text-tertiary' />
</span>
<h1 className='system-xl-semibold shrink-0 px-4 py-1 text-text-primary'>{t('appLog.runDetail.workflowTitle')}</h1>
<Run runID={runID}/>
<Run
runDetailUrl={`/apps/${appDetail?.id}/workflow-runs/${runID}`}
tracingListUrl={`/apps/${appDetail?.id}/workflow-runs/${runID}/node-executions`}
/>
</div>
)
}

View File

@ -0,0 +1,76 @@
import { render, screen } from '@testing-library/react'
import { ActionButton, ActionButtonState } from './index'
describe('ActionButton', () => {
test('renders button with default props', () => {
render(<ActionButton>Click me</ActionButton>)
const button = screen.getByRole('button', { name: 'Click me' })
expect(button).toBeInTheDocument()
expect(button.classList.contains('action-btn')).toBe(true)
expect(button.classList.contains('action-btn-m')).toBe(true)
})
test('renders button with xs size', () => {
render(<ActionButton size='xs'>Small Button</ActionButton>)
const button = screen.getByRole('button', { name: 'Small Button' })
expect(button.classList.contains('action-btn-xs')).toBe(true)
})
test('renders button with l size', () => {
render(<ActionButton size='l'>Large Button</ActionButton>)
const button = screen.getByRole('button', { name: 'Large Button' })
expect(button.classList.contains('action-btn-l')).toBe(true)
})
test('renders button with xl size', () => {
render(<ActionButton size='xl'>Extra Large Button</ActionButton>)
const button = screen.getByRole('button', { name: 'Extra Large Button' })
expect(button.classList.contains('action-btn-xl')).toBe(true)
})
test('applies correct state classes', () => {
const { rerender } = render(
<ActionButton state={ActionButtonState.Destructive}>Destructive</ActionButton>,
)
let button = screen.getByRole('button', { name: 'Destructive' })
expect(button.classList.contains('action-btn-destructive')).toBe(true)
rerender(<ActionButton state={ActionButtonState.Active}>Active</ActionButton>)
button = screen.getByRole('button', { name: 'Active' })
expect(button.classList.contains('action-btn-active')).toBe(true)
rerender(<ActionButton state={ActionButtonState.Disabled}>Disabled</ActionButton>)
button = screen.getByRole('button', { name: 'Disabled' })
expect(button.classList.contains('action-btn-disabled')).toBe(true)
rerender(<ActionButton state={ActionButtonState.Hover}>Hover</ActionButton>)
button = screen.getByRole('button', { name: 'Hover' })
expect(button.classList.contains('action-btn-hover')).toBe(true)
})
test('applies custom className', () => {
render(<ActionButton className='custom-class'>Custom Class</ActionButton>)
const button = screen.getByRole('button', { name: 'Custom Class' })
expect(button.classList.contains('custom-class')).toBe(true)
})
test('applies custom style', () => {
render(
<ActionButton styleCss={{ color: 'red', backgroundColor: 'blue' }}>
Custom Style
</ActionButton>,
)
const button = screen.getByRole('button', { name: 'Custom Style' })
expect(button).toHaveStyle({
color: 'red',
backgroundColor: 'blue',
})
})
test('forwards additional button props', () => {
render(<ActionButton disabled data-testid='test-button'>Disabled Button</ActionButton>)
const button = screen.getByRole('button', { name: 'Disabled Button' })
expect(button).toBeDisabled()
expect(button).toHaveAttribute('data-testid', 'test-button')
})
})

View File

@ -5,7 +5,6 @@ import type { Area } from 'react-easy-crop'
import Modal from '../modal'
import Divider from '../divider'
import Button from '../button'
import { ImagePlus } from '../icons/src/vender/line/images'
import { useLocalFileUploader } from '../image-uploader/hooks'
import EmojiPickerInner from '../emoji-picker/Inner'
import type { OnImageInput } from './ImageInput'
@ -16,6 +15,7 @@ import type { AppIconType, ImageFile } from '@/types/app'
import cn from '@/utils/classnames'
import { DISABLE_UPLOAD_IMAGE_AS_ICON } from '@/config'
import { noop } from 'lodash-es'
import { RiImageCircleAiLine } from '@remixicon/react'
export type AppIconEmojiSelection = {
type: 'emoji'
@ -46,7 +46,7 @@ const AppIconPicker: FC<AppIconPickerProps> = ({
const tabs = [
{ key: 'emoji', label: t('app.iconPicker.emoji'), icon: <span className="text-lg">🤖</span> },
{ key: 'image', label: t('app.iconPicker.image'), icon: <ImagePlus /> },
{ key: 'image', label: t('app.iconPicker.image'), icon: <RiImageCircleAiLine className='size-4' /> },
]
const [activeTab, setActiveTab] = useState<AppIconType>('emoji')
@ -119,10 +119,10 @@ const AppIconPicker: FC<AppIconPickerProps> = ({
{tabs.map(tab => (
<button
key={tab.key}
className={`
flex h-8 flex-1 shrink-0 items-center justify-center rounded-xl p-2 text-sm font-medium
${activeTab === tab.key && 'bg-components-main-nav-nav-button-bg-active shadow-md'}
`}
className={cn(
'system-sm-medium flex h-8 flex-1 shrink-0 items-center justify-center rounded-xl p-2 text-text-tertiary',
activeTab === tab.key && 'bg-components-main-nav-nav-button-bg-active text-text-accent shadow-md',
)}
onClick={() => setActiveTab(tab.key as AppIconType)}
>
{tab.icon} &nbsp; {tab.label}

View File

@ -0,0 +1,159 @@
import { fireEvent, render, screen } from '@testing-library/react'
import '@testing-library/jest-dom'
import AppIcon from './index'
// Mock emoji-mart initialization
jest.mock('emoji-mart', () => ({
init: jest.fn(),
}))
// Mock emoji data
jest.mock('@emoji-mart/data', () => ({}))
// Mock the ahooks useHover hook
jest.mock('ahooks', () => ({
useHover: jest.fn(() => false),
}))
describe('AppIcon', () => {
beforeEach(() => {
// Mock custom element
if (!customElements.get('em-emoji')) {
customElements.define('em-emoji', class extends HTMLElement {
constructor() {
super()
}
// Mock basic functionality
connectedCallback() {
this.innerHTML = '🤖'
}
})
}
// Reset mocks
require('ahooks').useHover.mockReset().mockReturnValue(false)
})
it('renders default emoji when no icon or image is provided', () => {
render(<AppIcon />)
const emojiElement = document.querySelector('em-emoji')
expect(emojiElement).toBeInTheDocument()
expect(emojiElement?.getAttribute('id')).toBe('🤖')
})
it('renders with custom emoji when icon is provided', () => {
render(<AppIcon icon='smile' />)
const emojiElement = document.querySelector('em-emoji')
expect(emojiElement).toBeInTheDocument()
expect(emojiElement?.getAttribute('id')).toBe('smile')
})
it('renders image when iconType is image and imageUrl is provided', () => {
render(<AppIcon iconType='image' imageUrl='test-image.jpg' />)
const imgElement = screen.getByAltText('app icon')
expect(imgElement).toBeInTheDocument()
expect(imgElement).toHaveAttribute('src', 'test-image.jpg')
})
it('renders innerIcon when provided', () => {
render(<AppIcon innerIcon={<div data-testid='inner-icon'>Custom Icon</div>} />)
const innerIcon = screen.getByTestId('inner-icon')
expect(innerIcon).toBeInTheDocument()
})
it('applies size classes correctly', () => {
const { container: xsContainer } = render(<AppIcon size='xs' />)
expect(xsContainer.firstChild).toHaveClass('w-4 h-4 rounded-[4px]')
const { container: tinyContainer } = render(<AppIcon size='tiny' />)
expect(tinyContainer.firstChild).toHaveClass('w-6 h-6 rounded-md')
const { container: smallContainer } = render(<AppIcon size='small' />)
expect(smallContainer.firstChild).toHaveClass('w-8 h-8 rounded-lg')
const { container: mediumContainer } = render(<AppIcon size='medium' />)
expect(mediumContainer.firstChild).toHaveClass('w-9 h-9 rounded-[10px]')
const { container: largeContainer } = render(<AppIcon size='large' />)
expect(largeContainer.firstChild).toHaveClass('w-10 h-10 rounded-[10px]')
const { container: xlContainer } = render(<AppIcon size='xl' />)
expect(xlContainer.firstChild).toHaveClass('w-12 h-12 rounded-xl')
const { container: xxlContainer } = render(<AppIcon size='xxl' />)
expect(xxlContainer.firstChild).toHaveClass('w-14 h-14 rounded-2xl')
})
it('applies rounded class when rounded=true', () => {
const { container } = render(<AppIcon rounded />)
expect(container.firstChild).toHaveClass('rounded-full')
})
it('applies custom background color', () => {
const { container } = render(<AppIcon background='#FF5500' />)
expect(container.firstChild).toHaveStyle('background: #FF5500')
})
it('uses default background color when no background is provided for non-image icons', () => {
const { container } = render(<AppIcon />)
expect(container.firstChild).toHaveStyle('background: #FFEAD5')
})
it('does not apply background style for image icons', () => {
const { container } = render(<AppIcon iconType='image' imageUrl='test.jpg' background='#FF5500' />)
// Should not have the background style from the prop
expect(container.firstChild).not.toHaveStyle('background: #FF5500')
})
it('calls onClick handler when clicked', () => {
const handleClick = jest.fn()
const { container } = render(<AppIcon onClick={handleClick} />)
fireEvent.click(container.firstChild!)
expect(handleClick).toHaveBeenCalledTimes(1)
})
it('applies custom className', () => {
const { container } = render(<AppIcon className='custom-class' />)
expect(container.firstChild).toHaveClass('custom-class')
})
it('does not display edit icon when showEditIcon=false', () => {
render(<AppIcon />)
const editIcon = screen.queryByRole('svg')
expect(editIcon).not.toBeInTheDocument()
})
it('displays edit icon when showEditIcon=true and hovering', () => {
// Mock the useHover hook to return true for this test
require('ahooks').useHover.mockReturnValue(true)
render(<AppIcon showEditIcon />)
const editIcon = document.querySelector('svg')
expect(editIcon).toBeInTheDocument()
})
it('does not display edit icon when showEditIcon=true but not hovering', () => {
// useHover returns false by default from our mock setup
render(<AppIcon showEditIcon />)
const editIcon = document.querySelector('svg')
expect(editIcon).not.toBeInTheDocument()
})
it('handles conditional isValidImageIcon check correctly', () => {
// Case 1: Valid image icon
const { rerender } = render(
<AppIcon iconType='image' imageUrl='test.jpg' />,
)
expect(screen.getByAltText('app icon')).toBeInTheDocument()
// Case 2: Invalid - missing image URL
rerender(<AppIcon iconType='image' imageUrl={null} />)
expect(screen.queryByAltText('app icon')).not.toBeInTheDocument()
// Case 3: Invalid - wrong icon type
rerender(<AppIcon iconType='emoji' imageUrl='test.jpg' />)
expect(screen.queryByAltText('app icon')).not.toBeInTheDocument()
})
})

View File

@ -1,11 +1,12 @@
'use client'
import type { FC } from 'react'
import React, { type FC, useRef } from 'react'
import { init } from 'emoji-mart'
import data from '@emoji-mart/data'
import { cva } from 'class-variance-authority'
import type { AppIconType } from '@/types/app'
import classNames from '@/utils/classnames'
import { useHover } from 'ahooks'
import { RiEditLine } from '@remixicon/react'
init({ data })
@ -18,20 +19,21 @@ export type AppIconProps = {
imageUrl?: string | null
className?: string
innerIcon?: React.ReactNode
showEditIcon?: boolean
onClick?: () => void
}
const appIconVariants = cva(
'flex items-center justify-center relative text-lg rounded-lg grow-0 shrink-0 overflow-hidden leading-none',
'flex items-center justify-center relative grow-0 shrink-0 overflow-hidden leading-none border-[0.5px] border-divider-regular',
{
variants: {
size: {
xs: 'w-4 h-4 text-xs',
tiny: 'w-6 h-6 text-base',
small: 'w-8 h-8 text-xl',
medium: 'w-9 h-9 text-[22px]',
large: 'w-10 h-10 text-[24px]',
xl: 'w-12 h-12 text-[28px]',
xxl: 'w-14 h-14 text-[32px]',
xs: 'w-4 h-4 text-xs rounded-[4px]',
tiny: 'w-6 h-6 text-base rounded-md',
small: 'w-8 h-8 text-xl rounded-lg',
medium: 'w-9 h-9 text-[22px] rounded-[10px]',
large: 'w-10 h-10 text-[24px] rounded-[10px]',
xl: 'w-12 h-12 text-[28px] rounded-xl',
xxl: 'w-14 h-14 text-[32px] rounded-2xl',
},
rounded: {
true: 'rounded-full',
@ -42,6 +44,46 @@ const appIconVariants = cva(
rounded: false,
},
})
const EditIconWrapperVariants = cva(
'absolute left-0 top-0 z-10 flex items-center justify-center bg-background-overlay-alt',
{
variants: {
size: {
xs: 'w-4 h-4 rounded-[4px]',
tiny: 'w-6 h-6 rounded-md',
small: 'w-8 h-8 rounded-lg',
medium: 'w-9 h-9 rounded-[10px]',
large: 'w-10 h-10 rounded-[10px]',
xl: 'w-12 h-12 rounded-xl',
xxl: 'w-14 h-14 rounded-2xl',
},
rounded: {
true: 'rounded-full',
},
},
defaultVariants: {
size: 'medium',
rounded: false,
},
})
const EditIconVariants = cva(
'text-text-primary-on-surface',
{
variants: {
size: {
xs: 'size-3',
tiny: 'size-3.5',
small: 'size-5',
medium: 'size-[22px]',
large: 'size-6',
xl: 'size-7',
xxl: 'size-8',
},
},
defaultVariants: {
size: 'medium',
},
})
const AppIcon: FC<AppIconProps> = ({
size = 'medium',
rounded = false,
@ -52,20 +94,34 @@ const AppIcon: FC<AppIconProps> = ({
className,
innerIcon,
onClick,
showEditIcon = false,
}) => {
const isValidImageIcon = iconType === 'image' && imageUrl
const Icon = (icon && icon !== '') ? <em-emoji id={icon} /> : <em-emoji id='🤖' />
const wrapperRef = useRef<HTMLSpanElement>(null)
const isHovering = useHover(wrapperRef)
return <span
className={classNames(appIconVariants({ size, rounded }), className)}
style={{ background: isValidImageIcon ? undefined : (background || '#FFEAD5') }}
onClick={onClick}
>
{isValidImageIcon
? <img src={imageUrl} className="h-full w-full" alt="app icon" />
: (innerIcon || ((icon && icon !== '') ? <em-emoji id={icon} /> : <em-emoji id='🤖' />))
}
</span>
return (
<span
ref={wrapperRef}
className={classNames(appIconVariants({ size, rounded }), className)}
style={{ background: isValidImageIcon ? undefined : (background || '#FFEAD5') }}
onClick={onClick}
>
{
isValidImageIcon
? <img src={imageUrl} className='h-full w-full' alt='app icon' />
: (innerIcon || Icon)
}
{
showEditIcon && isHovering && (
<div className={EditIconWrapperVariants({ size, rounded })}>
<RiEditLine className={EditIconVariants({ size })} />
</div>
)
}
</span>
)
}
export default AppIcon
export default React.memo(AppIcon)

View File

@ -10,8 +10,8 @@ type CornerLabelProps = {
const CornerLabel: React.FC<CornerLabelProps> = ({ label, className, labelClassName }) => {
return (
<div className={cn('group/corner-label inline-flex items-start', className)}>
<Corner className='h-5 w-[13px] text-background-section group-hover/corner-label:text-background-section-burn' />
<div className={cn('flex items-center gap-0.5 bg-background-section py-1 pr-2 group-hover/corner-label:bg-background-section-burn', labelClassName)}>
<Corner className='h-5 w-[13px] text-background-section-burn' />
<div className={cn('flex items-center gap-0.5 bg-background-section-burn py-1 pr-2', labelClassName)}>
<div className='system-2xs-medium-uppercase text-text-tertiary'>{label}</div>
</div>
</div>

View File

@ -0,0 +1,18 @@
import React from 'react'
import cn from '@/utils/classnames'
type EffectProps = {
className?: string
}
const Effect = ({
className,
}: EffectProps) => {
return (
<div
className={cn('absolute size-[112px] rounded-full bg-util-colors-blue-brand-blue-brand-500 blur-[80px]', className)}
/>
)
}
export default React.memo(Effect)

View File

@ -114,7 +114,7 @@ const FileUploaderInAttachment = ({
)
}
type FileUploaderInAttachmentWrapperProps = {
export type FileUploaderInAttachmentWrapperProps = {
value?: FileEntity[]
onChange: (files: FileEntity[]) => void
fileConfig: FileUpload

View File

@ -0,0 +1,41 @@
import cn from '@/utils/classnames'
import { useFieldContext } from '../..'
import type { CustomSelectProps, Option } from '../../../select/custom'
import CustomSelect from '../../../select/custom'
import type { LabelProps } from '../label'
import Label from '../label'
type CustomSelectFieldProps<T extends Option> = {
label: string
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
options: T[]
className?: string
} & Omit<CustomSelectProps<T>, 'options' | 'value' | 'onChange'>
const CustomSelectField = <T extends Option>({
label,
labelOptions,
options,
className,
...selectProps
}: CustomSelectFieldProps<T>) => {
const field = useFieldContext<string>()
return (
<div className={cn('flex flex-col gap-y-0.5', className)}>
<Label
htmlFor={field.name}
label={label}
{...(labelOptions ?? {})}
/>
<CustomSelect<T>
value={field.state.value}
options={options}
onChange={value => field.handleChange(value)}
{...selectProps}
/>
</div>
)
}
export default CustomSelectField

View File

@ -0,0 +1,83 @@
import cn from '@/utils/classnames'
import type { LabelProps } from '../label'
import { useFieldContext } from '../..'
import Label from '../label'
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
import FileTypeItem from '@/app/components/workflow/nodes/_base/components/file-type-item'
import { useCallback } from 'react'
type FieldValue = {
allowedFileTypes: string[],
allowedFileExtensions: string[]
}
type FileTypesFieldProps = {
label: string
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
className?: string
}
const FileTypesField = ({
label,
labelOptions,
className,
}: FileTypesFieldProps) => {
const field = useFieldContext<FieldValue>()
const handleSupportFileTypeChange = useCallback((type: SupportUploadFileTypes) => {
let newAllowFileTypes = [...field.state.value.allowedFileTypes]
if (type === SupportUploadFileTypes.custom) {
if (!newAllowFileTypes.includes(SupportUploadFileTypes.custom))
newAllowFileTypes = [SupportUploadFileTypes.custom]
else
newAllowFileTypes = newAllowFileTypes.filter(v => v !== type)
}
else {
newAllowFileTypes = newAllowFileTypes.filter(v => v !== SupportUploadFileTypes.custom)
if (newAllowFileTypes.includes(type))
newAllowFileTypes = newAllowFileTypes.filter(v => v !== type)
else
newAllowFileTypes.push(type)
}
field.handleChange({
...field.state.value,
allowedFileTypes: newAllowFileTypes,
})
}, [field])
const handleCustomFileTypesChange = useCallback((customFileTypes: string[]) => {
field.handleChange({
...field.state.value,
allowedFileExtensions: customFileTypes,
})
}, [field])
return (
<div className={cn('flex flex-col gap-y-0.5', className)}>
<Label
htmlFor={field.name}
label={label}
{...(labelOptions ?? {})}
/>
{
[SupportUploadFileTypes.document, SupportUploadFileTypes.image, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
<FileTypeItem
key={type}
type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
selected={field.state.value.allowedFileTypes.includes(type)}
onToggle={handleSupportFileTypeChange}
/>
))
}
<FileTypeItem
type={SupportUploadFileTypes.custom}
selected={field.state.value.allowedFileTypes.includes(SupportUploadFileTypes.custom)}
onToggle={handleSupportFileTypeChange}
customFileTypes={field.state.value.allowedFileExtensions}
onCustomFileTypesChange={handleCustomFileTypesChange}
/>
</div>
)
}
export default FileTypesField

View File

@ -0,0 +1,40 @@
import React from 'react'
import { useFieldContext } from '../..'
import type { LabelProps } from '../label'
import Label from '../label'
import cn from '@/utils/classnames'
import type { FileUploaderInAttachmentWrapperProps } from '../../../file-uploader/file-uploader-in-attachment'
import FileUploaderInAttachmentWrapper from '../../../file-uploader/file-uploader-in-attachment'
import type { FileEntity } from '../../../file-uploader/types'
type FileUploaderFieldProps = {
label: string
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
className?: string
} & Omit<FileUploaderInAttachmentWrapperProps, 'value' | 'onChange'>
const FileUploaderField = ({
label,
labelOptions,
className,
...inputProps
}: FileUploaderFieldProps) => {
const field = useFieldContext<FileEntity[]>()
return (
<div className={cn('flex flex-col gap-y-0.5', className)}>
<Label
htmlFor={field.name}
label={label}
{...(labelOptions ?? {})}
/>
<FileUploaderInAttachmentWrapper
value={field.state.value}
onChange={value => field.handleChange(value)}
{...inputProps}
/>
</div>
)
}
export default FileUploaderField

View File

@ -0,0 +1,52 @@
import { InputTypeEnum } from './types'
import { PipelineInputVarType } from '@/models/pipeline'
import { useTranslation } from 'react-i18next'
import {
RiAlignLeft,
RiCheckboxLine,
RiFileCopy2Line,
RiFileTextLine,
RiHashtag,
RiListCheck3,
RiTextSnippet,
} from '@remixicon/react'
const i18nFileTypeMap: Record<string, string> = {
'number': 'number',
'file': 'single-file',
'file-list': 'multi-files',
}
const INPUT_TYPE_ICON = {
[PipelineInputVarType.textInput]: RiTextSnippet,
[PipelineInputVarType.paragraph]: RiAlignLeft,
[PipelineInputVarType.number]: RiHashtag,
[PipelineInputVarType.select]: RiListCheck3,
[PipelineInputVarType.checkbox]: RiCheckboxLine,
[PipelineInputVarType.singleFile]: RiFileTextLine,
[PipelineInputVarType.multiFiles]: RiFileCopy2Line,
}
const DATA_TYPE = {
[PipelineInputVarType.textInput]: 'string',
[PipelineInputVarType.paragraph]: 'string',
[PipelineInputVarType.number]: 'number',
[PipelineInputVarType.select]: 'string',
[PipelineInputVarType.checkbox]: 'boolean',
[PipelineInputVarType.singleFile]: 'file',
[PipelineInputVarType.multiFiles]: 'array[file]',
}
export const useInputTypeOptions = (supportFile: boolean) => {
const { t } = useTranslation()
const options = supportFile ? InputTypeEnum.options : InputTypeEnum.exclude(['file', 'file-list']).options
return options.map((value) => {
return {
value,
label: t(`appDebug.variableConfig.${i18nFileTypeMap[value] || value}`),
Icon: INPUT_TYPE_ICON[value],
type: DATA_TYPE[value],
}
})
}

View File

@ -0,0 +1,64 @@
import cn from '@/utils/classnames'
import { useFieldContext } from '../../..'
import type { CustomSelectProps } from '../../../../select/custom'
import CustomSelect from '../../../../select/custom'
import type { LabelProps } from '../../label'
import Label from '../../label'
import { useCallback } from 'react'
import Trigger from './trigger'
import type { FileTypeSelectOption, InputType } from './types'
import { useInputTypeOptions } from './hooks'
import Option from './option'
type InputTypeSelectFieldProps = {
label: string
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
supportFile: boolean
className?: string
} & Omit<CustomSelectProps<FileTypeSelectOption>, 'options' | 'value' | 'onChange' | 'CustomTrigger' | 'CustomOption'>
const InputTypeSelectField = ({
label,
labelOptions,
supportFile,
className,
...customSelectProps
}: InputTypeSelectFieldProps) => {
const field = useFieldContext<InputType>()
const inputTypeOptions = useInputTypeOptions(supportFile)
const renderTrigger = useCallback((option: FileTypeSelectOption | undefined, open: boolean) => {
return <Trigger option={option} open={open} />
}, [])
const renderOption = useCallback((option: FileTypeSelectOption) => {
return <Option option={option} />
}, [])
return (
<div className={cn('flex flex-col gap-y-0.5', className)}>
<Label
htmlFor={field.name}
label={label}
{...(labelOptions ?? {})}
/>
<CustomSelect<FileTypeSelectOption>
value={field.state.value}
options={inputTypeOptions}
onChange={value => field.handleChange(value as InputType)}
triggerProps={{
className: 'gap-x-0.5',
}}
popupProps={{
className: 'w-[368px]',
wrapperClassName: 'z-[9999999]',
itemClassName: 'gap-x-1',
}}
CustomTrigger={renderTrigger}
CustomOption={renderOption}
{...customSelectProps}
/>
</div>
)
}
export default InputTypeSelectField

View File

@ -0,0 +1,21 @@
import React from 'react'
import type { FileTypeSelectOption } from './types'
import Badge from '@/app/components/base/badge'
type OptionProps = {
option: FileTypeSelectOption
}
const Option = ({
option,
}: OptionProps) => {
return (
<>
<option.Icon className='h-4 w-4 shrink-0 text-text-tertiary' />
<span className='grow px-1'>{option.label}</span>
<Badge text={option.type} uppercase={false} />
</>
)
}
export default React.memo(Option)

View File

@ -0,0 +1,42 @@
import React from 'react'
import Badge from '@/app/components/base/badge'
import cn from '@/utils/classnames'
import { RiArrowDownSLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import type { FileTypeSelectOption } from './types'
type TriggerProps = {
option: FileTypeSelectOption | undefined
open: boolean
}
const Trigger = ({
option,
open,
}: TriggerProps) => {
const { t } = useTranslation()
return (
<>
{option ? (
<>
<option.Icon className='h-4 w-4 shrink-0 text-text-tertiary' />
<span className='grow p-1'>{option.label}</span>
<div className='pr-0.5'>
<Badge text={option.type} uppercase={false} />
</div>
</>
) : (
<span className='grow p-1'>{t('common.placeholder.select')}</span>
)}
<RiArrowDownSLine
className={cn(
'h-4 w-4 shrink-0 text-text-quaternary group-hover:text-text-secondary',
open && 'text-text-secondary',
)}
/>
</>
)
}
export default React.memo(Trigger)

View File

@ -0,0 +1,21 @@
import type { RemixiconComponentType } from '@remixicon/react'
import { z } from 'zod'
export const InputTypeEnum = z.enum([
'text-input',
'paragraph',
'number',
'select',
'checkbox',
'file',
'file-list',
])
export type InputType = z.infer<typeof InputTypeEnum>
export type FileTypeSelectOption = {
value: InputType
label: string
Icon: RemixiconComponentType
type: string
}

View File

@ -0,0 +1,39 @@
import {
memo,
} from 'react'
import PromptEditor from '@/app/components/base/prompt-editor'
import cn from '@/utils/classnames'
import Placeholder from './placeholder'
type MixedVariableTextInputProps = {
editable?: boolean
value?: string
onChange?: (text: string) => void
}
const MixedVariableTextInput = ({
editable = true,
value = '',
onChange,
}: MixedVariableTextInputProps) => {
return (
<PromptEditor
wrapperClassName={cn(
'rounded-lg border border-transparent bg-components-input-bg-normal px-2 py-1',
'hover:border-components-input-border-hover hover:bg-components-input-bg-hover',
'focus-within:border-components-input-border-active focus-within:bg-components-input-bg-active focus-within:shadow-xs',
)}
className='caret:text-text-accent'
editable={editable}
value={value}
workflowVariableBlock={{
show: true,
variables: [],
workflowNodesMap: {},
}}
placeholder={<Placeholder />}
onChange={onChange}
/>
)
}
export default memo(MixedVariableTextInput)

View File

@ -0,0 +1,49 @@
import { useCallback } from 'react'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { FOCUS_COMMAND } from 'lexical'
import { $insertNodes } from 'lexical'
import { CustomTextNode } from '@/app/components/base/prompt-editor/plugins/custom-text/node'
import Badge from '@/app/components/base/badge'
const Placeholder = () => {
const [editor] = useLexicalComposerContext()
const handleInsert = useCallback((text: string) => {
editor.update(() => {
const textNode = new CustomTextNode(text)
$insertNodes([textNode])
})
editor.dispatchCommand(FOCUS_COMMAND, undefined as any)
}, [editor])
return (
<div
className='pointer-events-auto flex h-full w-full cursor-text items-center px-2'
onClick={(e) => {
e.stopPropagation()
handleInsert('')
}}
>
<div className='flex grow items-center'>
Type or press
<div className='system-kbd mx-0.5 flex h-4 w-4 items-center justify-center rounded bg-components-kbd-bg-gray text-text-placeholder'>/</div>
<div
className='system-sm-regular cursor-pointer text-components-input-text-placeholder underline decoration-dotted decoration-auto underline-offset-auto hover:text-text-tertiary'
onClick={((e) => {
e.stopPropagation()
handleInsert('/')
})}
>
insert variable
</div>
</div>
<Badge
className='shrink-0'
text='String'
uppercase={false}
/>
</div>
)
}
export default Placeholder

View File

@ -1,5 +1,6 @@
import React from 'react'
import { useFieldContext } from '../..'
import type { LabelProps } from '../label'
import Label from '../label'
import cn from '@/utils/classnames'
import type { InputNumberProps } from '../../../input-number'
@ -7,20 +8,14 @@ import { InputNumber } from '../../../input-number'
type TextFieldProps = {
label: string
isRequired?: boolean
showOptional?: boolean
tooltip?: string
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
className?: string
labelClassName?: string
} & Omit<InputNumberProps, 'id' | 'value' | 'onChange' | 'onBlur'>
const NumberInputField = ({
label,
isRequired,
showOptional,
tooltip,
labelOptions,
className,
labelClassName,
...inputProps
}: TextFieldProps) => {
const field = useFieldContext<number | undefined>()
@ -30,10 +25,7 @@ const NumberInputField = ({
<Label
htmlFor={field.name}
label={label}
isRequired={isRequired}
showOptional={showOptional}
tooltip={tooltip}
className={labelClassName}
{...(labelOptions ?? {})}
/>
<InputNumber
id={field.name}

View File

@ -0,0 +1,47 @@
import cn from '@/utils/classnames'
import type { LabelProps } from '../label'
import { useFieldContext } from '../..'
import Label from '../label'
import type { InputNumberWithSliderProps } from '@/app/components/workflow/nodes/_base/components/input-number-with-slider'
import InputNumberWithSlider from '@/app/components/workflow/nodes/_base/components/input-number-with-slider'
type NumberSliderFieldProps = {
label: string
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
description?: string
className?: string
} & Omit<InputNumberWithSliderProps, 'value' | 'onChange'>
const NumberSliderField = ({
label,
labelOptions,
description,
className,
...InputNumberWithSliderProps
}: NumberSliderFieldProps) => {
const field = useFieldContext<number>()
return (
<div className={cn('flex flex-col gap-y-0.5', className)}>
<div>
<Label
htmlFor={field.name}
label={label}
{...(labelOptions ?? {})}
/>
{description && (
<div className='body-xs-regular pb-0.5 text-text-tertiary'>
{description}
</div>
)}
</div>
<InputNumberWithSlider
value={field.state.value}
onChange={value => field.handleChange(value)}
{...InputNumberWithSliderProps}
/>
</div>
)
}
export default NumberSliderField

View File

@ -1,27 +1,29 @@
import cn from '@/utils/classnames'
import { useFieldContext } from '../..'
import type { LabelProps } from '../label'
import Label from '../label'
import type { Options } from '@/app/components/app/configuration/config-var/config-select'
import ConfigSelect from '@/app/components/app/configuration/config-var/config-select'
type OptionsFieldProps = {
label: string;
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
className?: string;
labelClassName?: string;
}
const OptionsField = ({
label,
className,
labelClassName,
labelOptions,
}: OptionsFieldProps) => {
const field = useFieldContext<string[]>()
const field = useFieldContext<Options>()
return (
<div className={cn('flex flex-col gap-y-0.5', className)}>
<Label
htmlFor={field.name}
label={label}
className={labelClassName}
{...(labelOptions ?? {})}
/>
<ConfigSelect
options={field.state.value}

View File

@ -1,31 +1,25 @@
import cn from '@/utils/classnames'
import { useFieldContext } from '../..'
import type { Option, PureSelectProps } from '../../../select/pure'
import PureSelect from '../../../select/pure'
import type { LabelProps } from '../label'
import Label from '../label'
type SelectOption = {
value: string
label: string
}
type SelectFieldProps = {
label: string
options: SelectOption[]
isRequired?: boolean
showOptional?: boolean
tooltip?: string
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
options: Option[]
onChange?: (value: string) => void
className?: string
labelClassName?: string
}
} & Omit<PureSelectProps, 'options' | 'value' | 'onChange'>
const SelectField = ({
label,
labelOptions,
options,
isRequired,
showOptional,
tooltip,
onChange,
className,
labelClassName,
...selectProps
}: SelectFieldProps) => {
const field = useFieldContext<string>()
@ -34,15 +28,13 @@ const SelectField = ({
<Label
htmlFor={field.name}
label={label}
isRequired={isRequired}
showOptional={showOptional}
tooltip={tooltip}
className={labelClassName}
{...(labelOptions ?? {})}
/>
<PureSelect
value={field.state.value}
options={options}
onChange={value => field.handleChange(value)}
{...selectProps}
/>
</div>
)

View File

@ -0,0 +1,41 @@
import React from 'react'
import { useFieldContext } from '../..'
import type { LabelProps } from '../label'
import Label from '../label'
import cn from '@/utils/classnames'
import type { TextareaProps } from '../../../textarea'
import Textarea from '../../../textarea'
type TextAreaFieldProps = {
label: string
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
className?: string
} & Omit<TextareaProps, 'className' | 'onChange' | 'onBlur' | 'value' | 'id'>
const TextAreaField = ({
label,
labelOptions,
className,
...inputProps
}: TextAreaFieldProps) => {
const field = useFieldContext<string>()
return (
<div className={cn('flex flex-col gap-y-0.5', className)}>
<Label
htmlFor={field.name}
label={label}
{...(labelOptions ?? {})}
/>
<Textarea
id={field.name}
value={field.state.value}
onChange={e => field.handleChange(e.target.value)}
onBlur={field.handleBlur}
{...inputProps}
/>
</div>
)
}
export default TextAreaField

View File

@ -1,25 +1,20 @@
import React from 'react'
import { useFieldContext } from '../..'
import Input, { type InputProps } from '../../../input'
import type { LabelProps } from '../label'
import Label from '../label'
import cn from '@/utils/classnames'
type TextFieldProps = {
label: string
isRequired?: boolean
showOptional?: boolean
tooltip?: string
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
className?: string
labelClassName?: string
} & Omit<InputProps, 'className' | 'onChange' | 'onBlur' | 'value' | 'id'>
const TextField = ({
label,
isRequired,
showOptional,
tooltip,
labelOptions,
className,
labelClassName,
...inputProps
}: TextFieldProps) => {
const field = useFieldContext<string>()
@ -29,10 +24,7 @@ const TextField = ({
<Label
htmlFor={field.name}
label={label}
isRequired={isRequired}
showOptional={showOptional}
tooltip={tooltip}
className={labelClassName}
{...(labelOptions ?? {})}
/>
<Input
id={field.name}

View File

@ -0,0 +1,58 @@
import cn from '@/utils/classnames'
import type { LabelProps } from '../label'
import { useFieldContext } from '../..'
import Label from '../label'
import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
import { useTranslation } from 'react-i18next'
import { TransferMethod } from '@/types/app'
import { useCallback } from 'react'
type UploadMethodFieldProps = {
label: string
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
className?: string
}
const UploadMethodField = ({
label,
labelOptions,
className,
}: UploadMethodFieldProps) => {
const { t } = useTranslation()
const field = useFieldContext<TransferMethod[]>()
const { value } = field.state
const handleUploadMethodChange = useCallback((method: TransferMethod) => {
field.handleChange(method === TransferMethod.all ? [TransferMethod.local_file, TransferMethod.remote_url] : [method])
}, [field])
return (
<div className={cn('flex flex-col gap-y-0.5', className)}>
<Label
htmlFor={field.name}
label={label}
{...(labelOptions ?? {})}
/>
<div className='grid grid-cols-3 gap-2'>
<OptionCard
title={t('appDebug.variableConfig.localUpload')}
selected={value.length === 1 && value.includes(TransferMethod.local_file)}
onSelect={handleUploadMethodChange.bind(null, TransferMethod.local_file)}
/>
<OptionCard
title="URL"
selected={value.length === 1 && value.includes(TransferMethod.remote_url)}
onSelect={handleUploadMethodChange.bind(null, TransferMethod.remote_url)}
/>
<OptionCard
title={t('appDebug.variableConfig.both')}
selected={value.includes(TransferMethod.local_file) && value.includes(TransferMethod.remote_url)}
onSelect={handleUploadMethodChange.bind(null, TransferMethod.all)}
/>
</div>
</div>
)
}
export default UploadMethodField

View File

@ -0,0 +1,86 @@
import type { ChangeEvent } from 'react'
import { useState } from 'react'
import { RiEditLine } from '@remixicon/react'
import cn from '@/utils/classnames'
import SegmentedControl from '@/app/components/base/segmented-control'
import { VariableX } from '@/app/components/base/icons/src/vender/workflow'
import Input from '@/app/components/base/input'
import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker'
import type { LabelProps } from '../label'
import Label from '../label'
type VariableOrConstantInputFieldProps = {
label: string
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
className?: string
}
const VariableOrConstantInputField = ({
className,
label,
labelOptions,
}: VariableOrConstantInputFieldProps) => {
const [variableType, setVariableType] = useState('variable')
const options = [
{
Icon: VariableX,
value: 'variable',
},
{
Icon: RiEditLine,
value: 'constant',
},
]
const handleVariableOrConstantChange = (value: string) => {
setVariableType(value)
}
const handleVariableValueChange = () => {
console.log('Variable value changed')
}
const handleConstantValueChange = (e: ChangeEvent<HTMLInputElement>) => {
console.log('Constant value changed:', e.target.value)
}
return (
<div className={cn('flex flex-col gap-y-0.5', className)}>
<Label
htmlFor={'variable-or-constant'}
label={label}
{...(labelOptions ?? {})}
/>
<div className='flex items-center'>
<SegmentedControl
className='mr-1 shrink-0'
value={variableType}
onChange={handleVariableOrConstantChange as any}
options={options as any}
/>
{
variableType === 'variable' && (
<VarReferencePicker
className='grow'
nodeId=''
readonly={false}
value={[]}
onChange={handleVariableValueChange}
/>
)
}
{
variableType === 'constant' && (
<Input
className='ml-1'
onChange={handleConstantValueChange}
/>
)
}
</div>
</div>
)
}
export default VariableOrConstantInputField

View File

@ -0,0 +1,41 @@
import cn from '@/utils/classnames'
import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker'
import type { LabelProps } from '../label'
import Label from '../label'
type VariableOrConstantInputFieldProps = {
label: string
labelOptions?: Omit<LabelProps, 'htmlFor' | 'label'>
className?: string
}
const VariableOrConstantInputField = ({
className,
label,
labelOptions,
}: VariableOrConstantInputFieldProps) => {
const handleVariableValueChange = () => {
console.log('Variable value changed')
}
return (
<div className={cn('flex flex-col gap-y-0.5', className)}>
<Label
htmlFor={'variable-or-constant'}
label={label}
{...(labelOptions ?? {})}
/>
<div className='flex items-center'>
<VarReferencePicker
className='grow'
nodeId=''
readonly={false}
value={[]}
onChange={handleVariableValueChange}
/>
</div>
</div>
)
}
export default VariableOrConstantInputField

View File

@ -0,0 +1,43 @@
import { useStore } from '@tanstack/react-form'
import type { FormType } from '../..'
import { useFormContext } from '../..'
import Button from '../../../button'
import { useTranslation } from 'react-i18next'
export type CustomActionsProps = {
form: FormType
isSubmitting: boolean
canSubmit: boolean
}
type ActionsProps = {
CustomActions?: (props: CustomActionsProps) => React.ReactNode | React.JSX.Element
}
const Actions = ({
CustomActions,
}: ActionsProps) => {
const { t } = useTranslation()
const form = useFormContext()
const [isSubmitting, canSubmit] = useStore(form.store, state => [
state.isSubmitting,
state.canSubmit,
])
if (CustomActions)
return CustomActions({ form, isSubmitting, canSubmit })
return (
<Button
variant='primary'
disabled={isSubmitting || !canSubmit}
loading={isSubmitting}
onClick={() => form.handleSubmit()}
>
{t('common.operation.submit')}
</Button>
)
}
export default Actions

View File

@ -1,25 +0,0 @@
import { useStore } from '@tanstack/react-form'
import { useFormContext } from '../..'
import Button, { type ButtonProps } from '../../../button'
type SubmitButtonProps = Omit<ButtonProps, 'disabled' | 'loading' | 'onClick'>
const SubmitButton = ({ ...buttonProps }: SubmitButtonProps) => {
const form = useFormContext()
const [isSubmitting, canSubmit] = useStore(form.store, state => [
state.isSubmitting,
state.canSubmit,
])
return (
<Button
disabled={isSubmitting || !canSubmit}
loading={isSubmitting}
onClick={() => form.handleSubmit()}
{...buttonProps}
/>
)
}
export default SubmitButton

View File

@ -0,0 +1,197 @@
import React from 'react'
import { type BaseConfiguration, BaseFieldType } from './types'
import { withForm } from '../..'
import { useStore } from '@tanstack/react-form'
type BaseFieldProps = {
initialData?: Record<string, any>
config: BaseConfiguration
}
const BaseField = ({
initialData,
config,
}: BaseFieldProps) => withForm({
defaultValues: initialData,
render: function Render({
form,
}) {
const {
type,
label,
placeholder,
variable,
tooltip,
showConditions,
max,
min,
options,
required,
showOptional,
popupProps,
allowedFileExtensions,
allowedFileTypes,
allowedFileUploadMethods,
maxLength,
unit,
} = config
const isAllConditionsMet = useStore(form.store, (state) => {
const fieldValues = state.values
if (!showConditions.length) return true
return showConditions.every((condition) => {
const { variable, value } = condition
const fieldValue = fieldValues[variable as keyof typeof fieldValues]
return fieldValue === value
})
})
if (!isAllConditionsMet)
return <></>
if (type === BaseFieldType.textInput) {
return (
<form.AppField
name={variable}
children={field => (
<field.TextField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
placeholder={placeholder}
/>
)}
/>
)
}
if (type === BaseFieldType.paragraph) {
return (
<form.AppField
name={variable}
children={field => (
<field.TextAreaField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
placeholder={placeholder}
/>
)}
/>
)
}
if (type === BaseFieldType.numberInput) {
return (
<form.AppField
name={variable}
children={field => (
<field.NumberInputField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
placeholder={placeholder}
max={max}
min={min}
unit={unit}
/>
)}
/>
)
}
if (type === BaseFieldType.checkbox) {
return (
<form.AppField
name={variable}
children={field => (
<field.CheckboxField
label={label}
/>
)}
/>
)
}
if (type === BaseFieldType.select) {
return (
<form.AppField
name={variable}
children={field => (
<field.SelectField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
options={options!}
popupProps={popupProps}
/>
)}
/>
)
}
if (type === BaseFieldType.file) {
return (
<form.AppField
name={variable}
children={field => (
<field.FileUploaderField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
fileConfig={{
allowed_file_extensions: allowedFileExtensions,
allowed_file_types: allowedFileTypes,
allowed_file_upload_methods: allowedFileUploadMethods,
number_limits: 1,
}}
/>
)}
/>
)
}
if (type === BaseFieldType.fileList) {
return (
<form.AppField
name={variable}
children={field => (
<field.FileUploaderField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
fileConfig={{
allowed_file_extensions: allowedFileExtensions,
allowed_file_types: allowedFileTypes,
allowed_file_upload_methods: allowedFileUploadMethods,
number_limits: maxLength,
}}
/>
)}
/>
)
}
return <></>
},
})
export default BaseField

View File

@ -0,0 +1,63 @@
import React, { useMemo } from 'react'
import { useAppForm } from '../..'
import BaseField from './field'
import type { BaseFormProps } from './types'
import { generateZodSchema } from './utils'
const BaseForm = ({
initialData,
configurations,
onSubmit,
CustomActions,
}: BaseFormProps) => {
const schema = useMemo(() => {
const schema = generateZodSchema(configurations)
return schema
}, [configurations])
const baseForm = useAppForm({
defaultValues: initialData,
validators: {
onChange: ({ value }) => {
const result = schema.safeParse(value)
if (!result.success) {
const issues = result.error.issues
const firstIssue = issues[0].message
return firstIssue
}
return undefined
},
},
onSubmit: ({ value }) => {
onSubmit(value)
},
})
return (
<form
className='w-full'
onSubmit={(e) => {
e.preventDefault()
e.stopPropagation()
baseForm.handleSubmit()
}}
>
<div className='flex flex-col gap-4 px-4 py-2'>
{configurations.map((config, index) => {
const FieldComponent = BaseField({
initialData,
config,
})
return <FieldComponent key={index} form={baseForm} />
})}
</div>
<baseForm.AppForm>
<baseForm.Actions
CustomActions={CustomActions}
/>
</baseForm.AppForm>
</form>
)
}
export default React.memo(BaseForm)

View File

@ -0,0 +1,61 @@
import type { TransferMethod } from '@/types/app'
import type { Option } from '../../../select/pure'
import type { CustomActionsProps } from '../../components/form/actions'
export enum BaseFieldType {
textInput = 'text-input',
paragraph = 'paragraph',
numberInput = 'number-input',
checkbox = 'checkbox',
select = 'select',
file = 'file',
fileList = 'file-list',
}
export type ShowCondition = {
variable: string
value: any
}
export type NumberConfiguration = {
max?: number
min?: number
unit?: string
}
export type SelectConfiguration = {
options: Option[] // Options for select field
popupProps?: {
wrapperClassName?: string
className?: string
itemClassName?: string
title?: string
}
}
export type FileConfiguration = {
allowedFileTypes: string[]
allowedFileExtensions: string[]
allowedFileUploadMethods: TransferMethod[]
}
export type BaseConfiguration = {
label: string
variable: string // Variable name
maxLength?: number // Max length for text input
placeholder?: string
required: boolean
showOptional?: boolean // show optional label
showConditions: ShowCondition[] // Show this field only when all conditions are met
type: BaseFieldType
tooltip?: string // Tooltip for this field
} & NumberConfiguration
& Partial<SelectConfiguration>
& Partial<FileConfiguration>
export type BaseFormProps = {
initialData?: Record<string, any>
configurations: BaseConfiguration[]
CustomActions?: (props: CustomActionsProps) => React.ReactNode
onSubmit: (value: Record<string, any>) => void
}

View File

@ -0,0 +1,57 @@
import type { ZodNumber, ZodSchema, ZodString } from 'zod'
import { z } from 'zod'
import { type BaseConfiguration, BaseFieldType } from './types'
export const generateZodSchema = (fields: BaseConfiguration[]) => {
const shape: Record<string, ZodSchema> = {}
fields.forEach((field) => {
let zodType
switch (field.type) {
case BaseFieldType.textInput:
case BaseFieldType.paragraph:
zodType = z.string()
break
case BaseFieldType.numberInput:
zodType = z.number()
break
case BaseFieldType.checkbox:
zodType = z.boolean()
break
case BaseFieldType.select:
zodType = z.string()
break
default:
zodType = z.any()
break
}
if (field.maxLength) {
if ([BaseFieldType.textInput, BaseFieldType.paragraph].includes(field.type))
zodType = (zodType as ZodString).max(field.maxLength, `${field.label} exceeds max length of ${field.maxLength}`)
}
if (field.min) {
if ([BaseFieldType.numberInput].includes(field.type))
zodType = (zodType as ZodNumber).min(field.min, `${field.label} must be at least ${field.min}`)
}
if (field.max) {
if ([BaseFieldType.numberInput].includes(field.type))
zodType = (zodType as ZodNumber).max(field.max, `${field.label} exceeds max value of ${field.max}`)
}
if (field.required) {
if ([BaseFieldType.textInput, BaseFieldType.paragraph].includes(field.type))
zodType = (zodType as ZodString).nonempty(`${field.label} is required`)
}
else {
zodType = zodType.optional()
}
shape[field.variable] = zodType
})
return z.object(shape)
}

View File

@ -24,7 +24,7 @@ const DemoForm = () => {
},
})
const name = useStore(form.store, state => state.values.name)
const name = useStore(form.store, state => state.values.name)
return (
<form
@ -59,7 +59,7 @@ const name = useStore(form.store, state => state.values.name)
)
}
<form.AppForm>
<form.SubmitButton>Submit</form.SubmitButton>
<form.Actions />
</form.AppForm>
</form>
)

View File

@ -0,0 +1,222 @@
import React from 'react'
import { type InputFieldConfiguration, InputFieldType } from './types'
import { withForm } from '../..'
import { useStore } from '@tanstack/react-form'
type InputFieldProps = {
initialData?: Record<string, any>
config: InputFieldConfiguration
}
const InputField = ({
initialData,
config,
}: InputFieldProps) => withForm({
defaultValues: initialData,
render: function Render({
form,
}) {
const {
type,
label,
placeholder,
variable,
tooltip,
showConditions,
max,
min,
required,
showOptional,
supportFile,
description,
options,
listeners,
popupProps,
} = config
const isAllConditionsMet = useStore(form.store, (state) => {
const fieldValues = state.values
return showConditions.every((condition) => {
const { variable, value } = condition
const fieldValue = fieldValues[variable as keyof typeof fieldValues]
return fieldValue === value
})
})
if (!isAllConditionsMet)
return <></>
if (type === InputFieldType.textInput) {
return (
<form.AppField
name={variable}
listeners={listeners}
children={field => (
<field.TextField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
placeholder={placeholder}
/>
)}
/>
)
}
if (type === InputFieldType.numberInput) {
return (
<form.AppField
name={variable}
children={field => (
<field.NumberInputField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
placeholder={placeholder}
max={max}
min={min}
/>
)}
/>
)
}
if (type === InputFieldType.numberSlider) {
return (
<form.AppField
name={variable}
children={field => (
<field.NumberSliderField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
description={description}
max={max}
min={min}
/>
)}
/>
)
}
if (type === InputFieldType.checkbox) {
return (
<form.AppField
name={variable}
children={field => (
<field.CheckboxField
label={label}
/>
)}
/>
)
}
if (type === InputFieldType.select) {
return (
<form.AppField
name={variable}
children={field => (
<field.SelectField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
options={options!}
popupProps={popupProps}
/>
)}
/>
)
}
if (type === InputFieldType.inputTypeSelect) {
return (
<form.AppField
name={variable}
listeners={listeners}
children={field => (
<field.InputTypeSelectField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
supportFile={!!supportFile}
/>
)}
/>
)
}
if (type === InputFieldType.uploadMethod) {
return (
<form.AppField
name={variable}
children={field => (
<field.UploadMethodField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
/>
)}
/>
)
}
if (type === InputFieldType.fileTypes) {
return (
<form.AppField
name={variable}
children={field => (
<field.FileTypesField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
/>
)}
/>
)
}
if (type === InputFieldType.options) {
return (
<form.AppField
name={variable}
children={field => (
<field.OptionsField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
/>
)}
/>
)
}
return <></>
},
})
export default InputField

View File

@ -0,0 +1,39 @@
import type { DeepKeys, FieldListeners } from '@tanstack/react-form'
import type { NumberConfiguration, SelectConfiguration, ShowCondition } from '../base/types'
export enum InputFieldType {
textInput = 'textInput',
numberInput = 'numberInput',
numberSlider = 'numberSlider',
checkbox = 'checkbox',
options = 'options',
select = 'select',
inputTypeSelect = 'inputTypeSelect',
uploadMethod = 'uploadMethod',
fileTypes = 'fileTypes',
}
export type InputTypeSelectConfiguration = {
supportFile: boolean
}
export type NumberSliderConfiguration = {
description: string
max?: number
min?: number
}
export type InputFieldConfiguration = {
label: string
variable: string // Variable name
maxLength?: number // Max length for text input
placeholder?: string
required: boolean
showOptional?: boolean // show optional label
showConditions: ShowCondition[] // Show this field only when all conditions are met
type: InputFieldType
tooltip?: string // Tooltip for this field
listeners?: FieldListeners<Record<string, any>, DeepKeys<Record<string, any>>> // Listener for this field
} & NumberConfiguration & Partial<InputTypeSelectConfiguration>
& Partial<NumberSliderConfiguration>
& Partial<SelectConfiguration>

View File

@ -0,0 +1,75 @@
import type { ZodSchema, ZodString } from 'zod'
import { z } from 'zod'
import { type InputFieldConfiguration, InputFieldType } from './types'
import { SupportedFileTypes, TransferMethod } from '@/app/components/rag-pipeline/components/input-field/editor/form/schema'
export const generateZodSchema = (fields: InputFieldConfiguration[]) => {
const shape: Record<string, ZodSchema> = {}
fields.forEach((field) => {
let zodType
switch (field.type) {
case InputFieldType.textInput:
zodType = z.string()
break
case InputFieldType.numberInput:
zodType = z.number()
break
case InputFieldType.numberSlider:
zodType = z.number()
break
case InputFieldType.checkbox:
zodType = z.boolean()
break
case InputFieldType.options:
zodType = z.array(z.string())
break
case InputFieldType.select:
zodType = z.string()
break
case InputFieldType.fileTypes:
zodType = z.object({
allowedFileExtensions: z.string().optional(),
allowedFileTypes: z.array(SupportedFileTypes),
})
break
case InputFieldType.inputTypeSelect:
zodType = z.string()
break
case InputFieldType.uploadMethod:
zodType = z.array(TransferMethod)
break
default:
zodType = z.any()
break
}
if (field.maxLength) {
if ([InputFieldType.textInput].includes(field.type))
zodType = (zodType as ZodString).max(field.maxLength, `${field.label} exceeds max length of ${field.maxLength}`)
}
if (field.min) {
if ([InputFieldType.numberInput].includes(field.type))
zodType = (zodType as ZodString).min(field.min, `${field.label} must be at least ${field.min}`)
}
if (field.max) {
if ([InputFieldType.numberInput].includes(field.type))
zodType = (zodType as ZodString).max(field.max, `${field.label} exceeds max value of ${field.max}`)
}
if (field.required) {
if ([InputFieldType.textInput].includes(field.type))
zodType = (zodType as ZodString).nonempty(`${field.label} is required`)
}
else {
zodType = zodType.optional()
}
shape[field.variable] = zodType
})
return z.object(shape)
}

View File

@ -0,0 +1,239 @@
import React from 'react'
import { type InputFieldConfiguration, InputFieldType } from './types'
import { withForm } from '../..'
import { useStore } from '@tanstack/react-form'
type InputFieldProps = {
initialData?: Record<string, any>
config: InputFieldConfiguration
}
const NodePanelField = ({
initialData,
config,
}: InputFieldProps) => withForm({
defaultValues: initialData,
render: function Render({
form,
}) {
const {
type,
label,
placeholder,
variable,
tooltip,
showConditions,
max,
min,
required,
showOptional,
supportFile,
description,
options,
listeners,
popupProps,
} = config
const isAllConditionsMet = useStore(form.store, (state) => {
const fieldValues = state.values
return showConditions.every((condition) => {
const { variable, value } = condition
const fieldValue = fieldValues[variable as keyof typeof fieldValues]
return fieldValue === value
})
})
if (!isAllConditionsMet)
return <></>
if (type === InputFieldType.textInput) {
return (
<form.AppField
name={variable}
children={field => (
<field.TextField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
placeholder={placeholder}
/>
)}
/>
)
}
if (type === InputFieldType.numberInput) {
return (
<form.AppField
name={variable}
children={field => (
<field.NumberInputField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
placeholder={placeholder}
max={max}
min={min}
/>
)}
/>
)
}
if (type === InputFieldType.numberSlider) {
return (
<form.AppField
name={variable}
children={field => (
<field.NumberSliderField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
description={description}
max={max}
min={min}
/>
)}
/>
)
}
if (type === InputFieldType.checkbox) {
return (
<form.AppField
name={variable}
children={field => (
<field.CheckboxField
label={label}
/>
)}
/>
)
}
if (type === InputFieldType.select) {
return (
<form.AppField
name={variable}
children={field => (
<field.SelectField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
options={options!}
popupProps={popupProps}
/>
)}
/>
)
}
if (type === InputFieldType.inputTypeSelect) {
return (
<form.AppField
name={variable}
listeners={listeners}
children={field => (
<field.InputTypeSelectField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
supportFile={!!supportFile}
/>
)}
/>
)
}
if (type === InputFieldType.uploadMethod) {
return (
<form.AppField
name={variable}
children={field => (
<field.UploadMethodField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
/>
)}
/>
)
}
if (type === InputFieldType.fileTypes) {
return (
<form.AppField
name={variable}
children={field => (
<field.FileTypesField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
/>
)}
/>
)
}
if (type === InputFieldType.options) {
return (
<form.AppField
name={variable}
children={field => (
<field.OptionsField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
/>
)}
/>
)
}
if (type === InputFieldType.variableOrConstant) {
return (
<form.AppField
name={variable}
children={field => (
<field.VariableOrConstantInputField
label={label}
labelOptions={{
tooltip,
isRequired: required,
showOptional,
}}
/>
)}
/>
)
}
return <></>
},
})
export default NodePanelField

View File

@ -0,0 +1,40 @@
import type { DeepKeys, FieldListeners } from '@tanstack/react-form'
import type { NumberConfiguration, SelectConfiguration, ShowCondition } from '../base/types'
export enum InputFieldType {
textInput = 'textInput',
numberInput = 'numberInput',
numberSlider = 'numberSlider',
checkbox = 'checkbox',
options = 'options',
select = 'select',
inputTypeSelect = 'inputTypeSelect',
uploadMethod = 'uploadMethod',
fileTypes = 'fileTypes',
variableOrConstant = 'variableOrConstant',
}
export type InputTypeSelectConfiguration = {
supportFile: boolean
}
export type NumberSliderConfiguration = {
description: string
max?: number
min?: number
}
export type InputFieldConfiguration = {
label: string
variable: string // Variable name
maxLength?: number // Max length for text input
placeholder?: string
required: boolean
showOptional?: boolean // show optional label
showConditions: ShowCondition[] // Show this field only when all conditions are met
type: InputFieldType
tooltip?: string // Tooltip for this field
listeners?: FieldListeners<Record<string, any>, DeepKeys<Record<string, any>>> // Listener for this field
} & NumberConfiguration & Partial<InputTypeSelectConfiguration>
& Partial<NumberSliderConfiguration>
& Partial<SelectConfiguration>

View File

@ -3,8 +3,16 @@ import TextField from './components/field/text'
import NumberInputField from './components/field/number-input'
import CheckboxField from './components/field/checkbox'
import SelectField from './components/field/select'
import CustomSelectField from './components/field/custom-select'
import OptionsField from './components/field/options'
import SubmitButton from './components/form/submit-button'
import Actions from './components/form/actions'
import InputTypeSelectField from './components/field/input-type-select'
import FileTypesField from './components/field/file-types'
import UploadMethodField from './components/field/upload-method'
import NumberSliderField from './components/field/number-slider'
import VariableOrConstantInputField from './components/field/variable-selector'
import TextAreaField from './components/field/text-area'
import FileUploaderField from './components/field/file-uploader'
export const { fieldContext, useFieldContext, formContext, useFormContext }
= createFormHookContexts()
@ -12,14 +20,24 @@ export const { fieldContext, useFieldContext, formContext, useFormContext }
export const { useAppForm, withForm } = createFormHook({
fieldComponents: {
TextField,
TextAreaField,
NumberInputField,
CheckboxField,
SelectField,
CustomSelectField,
OptionsField,
InputTypeSelectField,
FileTypesField,
UploadMethodField,
NumberSliderField,
VariableOrConstantInputField,
FileUploaderField,
},
formComponents: {
SubmitButton,
Actions,
},
fieldContext,
formContext,
})
export type FormType = ReturnType<typeof useFormContext>

View File

@ -0,0 +1,29 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5996 0.5C11.7113 0.5 12.5755 0.500118 13.2676 0.556641C13.9655 0.61366 14.5329 0.730378 15.043 0.990234L15.3525 1.16406C16.0576 1.59644 16.6322 2.21605 17.0098 2.95703L17.1006 3.15137C17.2979 3.61108 17.3935 4.12184 17.4434 4.73242C17.4999 5.42447 17.5 6.28869 17.5 7.40039V10.5996C17.5 11.7113 17.4999 12.5755 17.4434 13.2676C17.3935 13.8782 17.2979 14.3889 17.1006 14.8486L17.0098 15.043C16.6322 15.7839 16.0576 16.4036 15.3525 16.8359L15.043 17.0098C14.5329 17.2696 13.9655 17.3863 13.2676 17.4434C12.5755 17.4999 11.7113 17.5 10.5996 17.5H7.40039C6.28869 17.5 5.42447 17.4999 4.73242 17.4434C4.12184 17.3935 3.61108 17.2979 3.15137 17.1006L2.95703 17.0098C2.21605 16.6322 1.59644 16.0576 1.16406 15.3525L0.990234 15.043C0.730378 14.5329 0.61366 13.9655 0.556641 13.2676C0.500118 12.5755 0.5 11.7113 0.5 10.5996V7.40039C0.5 6.28869 0.500118 5.42447 0.556641 4.73242C0.61366 4.03453 0.730378 3.46707 0.990234 2.95703L1.16406 2.64746C1.59644 1.94243 2.21605 1.36778 2.95703 0.990234L3.15137 0.899414C3.61108 0.702129 4.12184 0.606527 4.73242 0.556641C5.42447 0.500118 6.28869 0.5 7.40039 0.5H10.5996Z" stroke="white"/>
<path d="M1 7.4C1 5.15979 1 4.03969 1.43597 3.18404C1.81947 2.43139 2.43139 1.81947 3.18404 1.43597C4.03969 1 5.15979 1 7.4 1H10.6C12.8402 1 13.9603 1 14.816 1.43597C15.5686 1.81947 16.1805 2.43139 16.564 3.18404C17 4.03969 17 5.15979 17 7.4V10.6C17 12.8402 17 13.9603 16.564 14.816C16.1805 15.5686 15.5686 16.1805 14.816 16.564C13.9603 17 12.8402 17 10.6 17H7.4C5.15979 17 4.03969 17 3.18404 16.564C2.43139 16.1805 1.81947 15.5686 1.43597 14.816C1 13.9603 1 12.8402 1 10.6V7.4Z" fill="#828DAD"/>
<path d="M1 7.4C1 5.15979 1 4.03969 1.43597 3.18404C1.81947 2.43139 2.43139 1.81947 3.18404 1.43597C4.03969 1 5.15979 1 7.4 1H10.6C12.8402 1 13.9603 1 14.816 1.43597C15.5686 1.81947 16.1805 2.43139 16.564 3.18404C17 4.03969 17 5.15979 17 7.4V10.6C17 12.8402 17 13.9603 16.564 14.816C16.1805 15.5686 15.5686 16.1805 14.816 16.564C13.9603 17 12.8402 17 10.6 17H7.4C5.15979 17 4.03969 17 3.18404 16.564C2.43139 16.1805 1.81947 15.5686 1.43597 14.816C1 13.9603 1 12.8402 1 10.6V7.4Z" fill="url(#paint0_linear_5617_78238)"/>
<path d="M7.40039 1.5H10.5996C11.728 1.5 12.5446 1.50029 13.1865 1.55273C13.7434 1.59824 14.1352 1.68127 14.4561 1.81934L14.5889 1.88184C15.1651 2.17543 15.6471 2.62172 15.9834 3.16992L16.1182 3.41113C16.2942 3.75672 16.3953 4.17741 16.4473 4.81348C16.4997 5.4554 16.5 6.27204 16.5 7.40039V10.5996C16.5 11.728 16.4997 12.5446 16.4473 13.1865C16.4018 13.7434 16.3187 14.1352 16.1807 14.4561L16.1182 14.5889C15.8246 15.1651 15.3783 15.6471 14.8301 15.9834L14.5889 16.1182C14.2433 16.2942 13.8226 16.3953 13.1865 16.4473C12.5446 16.4997 11.728 16.5 10.5996 16.5H7.40039C6.27204 16.5 5.4554 16.4997 4.81348 16.4473C4.2566 16.4018 3.8648 16.3187 3.54395 16.1807L3.41113 16.1182C2.83494 15.8246 2.35287 15.3783 2.0166 14.8301L1.88184 14.5889C1.70575 14.2433 1.60471 13.8226 1.55273 13.1865C1.50029 12.5446 1.5 11.728 1.5 10.5996V7.40039C1.5 6.27204 1.50029 5.4554 1.55273 4.81348C1.59824 4.2566 1.68127 3.8648 1.81934 3.54395L1.88184 3.41113C2.17543 2.83494 2.62172 2.35287 3.16992 2.0166L3.41113 1.88184C3.75672 1.70575 4.17741 1.60471 4.81348 1.55273C5.4554 1.50029 6.27204 1.5 7.40039 1.5Z" stroke="#101828" stroke-opacity="0.08"/>
<g filter="url(#filter0_d_5617_78238)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 5.5C7.067 5.5 5.5 7.067 5.5 9C5.5 10.933 7.067 12.5 9 12.5C10.2949 12.5 11.4261 11.7971 12.032 10.7496C12.1703 10.5106 12.4762 10.4289 12.7152 10.5672C12.9542 10.7055 13.0359 11.0113 12.8977 11.2504C12.1204 12.5941 10.6662 13.5 9 13.5C6.68372 13.5 4.77619 11.75 4.52746 9.5H4C3.72386 9.5 3.5 9.27614 3.5 9C3.5 8.72386 3.72386 8.5 4 8.5H4.52746C4.77619 6.25002 6.68372 4.5 9 4.5C10.6662 4.5 12.1204 5.40588 12.8977 6.74964C13.0359 6.98867 12.9542 7.29454 12.7152 7.43281C12.4762 7.57107 12.1703 7.48939 12.032 7.25036C11.4261 6.20291 10.2949 5.5 9 5.5ZM9 7.5C8.17157 7.5 7.5 8.17157 7.5 9C7.5 9.82841 8.17158 10.5 9 10.5C9.82841 10.5 10.5 9.82841 10.5 9C10.5 8.17158 9.82841 7.5 9 7.5ZM6.5 9C6.5 7.61929 7.61929 6.5 9 6.5C10.2095 6.5 11.2184 7.35888 11.45 8.5H14C14.2761 8.5 14.5 8.72386 14.5 9C14.5 9.27614 14.2761 9.5 14 9.5H11.45C11.2183 10.6411 10.2095 11.5 9 11.5C7.61928 11.5 6.5 10.3807 6.5 9Z" fill="url(#paint1_linear_5617_78238)" shape-rendering="crispEdges"/>
</g>
<defs>
<filter id="filter0_d_5617_78238" x="3" y="4.25" width="12" height="10" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.25"/>
<feGaussianBlur stdDeviation="0.25"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_5617_78238"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5617_78238" result="shape"/>
</filter>
<linearGradient id="paint0_linear_5617_78238" x1="1" y1="1" x2="17" y2="17" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.12"/>
<stop offset="1" stop-color="white" stop-opacity="0.08"/>
</linearGradient>
<linearGradient id="paint1_linear_5617_78238" x1="9" y1="4.5" x2="9" y2="13.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -0,0 +1,54 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5996 0.5C11.7113 0.5 12.5755 0.500118 13.2676 0.556641C13.9655 0.61366 14.5329 0.730378 15.043 0.990234L15.3525 1.16406C16.0576 1.59644 16.6322 2.21605 17.0098 2.95703L17.1006 3.15137C17.2979 3.61108 17.3935 4.12184 17.4434 4.73242C17.4999 5.42447 17.5 6.28869 17.5 7.40039V10.5996C17.5 11.7113 17.4999 12.5755 17.4434 13.2676C17.3935 13.8782 17.2979 14.3889 17.1006 14.8486L17.0098 15.043C16.6322 15.7839 16.0576 16.4036 15.3525 16.8359L15.043 17.0098C14.5329 17.2696 13.9655 17.3863 13.2676 17.4434C12.5755 17.4999 11.7113 17.5 10.5996 17.5H7.40039C6.28869 17.5 5.42447 17.4999 4.73242 17.4434C4.12184 17.3935 3.61108 17.2979 3.15137 17.1006L2.95703 17.0098C2.21605 16.6322 1.59644 16.0576 1.16406 15.3525L0.990234 15.043C0.730378 14.5329 0.61366 13.9655 0.556641 13.2676C0.500118 12.5755 0.5 11.7113 0.5 10.5996V7.40039C0.5 6.28869 0.500118 5.42447 0.556641 4.73242C0.61366 4.03453 0.730378 3.46707 0.990234 2.95703L1.16406 2.64746C1.59644 1.94243 2.21605 1.36778 2.95703 0.990234L3.15137 0.899414C3.61108 0.702129 4.12184 0.606527 4.73242 0.556641C5.42447 0.500118 6.28869 0.5 7.40039 0.5H10.5996Z" stroke="white"/>
<path d="M1 7.4C1 5.15979 1 4.03969 1.43597 3.18404C1.81947 2.43139 2.43139 1.81947 3.18404 1.43597C4.03969 1 5.15979 1 7.4 1H10.6C12.8402 1 13.9603 1 14.816 1.43597C15.5686 1.81947 16.1805 2.43139 16.564 3.18404C17 4.03969 17 5.15979 17 7.4V10.6C17 12.8402 17 13.9603 16.564 14.816C16.1805 15.5686 15.5686 16.1805 14.816 16.564C13.9603 17 12.8402 17 10.6 17H7.4C5.15979 17 4.03969 17 3.18404 16.564C2.43139 16.1805 1.81947 15.5686 1.43597 14.816C1 13.9603 1 12.8402 1 10.6V7.4Z" fill="#444CE7"/>
<path d="M1 7.4C1 5.15979 1 4.03969 1.43597 3.18404C1.81947 2.43139 2.43139 1.81947 3.18404 1.43597C4.03969 1 5.15979 1 7.4 1H10.6C12.8402 1 13.9603 1 14.816 1.43597C15.5686 1.81947 16.1805 2.43139 16.564 3.18404C17 4.03969 17 5.15979 17 7.4V10.6C17 12.8402 17 13.9603 16.564 14.816C16.1805 15.5686 15.5686 16.1805 14.816 16.564C13.9603 17 12.8402 17 10.6 17H7.4C5.15979 17 4.03969 17 3.18404 16.564C2.43139 16.1805 1.81947 15.5686 1.43597 14.816C1 13.9603 1 12.8402 1 10.6V7.4Z" fill="url(#paint0_linear_5617_78288)"/>
<path d="M7.40039 1.5H10.5996C11.728 1.5 12.5446 1.50029 13.1865 1.55273C13.7434 1.59824 14.1352 1.68127 14.4561 1.81934L14.5889 1.88184C15.1651 2.17543 15.6471 2.62172 15.9834 3.16992L16.1182 3.41113C16.2942 3.75672 16.3953 4.17741 16.4473 4.81348C16.4997 5.4554 16.5 6.27204 16.5 7.40039V10.5996C16.5 11.728 16.4997 12.5446 16.4473 13.1865C16.4018 13.7434 16.3187 14.1352 16.1807 14.4561L16.1182 14.5889C15.8246 15.1651 15.3783 15.6471 14.8301 15.9834L14.5889 16.1182C14.2433 16.2942 13.8226 16.3953 13.1865 16.4473C12.5446 16.4997 11.728 16.5 10.5996 16.5H7.40039C6.27204 16.5 5.4554 16.4997 4.81348 16.4473C4.2566 16.4018 3.8648 16.3187 3.54395 16.1807L3.41113 16.1182C2.83494 15.8246 2.35287 15.3783 2.0166 14.8301L1.88184 14.5889C1.70575 14.2433 1.60471 13.8226 1.55273 13.1865C1.50029 12.5446 1.5 11.728 1.5 10.5996V7.40039C1.5 6.27204 1.50029 5.4554 1.55273 4.81348C1.59824 4.2566 1.68127 3.8648 1.81934 3.54395L1.88184 3.41113C2.17543 2.83494 2.62172 2.35287 3.16992 2.0166L3.41113 1.88184C3.75672 1.70575 4.17741 1.60471 4.81348 1.55273C5.4554 1.50029 6.27204 1.5 7.40039 1.5Z" stroke="#101828" stroke-opacity="0.08"/>
<g filter="url(#filter0_d_5617_78288)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.25 5.5C11.25 5.08579 11.5858 4.75 12 4.75H13.5C13.9142 4.75 14.25 5.08579 14.25 5.5C14.25 5.91421 13.9142 6.25 13.5 6.25H12C11.5858 6.25 11.25 5.91421 11.25 5.5Z" fill="url(#paint1_linear_5617_78288)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 5.5C3.75 5.08579 4.08579 4.75 4.5 4.75H9.5C9.91421 4.75 10.25 5.08579 10.25 5.5C10.25 5.91421 9.91421 6.25 9.5 6.25H4.5C4.08579 6.25 3.75 5.91421 3.75 5.5Z" fill="url(#paint2_linear_5617_78288)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 9C3.75 8.58579 4.08579 8.25 4.5 8.25H6C6.41421 8.25 6.75 8.58579 6.75 9C6.75 9.41421 6.41421 9.75 6 9.75H4.5C4.08579 9.75 3.75 9.41421 3.75 9Z" fill="url(#paint3_linear_5617_78288)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.75 9C7.75 8.58579 8.08579 8.25 8.5 8.25H13.5C13.9142 8.25 14.25 8.58579 14.25 9C14.25 9.41421 13.9142 9.75 13.5 9.75H8.5C8.08579 9.75 7.75 9.41421 7.75 9Z" fill="url(#paint4_linear_5617_78288)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.25 12.5C11.25 12.0858 11.5858 11.75 12 11.75H13.5C13.9142 11.75 14.25 12.0858 14.25 12.5C14.25 12.9142 13.9142 13.25 13.5 13.25H12C11.5858 13.25 11.25 12.9142 11.25 12.5Z" fill="url(#paint5_linear_5617_78288)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.75 12.5C3.75 12.0858 4.08579 11.75 4.5 11.75H9.5C9.91421 11.75 10.25 12.0858 10.25 12.5C10.25 12.9142 9.91421 13.25 9.5 13.25H4.5C4.08579 13.25 3.75 12.9142 3.75 12.5Z" fill="url(#paint6_linear_5617_78288)" shape-rendering="crispEdges"/>
</g>
<defs>
<filter id="filter0_d_5617_78288" x="3.25" y="4.5" width="11.5" height="9.5" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.25"/>
<feGaussianBlur stdDeviation="0.25"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_5617_78288"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5617_78288" result="shape"/>
</filter>
<linearGradient id="paint0_linear_5617_78288" x1="1" y1="1" x2="17" y2="17" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.12"/>
<stop offset="1" stop-color="white" stop-opacity="0.08"/>
</linearGradient>
<linearGradient id="paint1_linear_5617_78288" x1="9" y1="4.75" x2="9" y2="13.25" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint2_linear_5617_78288" x1="9" y1="4.75" x2="9" y2="13.25" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint3_linear_5617_78288" x1="9" y1="4.75" x2="9" y2="13.25" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint4_linear_5617_78288" x1="9" y1="4.75" x2="9" y2="13.25" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint5_linear_5617_78288" x1="9" y1="4.75" x2="9" y2="13.25" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint6_linear_5617_78288" x1="9" y1="4.75" x2="9" y2="13.25" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,124 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5996 0.5C11.7113 0.5 12.5755 0.500118 13.2676 0.556641C13.9655 0.61366 14.5329 0.730378 15.043 0.990234L15.3525 1.16406C16.0576 1.59644 16.6322 2.21605 17.0098 2.95703L17.1006 3.15137C17.2979 3.61108 17.3935 4.12184 17.4434 4.73242C17.4999 5.42447 17.5 6.28869 17.5 7.40039V10.5996C17.5 11.7113 17.4999 12.5755 17.4434 13.2676C17.3935 13.8782 17.2979 14.3889 17.1006 14.8486L17.0098 15.043C16.6322 15.7839 16.0576 16.4036 15.3525 16.8359L15.043 17.0098C14.5329 17.2696 13.9655 17.3863 13.2676 17.4434C12.5755 17.4999 11.7113 17.5 10.5996 17.5H7.40039C6.28869 17.5 5.42447 17.4999 4.73242 17.4434C4.12184 17.3935 3.61108 17.2979 3.15137 17.1006L2.95703 17.0098C2.21605 16.6322 1.59644 16.0576 1.16406 15.3525L0.990234 15.043C0.730378 14.5329 0.61366 13.9655 0.556641 13.2676C0.500118 12.5755 0.5 11.7113 0.5 10.5996V7.40039C0.5 6.28869 0.500118 5.42447 0.556641 4.73242C0.61366 4.03453 0.730378 3.46707 0.990234 2.95703L1.16406 2.64746C1.59644 1.94243 2.21605 1.36778 2.95703 0.990234L3.15137 0.899414C3.61108 0.702129 4.12184 0.606527 4.73242 0.556641C5.42447 0.500118 6.28869 0.5 7.40039 0.5H10.5996Z" stroke="white"/>
<path d="M1 7.4C1 5.15979 1 4.03969 1.43597 3.18404C1.81947 2.43139 2.43139 1.81947 3.18404 1.43597C4.03969 1 5.15979 1 7.4 1H10.6C12.8402 1 13.9603 1 14.816 1.43597C15.5686 1.81947 16.1805 2.43139 16.564 3.18404C17 4.03969 17 5.15979 17 7.4V10.6C17 12.8402 17 13.9603 16.564 14.816C16.1805 15.5686 15.5686 16.1805 14.816 16.564C13.9603 17 12.8402 17 10.6 17H7.4C5.15979 17 4.03969 17 3.18404 16.564C2.43139 16.1805 1.81947 15.5686 1.43597 14.816C1 13.9603 1 12.8402 1 10.6V7.4Z" fill="#7839EE"/>
<path d="M1 7.4C1 5.15979 1 4.03969 1.43597 3.18404C1.81947 2.43139 2.43139 1.81947 3.18404 1.43597C4.03969 1 5.15979 1 7.4 1H10.6C12.8402 1 13.9603 1 14.816 1.43597C15.5686 1.81947 16.1805 2.43139 16.564 3.18404C17 4.03969 17 5.15979 17 7.4V10.6C17 12.8402 17 13.9603 16.564 14.816C16.1805 15.5686 15.5686 16.1805 14.816 16.564C13.9603 17 12.8402 17 10.6 17H7.4C5.15979 17 4.03969 17 3.18404 16.564C2.43139 16.1805 1.81947 15.5686 1.43597 14.816C1 13.9603 1 12.8402 1 10.6V7.4Z" fill="url(#paint0_linear_5617_78246)"/>
<path d="M7.40039 1.5H10.5996C11.728 1.5 12.5446 1.50029 13.1865 1.55273C13.7434 1.59824 14.1352 1.68127 14.4561 1.81934L14.5889 1.88184C15.1651 2.17543 15.6471 2.62172 15.9834 3.16992L16.1182 3.41113C16.2942 3.75672 16.3953 4.17741 16.4473 4.81348C16.4997 5.4554 16.5 6.27204 16.5 7.40039V10.5996C16.5 11.728 16.4997 12.5446 16.4473 13.1865C16.4018 13.7434 16.3187 14.1352 16.1807 14.4561L16.1182 14.5889C15.8246 15.1651 15.3783 15.6471 14.8301 15.9834L14.5889 16.1182C14.2433 16.2942 13.8226 16.3953 13.1865 16.4473C12.5446 16.4997 11.728 16.5 10.5996 16.5H7.40039C6.27204 16.5 5.4554 16.4997 4.81348 16.4473C4.2566 16.4018 3.8648 16.3187 3.54395 16.1807L3.41113 16.1182C2.83494 15.8246 2.35287 15.3783 2.0166 14.8301L1.88184 14.5889C1.70575 14.2433 1.60471 13.8226 1.55273 13.1865C1.50029 12.5446 1.5 11.728 1.5 10.5996V7.40039C1.5 6.27204 1.50029 5.4554 1.55273 4.81348C1.59824 4.2566 1.68127 3.8648 1.81934 3.54395L1.88184 3.41113C2.17543 2.83494 2.62172 2.35287 3.16992 2.0166L3.41113 1.88184C3.75672 1.70575 4.17741 1.60471 4.81348 1.55273C5.4554 1.50029 6.27204 1.5 7.40039 1.5Z" stroke="#101828" stroke-opacity="0.08"/>
<g clip-path="url(#clip0_5617_78246)">
<g filter="url(#filter0_d_5617_78246)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.00004 5.31818C9.27619 5.31818 9.50004 5.54204 9.50004 5.81818V8.09091C9.50004 8.36705 9.27619 8.59091 9.00004 8.59091C8.7239 8.59091 8.50004 8.36705 8.50004 8.09091V5.81818C8.50004 5.54204 8.7239 5.31818 9.00004 5.31818Z" fill="url(#paint1_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.00004 9.40909C9.27619 9.40909 9.50004 9.63295 9.50004 9.90909V12.1818C9.50004 12.458 9.27619 12.6818 9.00004 12.6818C8.7239 12.6818 8.50004 12.458 8.50004 12.1818V9.90909C8.50004 9.63295 8.7239 9.40909 9.00004 9.40909Z" fill="url(#paint2_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.64355 5.1094C8.77894 5.35007 8.6936 5.65493 8.45293 5.79033L6.40065 6.94487C6.15998 7.08027 5.85512 6.99492 5.71973 6.75425C5.58433 6.51358 5.66968 6.20872 5.91035 6.07332L7.96262 4.91878C8.20329 4.78338 8.50815 4.86873 8.64355 5.1094Z" fill="url(#paint3_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.35653 5.1094C9.49193 4.86873 9.79679 4.78338 10.0375 4.91878L12.0897 6.07332C12.3304 6.20872 12.4158 6.51358 12.2804 6.75425C12.145 6.99492 11.8401 7.08027 11.5994 6.94487L9.54715 5.79033C9.30648 5.65493 9.22114 5.35007 9.35653 5.1094Z" fill="url(#paint4_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.71973 11.2458C5.85512 11.0051 6.15998 10.9197 6.40065 11.0551L8.45293 12.2097C8.6936 12.3451 8.77894 12.6499 8.64355 12.8906C8.50815 13.1313 8.20329 13.2166 7.96262 13.0812L5.91035 11.9267C5.66968 11.7913 5.58433 11.4864 5.71973 11.2458Z" fill="url(#paint5_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.2804 11.2458C12.4158 11.4864 12.3304 11.7913 12.0897 11.9267L10.0375 13.0812C9.79679 13.2166 9.49193 13.1313 9.35653 12.8906C9.22114 12.6499 9.30648 12.3451 9.54715 12.2097L11.5994 11.0551C11.8401 10.9197 12.145 11.0051 12.2804 11.2458Z" fill="url(#paint6_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.36368 7.36364C5.63982 7.36364 5.86368 7.58749 5.86368 7.86364V10.1364C5.86368 10.4125 5.63982 10.6364 5.36368 10.6364C5.08754 10.6364 4.86368 10.4125 4.86368 10.1364V7.86364C4.86368 7.58749 5.08754 7.36364 5.36368 7.36364Z" fill="url(#paint7_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.6364 7.36364C12.9126 7.36364 13.1364 7.58749 13.1364 7.86364V10.1364C13.1364 10.4125 12.9126 10.6364 12.6364 10.6364C12.3603 10.6364 12.1364 10.4125 12.1364 10.1364V7.86364C12.1364 7.58749 12.3603 7.36364 12.6364 7.36364Z" fill="url(#paint8_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.71928 7.1544C5.85467 6.91373 6.15953 6.82838 6.4002 6.96377L8.45338 8.11877C8.69406 8.25416 8.77941 8.55902 8.64402 8.79969C8.50863 9.04037 8.20377 9.12572 7.96309 8.99033L5.90991 7.83533C5.66924 7.69994 5.58389 7.39508 5.71928 7.1544Z" fill="url(#paint9_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.2799 7.15488C12.4153 7.39557 12.3299 7.70042 12.0892 7.8358L10.0374 8.98989C9.79675 9.12527 9.49189 9.0399 9.35652 8.79922C9.22114 8.55854 9.30651 8.25368 9.54719 8.1183L11.599 6.96421C11.8397 6.82884 12.1445 6.9142 12.2799 7.15488Z" fill="url(#paint10_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.64402 9.20031C8.77941 9.44099 8.69406 9.74585 8.45338 9.88124L6.4002 11.0362C6.15953 11.1716 5.85467 11.0863 5.71928 10.8456C5.58389 10.6049 5.66924 10.3001 5.90991 10.1647L7.96309 9.00968C8.20377 8.87429 8.50863 8.95964 8.64402 9.20031Z" fill="url(#paint11_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.35652 9.20078C9.49189 8.9601 9.79675 8.87473 10.0374 9.01011L12.0892 10.1642C12.3299 10.2996 12.4153 10.6044 12.2799 10.8451C12.1445 11.0858 11.8397 11.1712 11.599 11.0358L9.54719 9.8817C9.30651 9.74632 9.22114 9.44146 9.35652 9.20078Z" fill="url(#paint12_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.36368 10.6364C5.13775 10.6364 4.95459 10.8195 4.95459 11.0455C4.95459 11.2714 5.13775 11.4545 5.36368 11.4545C5.58962 11.4545 5.77277 11.2714 5.77277 11.0455C5.77277 10.8195 5.58962 10.6364 5.36368 10.6364ZM3.95459 11.0455C3.95459 10.2672 4.58546 9.63636 5.36368 9.63636C6.1419 9.63636 6.77277 10.2672 6.77277 11.0455C6.77277 11.8237 6.1419 12.4545 5.36368 12.4545C4.58546 12.4545 3.95459 11.8237 3.95459 11.0455Z" fill="url(#paint13_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.36368 6.54545C5.13775 6.54545 4.95459 6.72861 4.95459 6.95455C4.95459 7.18048 5.13775 7.36364 5.36368 7.36364C5.58962 7.36364 5.77277 7.18048 5.77277 6.95455C5.77277 6.72861 5.58962 6.54545 5.36368 6.54545ZM3.95459 6.95455C3.95459 6.17633 4.58546 5.54545 5.36368 5.54545C6.1419 5.54545 6.77277 6.17633 6.77277 6.95455C6.77277 7.73276 6.1419 8.36364 5.36368 8.36364C4.58546 8.36364 3.95459 7.73276 3.95459 6.95455Z" fill="url(#paint14_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.6364 10.6364C12.4105 10.6364 12.2273 10.8195 12.2273 11.0455C12.2273 11.2714 12.4105 11.4545 12.6364 11.4545C12.8623 11.4545 13.0455 11.2714 13.0455 11.0455C13.0455 10.8195 12.8623 10.6364 12.6364 10.6364ZM11.2273 11.0455C11.2273 10.2672 11.8582 9.63636 12.6364 9.63636C13.4146 9.63636 14.0455 10.2672 14.0455 11.0455C14.0455 11.8237 13.4146 12.4545 12.6364 12.4545C11.8582 12.4545 11.2273 11.8237 11.2273 11.0455Z" fill="url(#paint15_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.6364 6.54545C12.4105 6.54545 12.2273 6.72861 12.2273 6.95455C12.2273 7.18048 12.4105 7.36364 12.6364 7.36364C12.8623 7.36364 13.0455 7.18048 13.0455 6.95455C13.0455 6.72861 12.8623 6.54545 12.6364 6.54545ZM11.2273 6.95455C11.2273 6.17633 11.8582 5.54545 12.6364 5.54545C13.4146 5.54545 14.0455 6.17633 14.0455 6.95455C14.0455 7.73276 13.4146 8.36364 12.6364 8.36364C11.8582 8.36364 11.2273 7.73276 11.2273 6.95455Z" fill="url(#paint16_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.00004 8.59091C8.77411 8.59091 8.59095 8.77407 8.59095 9C8.59095 9.22593 8.77411 9.40909 9.00004 9.40909C9.22598 9.40909 9.40914 9.22593 9.40914 9C9.40914 8.77407 9.22598 8.59091 9.00004 8.59091ZM7.59095 9C7.59095 8.22178 8.22182 7.59091 9.00004 7.59091C9.77826 7.59091 10.4091 8.22178 10.4091 9C10.4091 9.77822 9.77826 10.4091 9.00004 10.4091C8.22182 10.4091 7.59095 9.77822 7.59095 9Z" fill="url(#paint17_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.00004 4.5C8.77411 4.5 8.59095 4.68316 8.59095 4.90909C8.59095 5.13503 8.77411 5.31818 9.00004 5.31818C9.22598 5.31818 9.40914 5.13503 9.40914 4.90909C9.40914 4.68316 9.22598 4.5 9.00004 4.5ZM7.59095 4.90909C7.59095 4.13087 8.22182 3.5 9.00004 3.5C9.77826 3.5 10.4091 4.13087 10.4091 4.90909C10.4091 5.68731 9.77826 6.31818 9.00004 6.31818C8.22182 6.31818 7.59095 5.68731 7.59095 4.90909Z" fill="url(#paint18_linear_5617_78246)" shape-rendering="crispEdges"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.00004 12.6818C8.77411 12.6818 8.59095 12.865 8.59095 13.0909C8.59095 13.3168 8.77411 13.5 9.00004 13.5C9.22598 13.5 9.40914 13.3168 9.40914 13.0909C9.40914 12.865 9.22598 12.6818 9.00004 12.6818ZM7.59095 13.0909C7.59095 12.3127 8.22182 11.6818 9.00004 11.6818C9.77826 11.6818 10.4091 12.3127 10.4091 13.0909C10.4091 13.8691 9.77826 14.5 9.00004 14.5C8.22182 14.5 7.59095 13.8691 7.59095 13.0909Z" fill="url(#paint19_linear_5617_78246)" shape-rendering="crispEdges"/>
</g>
</g>
<defs>
<filter id="filter0_d_5617_78246" x="3.45459" y="3.25" width="11.0908" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.25"/>
<feGaussianBlur stdDeviation="0.25"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_5617_78246"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5617_78246" result="shape"/>
</filter>
<linearGradient id="paint0_linear_5617_78246" x1="1" y1="1" x2="17" y2="17" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.12"/>
<stop offset="1" stop-color="white" stop-opacity="0.08"/>
</linearGradient>
<linearGradient id="paint1_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint2_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint3_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint4_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint5_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint6_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint7_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint8_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint9_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint10_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint11_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint12_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint13_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint14_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint15_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint16_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint17_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint18_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint19_linear_5617_78246" x1="9.00004" y1="3.5" x2="9.00004" y2="14.5" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<clipPath id="clip0_5617_78246">
<rect width="12" height="12" fill="white" transform="translate(3 3)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,44 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5996 0.5C11.7113 0.5 12.5755 0.500118 13.2676 0.556641C13.9655 0.61366 14.5329 0.730378 15.043 0.990234L15.3525 1.16406C16.0576 1.59644 16.6322 2.21605 17.0098 2.95703L17.1006 3.15137C17.2979 3.61108 17.3935 4.12184 17.4434 4.73242C17.4999 5.42447 17.5 6.28869 17.5 7.40039V10.5996C17.5 11.7113 17.4999 12.5755 17.4434 13.2676C17.3935 13.8782 17.2979 14.3889 17.1006 14.8486L17.0098 15.043C16.6322 15.7839 16.0576 16.4036 15.3525 16.8359L15.043 17.0098C14.5329 17.2696 13.9655 17.3863 13.2676 17.4434C12.5755 17.4999 11.7113 17.5 10.5996 17.5H7.40039C6.28869 17.5 5.42447 17.4999 4.73242 17.4434C4.12184 17.3935 3.61108 17.2979 3.15137 17.1006L2.95703 17.0098C2.21605 16.6322 1.59644 16.0576 1.16406 15.3525L0.990234 15.043C0.730378 14.5329 0.61366 13.9655 0.556641 13.2676C0.500118 12.5755 0.5 11.7113 0.5 10.5996V7.40039C0.5 6.28869 0.500118 5.42447 0.556641 4.73242C0.61366 4.03453 0.730378 3.46707 0.990234 2.95703L1.16406 2.64746C1.59644 1.94243 2.21605 1.36778 2.95703 0.990234L3.15137 0.899414C3.61108 0.702129 4.12184 0.606527 4.73242 0.556641C5.42447 0.500118 6.28869 0.5 7.40039 0.5H10.5996Z" stroke="white"/>
<path d="M1 7.4C1 5.15979 1 4.03969 1.43597 3.18404C1.81947 2.43139 2.43139 1.81947 3.18404 1.43597C4.03969 1 5.15979 1 7.4 1H10.6C12.8402 1 13.9603 1 14.816 1.43597C15.5686 1.81947 16.1805 2.43139 16.564 3.18404C17 4.03969 17 5.15979 17 7.4V10.6C17 12.8402 17 13.9603 16.564 14.816C16.1805 15.5686 15.5686 16.1805 14.816 16.564C13.9603 17 12.8402 17 10.6 17H7.4C5.15979 17 4.03969 17 3.18404 16.564C2.43139 16.1805 1.81947 15.5686 1.43597 14.816C1 13.9603 1 12.8402 1 10.6V7.4Z" fill="#0BA5EC"/>
<path d="M1 7.4C1 5.15979 1 4.03969 1.43597 3.18404C1.81947 2.43139 2.43139 1.81947 3.18404 1.43597C4.03969 1 5.15979 1 7.4 1H10.6C12.8402 1 13.9603 1 14.816 1.43597C15.5686 1.81947 16.1805 2.43139 16.564 3.18404C17 4.03969 17 5.15979 17 7.4V10.6C17 12.8402 17 13.9603 16.564 14.816C16.1805 15.5686 15.5686 16.1805 14.816 16.564C13.9603 17 12.8402 17 10.6 17H7.4C5.15979 17 4.03969 17 3.18404 16.564C2.43139 16.1805 1.81947 15.5686 1.43597 14.816C1 13.9603 1 12.8402 1 10.6V7.4Z" fill="url(#paint0_linear_5617_78274)"/>
<path d="M7.40039 1.5H10.5996C11.728 1.5 12.5446 1.50029 13.1865 1.55273C13.7434 1.59824 14.1352 1.68127 14.4561 1.81934L14.5889 1.88184C15.1651 2.17543 15.6471 2.62172 15.9834 3.16992L16.1182 3.41113C16.2942 3.75672 16.3953 4.17741 16.4473 4.81348C16.4997 5.4554 16.5 6.27204 16.5 7.40039V10.5996C16.5 11.728 16.4997 12.5446 16.4473 13.1865C16.4018 13.7434 16.3187 14.1352 16.1807 14.4561L16.1182 14.5889C15.8246 15.1651 15.3783 15.6471 14.8301 15.9834L14.5889 16.1182C14.2433 16.2942 13.8226 16.3953 13.1865 16.4473C12.5446 16.4997 11.728 16.5 10.5996 16.5H7.40039C6.27204 16.5 5.4554 16.4997 4.81348 16.4473C4.2566 16.4018 3.8648 16.3187 3.54395 16.1807L3.41113 16.1182C2.83494 15.8246 2.35287 15.3783 2.0166 14.8301L1.88184 14.5889C1.70575 14.2433 1.60471 13.8226 1.55273 13.1865C1.50029 12.5446 1.5 11.728 1.5 10.5996V7.40039C1.5 6.27204 1.50029 5.4554 1.55273 4.81348C1.59824 4.2566 1.68127 3.8648 1.81934 3.54395L1.88184 3.41113C2.17543 2.83494 2.62172 2.35287 3.16992 2.0166L3.41113 1.88184C3.75672 1.70575 4.17741 1.60471 4.81348 1.55273C5.4554 1.50029 6.27204 1.5 7.40039 1.5Z" stroke="#101828" stroke-opacity="0.08"/>
<g clip-path="url(#clip0_5617_78274)">
<g filter="url(#filter0_d_5617_78274)">
<path d="M6.70834 6.87516C7.51375 6.87516 8.16667 6.22224 8.16667 5.41683C8.16667 4.61141 7.51375 3.9585 6.70834 3.9585C5.90292 3.9585 5.25001 4.61141 5.25001 5.41683C5.25001 6.22224 5.90292 6.87516 6.70834 6.87516Z" fill="url(#paint1_linear_5617_78274)" shape-rendering="crispEdges"/>
<path d="M11.2917 6.87516C12.0971 6.87516 12.75 6.22224 12.75 5.41683C12.75 4.61141 12.0971 3.9585 11.2917 3.9585C10.4863 3.9585 9.83334 4.61141 9.83334 5.41683C9.83334 6.22224 10.4863 6.87516 11.2917 6.87516Z" fill="url(#paint2_linear_5617_78274)" shape-rendering="crispEdges"/>
<path d="M11.2917 7.70849C10.8377 7.709 10.3912 7.82377 9.99324 8.04222C9.59529 8.26067 9.25874 8.57578 9.01459 8.95849C9.34482 8.96235 9.66011 9.09673 9.89159 9.33229C10.1231 9.56785 10.2519 9.88545 10.25 10.2157C10.2481 10.5459 10.1155 10.862 9.8813 11.0949C9.6471 11.3277 9.33026 11.4584 9 11.4584C8.66975 11.4584 8.35291 11.3277 8.1187 11.0949C7.8845 10.862 7.75195 10.5459 7.75003 10.2157C7.7481 9.88545 7.87695 9.56785 8.10842 9.33229C8.3399 9.09673 8.65519 8.96235 8.98542 8.95849C8.67086 8.46429 8.20432 8.08561 7.656 7.87941C7.10767 7.67321 6.50721 7.65065 5.94496 7.81512C5.3827 7.97959 4.88906 8.32219 4.53831 8.79139C4.18755 9.26059 3.99864 9.83101 4.00001 10.4168V13.1252C4.00001 13.2357 4.04391 13.3416 4.12205 13.4198C4.20019 13.4979 4.30617 13.5418 4.41667 13.5418H7.33334V12.5002L5.83334 11.3752C5.78957 11.3423 5.75269 11.3012 5.72481 11.2541C5.69693 11.207 5.6786 11.1549 5.67086 11.1008C5.65523 10.9914 5.6837 10.8802 5.75001 10.7918C5.81631 10.7034 5.91502 10.645 6.02441 10.6293C6.13381 10.6137 6.24493 10.6422 6.33334 10.7085L7.88875 11.8752H10.1113L11.6667 10.7085C11.7551 10.6422 11.8662 10.6137 11.9756 10.6293C12.085 10.645 12.1837 10.7034 12.25 10.7918C12.3163 10.8802 12.3448 10.9914 12.3291 11.1008C12.3135 11.2101 12.2551 11.3089 12.1667 11.3752L10.6667 12.5002V13.5418H13.5833C13.6938 13.5418 13.7998 13.4979 13.878 13.4198C13.9561 13.3416 14 13.2357 14 13.1252V10.4168C13.9991 9.6988 13.7135 9.01044 13.2058 8.50272C12.6981 7.995 12.0097 7.70938 11.2917 7.70849Z" fill="url(#paint3_linear_5617_78274)" shape-rendering="crispEdges"/>
</g>
</g>
<defs>
<filter id="filter0_d_5617_78274" x="3.5" y="3.7085" width="11" height="10.5835" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.25"/>
<feGaussianBlur stdDeviation="0.25"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_5617_78274"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5617_78274" result="shape"/>
</filter>
<linearGradient id="paint0_linear_5617_78274" x1="1" y1="1" x2="17" y2="17" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.12"/>
<stop offset="1" stop-color="white" stop-opacity="0.08"/>
</linearGradient>
<linearGradient id="paint1_linear_5617_78274" x1="9" y1="3.9585" x2="9" y2="13.5418" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint2_linear_5617_78274" x1="9" y1="3.9585" x2="9" y2="13.5418" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<linearGradient id="paint3_linear_5617_78274" x1="9" y1="3.9585" x2="9" y2="13.5418" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
<clipPath id="clip0_5617_78274">
<rect width="12" height="12" fill="white" transform="translate(3 3)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,29 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5996 0.5C11.7113 0.5 12.5755 0.500118 13.2676 0.556641C13.9655 0.61366 14.5329 0.730378 15.043 0.990234L15.3525 1.16406C16.0576 1.59644 16.6322 2.21605 17.0098 2.95703L17.1006 3.15137C17.2979 3.61108 17.3935 4.12184 17.4434 4.73242C17.4999 5.42447 17.5 6.28869 17.5 7.40039V10.5996C17.5 11.7113 17.4999 12.5755 17.4434 13.2676C17.3935 13.8782 17.2979 14.3889 17.1006 14.8486L17.0098 15.043C16.6322 15.7839 16.0576 16.4036 15.3525 16.8359L15.043 17.0098C14.5329 17.2696 13.9655 17.3863 13.2676 17.4434C12.5755 17.4999 11.7113 17.5 10.5996 17.5H7.40039C6.28869 17.5 5.42447 17.4999 4.73242 17.4434C4.12184 17.3935 3.61108 17.2979 3.15137 17.1006L2.95703 17.0098C2.21605 16.6322 1.59644 16.0576 1.16406 15.3525L0.990234 15.043C0.730378 14.5329 0.61366 13.9655 0.556641 13.2676C0.500118 12.5755 0.5 11.7113 0.5 10.5996V7.40039C0.5 6.28869 0.500118 5.42447 0.556641 4.73242C0.61366 4.03453 0.730378 3.46707 0.990234 2.95703L1.16406 2.64746C1.59644 1.94243 2.21605 1.36778 2.95703 0.990234L3.15137 0.899414C3.61108 0.702129 4.12184 0.606527 4.73242 0.556641C5.42447 0.500118 6.28869 0.5 7.40039 0.5H10.5996Z" stroke="white"/>
<path d="M1 7.4C1 5.15979 1 4.03969 1.43597 3.18404C1.81947 2.43139 2.43139 1.81947 3.18404 1.43597C4.03969 1 5.15979 1 7.4 1H10.6C12.8402 1 13.9603 1 14.816 1.43597C15.5686 1.81947 16.1805 2.43139 16.564 3.18404C17 4.03969 17 5.15979 17 7.4V10.6C17 12.8402 17 13.9603 16.564 14.816C16.1805 15.5686 15.5686 16.1805 14.816 16.564C13.9603 17 12.8402 17 10.6 17H7.4C5.15979 17 4.03969 17 3.18404 16.564C2.43139 16.1805 1.81947 15.5686 1.43597 14.816C1 13.9603 1 12.8402 1 10.6V7.4Z" fill="#0E9384"/>
<path d="M1 7.4C1 5.15979 1 4.03969 1.43597 3.18404C1.81947 2.43139 2.43139 1.81947 3.18404 1.43597C4.03969 1 5.15979 1 7.4 1H10.6C12.8402 1 13.9603 1 14.816 1.43597C15.5686 1.81947 16.1805 2.43139 16.564 3.18404C17 4.03969 17 5.15979 17 7.4V10.6C17 12.8402 17 13.9603 16.564 14.816C16.1805 15.5686 15.5686 16.1805 14.816 16.564C13.9603 17 12.8402 17 10.6 17H7.4C5.15979 17 4.03969 17 3.18404 16.564C2.43139 16.1805 1.81947 15.5686 1.43597 14.816C1 13.9603 1 12.8402 1 10.6V7.4Z" fill="url(#paint0_linear_5617_78260)"/>
<path d="M7.40039 1.5H10.5996C11.728 1.5 12.5446 1.50029 13.1865 1.55273C13.7434 1.59824 14.1352 1.68127 14.4561 1.81934L14.5889 1.88184C15.1651 2.17543 15.6471 2.62172 15.9834 3.16992L16.1182 3.41113C16.2942 3.75672 16.3953 4.17741 16.4473 4.81348C16.4997 5.4554 16.5 6.27204 16.5 7.40039V10.5996C16.5 11.728 16.4997 12.5446 16.4473 13.1865C16.4018 13.7434 16.3187 14.1352 16.1807 14.4561L16.1182 14.5889C15.8246 15.1651 15.3783 15.6471 14.8301 15.9834L14.5889 16.1182C14.2433 16.2942 13.8226 16.3953 13.1865 16.4473C12.5446 16.4997 11.728 16.5 10.5996 16.5H7.40039C6.27204 16.5 5.4554 16.4997 4.81348 16.4473C4.2566 16.4018 3.8648 16.3187 3.54395 16.1807L3.41113 16.1182C2.83494 15.8246 2.35287 15.3783 2.0166 14.8301L1.88184 14.5889C1.70575 14.2433 1.60471 13.8226 1.55273 13.1865C1.50029 12.5446 1.5 11.728 1.5 10.5996V7.40039C1.5 6.27204 1.50029 5.4554 1.55273 4.81348C1.59824 4.2566 1.68127 3.8648 1.81934 3.54395L1.88184 3.41113C2.17543 2.83494 2.62172 2.35287 3.16992 2.0166L3.41113 1.88184C3.75672 1.70575 4.17741 1.60471 4.81348 1.55273C5.4554 1.50029 6.27204 1.5 7.40039 1.5Z" stroke="#101828" stroke-opacity="0.08"/>
<g filter="url(#filter0_d_5617_78260)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.001 5.5C14.001 4.94772 13.5533 4.5 13.001 4.5H7.00098C6.44869 4.5 6.00098 4.94772 6.00098 5.5V6.5H5.00098C4.44869 6.5 4.00098 6.94772 4.00098 7.5V11.5C4.00098 12.0523 4.44869 12.5 5.00098 12.5H5.50098V13.25C5.50098 13.4271 5.59473 13.5911 5.74742 13.681C5.9001 13.7708 6.08894 13.7731 6.2438 13.6871L8.38055 12.5H11.001C11.5533 12.5 12.001 12.0523 12.001 11.5V10.5H13.001C13.5533 10.5 14.001 10.0523 14.001 9.5V5.5ZM11.001 6.5H7.00098V5.5H13.001V9.5H12.001V7.5C12.001 6.94772 11.5533 6.5 11.001 6.5ZM6.5 8C6.22386 8 6 8.22386 6 8.5C6 8.77614 6.22386 9 6.5 9H9.5C9.77614 9 10 8.77614 10 8.5C10 8.22386 9.77614 8 9.5 8H6.5ZM6.5 10C6.22386 10 6 10.2239 6 10.5C6 10.7761 6.22386 11 6.5 11H8C8.27614 11 8.5 10.7761 8.5 10.5C8.5 10.2239 8.27614 10 8 10H6.5Z" fill="url(#paint1_linear_5617_78260)" shape-rendering="crispEdges"/>
</g>
<defs>
<filter id="filter0_d_5617_78260" x="3.50098" y="4.25" width="11" height="10.25" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="0.25"/>
<feGaussianBlur stdDeviation="0.25"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_5617_78260"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5617_78260" result="shape"/>
</filter>
<linearGradient id="paint0_linear_5617_78260" x1="1" y1="1" x2="17" y2="17" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.12"/>
<stop offset="1" stop-color="white" stop-opacity="0.08"/>
</linearGradient>
<linearGradient id="paint1_linear_5617_78260" x1="9.00099" y1="4.5" x2="9.00099" y2="13.75" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.9"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.66667 1.34356C8.66667 1.32602 8.66667 1.31725 8.66591 1.30135C8.65018 0.972168 8.3607 0.682824 8.03151 0.667251C8.01562 0.666499 8.0104 0.666501 8.00001 0.666504H5.8391C5.30248 0.666497 4.85957 0.666491 4.49878 0.695968C4.12405 0.726585 3.77958 0.792295 3.45603 0.957155C2.95426 1.21282 2.54631 1.62077 2.29065 2.12253C2.12579 2.44609 2.06008 2.79056 2.02946 3.16529C1.99999 3.52608 1.99999 3.96899 2 4.50562V11.494C1.99999 12.0307 1.99999 12.4736 2.02946 12.8344C2.06008 13.2091 2.12579 13.5536 2.29065 13.8771C2.54631 14.3789 2.95426 14.7869 3.45603 15.0425C3.77958 15.2074 4.12405 15.2731 4.49878 15.3037C4.85958 15.3332 5.30248 15.3332 5.83912 15.3332H10.1609C10.6975 15.3332 11.1404 15.3332 11.5012 15.3037C11.8759 15.2731 12.2204 15.2074 12.544 15.0425C13.0457 14.7869 13.4537 14.3789 13.7093 13.8771C13.8742 13.5536 13.9399 13.2091 13.9705 12.8344C14 12.4736 14 12.0307 14 11.4941V6.66646C14 6.65611 14 6.65093 13.9993 6.63505C13.9837 6.30583 13.6943 6.01631 13.3651 6.0006C13.3492 5.99985 13.3405 5.99985 13.323 5.99985L10.3787 5.99985C10.2105 5.99987 10.0466 5.99989 9.90785 5.98855C9.75545 5.9761 9.57563 5.94672 9.39468 5.85452C9.1438 5.72669 8.93983 5.52272 8.81199 5.27183C8.7198 5.09088 8.69042 4.91106 8.67797 4.75867C8.66663 4.61989 8.66665 4.45603 8.66667 4.28778L8.66667 1.34356ZM5.33333 8.6665C4.96514 8.6665 4.66667 8.96498 4.66667 9.33317C4.66667 9.70136 4.96514 9.99984 5.33333 9.99984H10.6667C11.0349 9.99984 11.3333 9.70136 11.3333 9.33317C11.3333 8.96498 11.0349 8.6665 10.6667 8.6665H5.33333ZM5.33333 11.3332C4.96514 11.3332 4.66667 11.6316 4.66667 11.9998C4.66667 12.368 4.96514 12.6665 5.33333 12.6665H9.33333C9.70152 12.6665 10 12.368 10 11.9998C10 11.6316 9.70152 11.3332 9.33333 11.3332H5.33333Z" fill="#444CE7"/>
<path d="M12.6053 4.6665C12.8011 4.6665 12.8989 4.6665 12.9791 4.61735C13.0923 4.54794 13.16 4.3844 13.129 4.25526C13.107 4.16382 13.0432 4.10006 12.9155 3.97253L10.694 1.75098C10.5664 1.62333 10.5027 1.5595 10.4112 1.53752C10.2821 1.50648 10.1186 1.57417 10.0492 1.6874C10 1.76757 10 1.86545 10 2.0612L10 4.13315C10 4.31982 10 4.41316 10.0363 4.48446C10.0683 4.54718 10.1193 4.59818 10.182 4.63014C10.2533 4.66647 10.3466 4.66647 10.5333 4.66647L12.6053 4.6665Z" fill="#444CE7"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,5 +0,0 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon L">
<path id="Vector" fill-rule="evenodd" clip-rule="evenodd" d="M6 0.5C6.27615 0.5 6.5 0.72386 6.5 1V1.52755C6.95855 1.57831 7.3967 1.69804 7.80355 1.87619L8.067 1.41997C8.20505 1.18083 8.51085 1.09889 8.75 1.23696C8.98915 1.37503 9.07105 1.68082 8.933 1.91998L8.6692 2.37685C9.033 2.64523 9.3548 2.96707 9.6232 3.33084L10.0801 3.06703C10.3193 2.92896 10.6251 3.0109 10.7632 3.25005C10.9012 3.4892 10.8193 3.79499 10.5801 3.93306L10.1238 4.19649C10.302 4.60333 10.4218 5.0415 10.4725 5.50005H11C11.2761 5.50005 11.5 5.7239 11.5 6.00005C11.5 6.2762 11.2761 6.50005 11 6.50005H10.4725C10.4218 6.9586 10.302 7.3968 10.1238 7.80365L10.5801 8.0671C10.8193 8.20515 10.9012 8.51095 10.7632 8.7501C10.6251 8.98925 10.3193 9.0712 10.0801 8.9331L9.6232 8.6693C9.3548 9.03305 9.03295 9.3549 8.6692 9.62325L8.933 10.0802C9.07105 10.3193 8.98915 10.6251 8.75 10.7632C8.51085 10.9012 8.20505 10.8193 8.067 10.5802L7.80355 10.1239C7.3967 10.3021 6.95855 10.4218 6.5 10.4726V11C6.5 11.2761 6.27615 11.5 6 11.5C5.72385 11.5 5.5 11.2761 5.5 11V10.4726C5.04145 10.4218 4.60328 10.3021 4.19644 10.1239L3.933 10.5802C3.79493 10.8194 3.48914 10.9013 3.24999 10.7633C3.01084 10.6252 2.92891 10.3194 3.06698 10.0802L3.3308 9.62325C2.96702 9.3549 2.64517 9.03305 2.37678 8.66925L1.91986 8.93305C1.68071 9.07115 1.37492 8.9892 1.23685 8.75005C1.09878 8.5109 1.18072 8.2051 1.41986 8.06705L1.87612 7.8036C1.69797 7.39675 1.57824 6.9586 1.52749 6.50005L0.999975 6.5C0.723835 6.5 0.499987 6.2761 0.5 6C0.500015 5.72385 0.72388 5.5 1.00003 5.5L1.5275 5.50005C1.57825 5.0415 1.69796 4.60335 1.87611 4.19652L1.41987 3.93312C1.18072 3.79504 1.09878 3.48925 1.23685 3.2501C1.37492 3.01095 1.68071 2.92901 1.91985 3.06709L2.37675 3.33086C2.64514 2.96708 2.967 2.64524 3.33078 2.37684L3.06698 1.91992C2.92891 1.68077 3.01084 1.37498 3.24999 1.23691C3.48914 1.09884 3.79493 1.18077 3.933 1.41992L4.19642 1.87619C4.60327 1.69803 5.04145 1.57831 5.5 1.52755V1C5.5 0.72386 5.72385 0.5 6 0.5ZM3.83484 3.24991C3.48643 3.52463 3.19141 3.86415 2.96808 4.25014C2.67048 4.7645 2.49999 5.3616 2.49999 6.00005C2.49999 6.6385 2.67048 7.2356 2.96809 7.75C3.19142 8.13595 3.48645 8.4755 3.83486 8.7502L4.8599 6.97475C4.63581 6.71285 4.49999 6.37245 4.49999 6.00005C4.49999 5.62765 4.63581 5.28725 4.8599 5.02535L3.83484 3.24991ZM5.7258 4.52514L4.70041 2.74911C5.10185 2.58847 5.5402 2.50005 6 2.50005C6.63845 2.50005 7.23555 2.67054 7.74995 2.96816C8.28125 3.27557 8.7245 3.71882 9.0319 4.25012C9.2503 4.62764 9.4003 5.04975 9.4646 5.50005H7.41465C7.2087 4.91745 6.6531 4.50005 6 4.50005C5.9065 4.50005 5.8148 4.50865 5.7258 4.52514ZM7.41465 6.50005C7.2087 7.08265 6.6531 7.50005 6 7.50005C5.9065 7.50005 5.8148 7.49145 5.7258 7.47495L4.70043 9.251C5.10185 9.41165 5.5402 9.50005 6 9.50005C6.63845 9.50005 7.23555 9.32955 7.7499 9.03195C8.2812 8.72455 8.72445 8.2813 9.03185 7.75C9.2503 7.3725 9.4003 6.95035 9.4646 6.50005H7.41465Z" fill="#676F83"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -1,5 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon L">
<path id="Vector" d="M14.0002 2C14.3684 2 14.6668 2.29848 14.6668 2.66667V13.3333C14.6668 13.7015 14.3684 14 14.0002 14H2.00016C1.63198 14 1.3335 13.7015 1.3335 13.3333V2.66667C1.3335 2.29848 1.63198 2 2.00016 2H14.0002ZM13.3335 3.33333H2.66683V12.6667H13.3335V3.33333ZM14.0002 2.66667V13.3333H10.0002V2.66667H14.0002Z" fill="#354052"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 461 B

View File

@ -0,0 +1,12 @@
<svg width="212" height="74" viewBox="0 0 212 74" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Effect" opacity="0.8" filter="url(#filter0_f_6128_88629)">
<circle cx="24" cy="24" r="28" fill="#0BA5EC"/>
</g>
<defs>
<filter id="filter0_f_6128_88629" x="-164" y="-164" width="376" height="376" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_6128_88629"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 611 B

View File

@ -0,0 +1,12 @@
<svg width="214" height="124" viewBox="0 0 214 124" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Effect" opacity="0.5" filter="url(#filter0_f_6128_89310)">
<circle cx="26" cy="26" r="28" fill="#6172F3"/>
</g>
<defs>
<filter id="filter0_f_6128_89310" x="-162" y="-162" width="376" height="376" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_6128_89310"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 613 B

View File

@ -0,0 +1,12 @@
<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Effect" opacity="0.8" filter="url(#filter0_f_481_16338)">
<circle cx="32" cy="32" r="28" fill="#EF6820"/>
</g>
<defs>
<filter id="filter0_f_481_16338" x="-156" y="-156" width="376" height="376" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_481_16338"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 610 B

View File

@ -0,0 +1,12 @@
<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Effect" opacity="0.8" filter="url(#filter0_f_5863_82395)">
<circle cx="32" cy="32" r="28" fill="#6938EF"/>
</g>
<defs>
<filter id="filter0_f_5863_82395" x="-156" y="-156" width="376" height="376" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_5863_82395"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 613 B

View File

@ -1,7 +0,0 @@
<svg width="10" height="11" viewBox="0 0 10 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group">
<path id="Vector" d="M2.70833 3.87501C3.51375 3.87501 4.16666 3.22209 4.16666 2.41668C4.16666 1.61126 3.51375 0.958344 2.70833 0.958344C1.90292 0.958344 1.25 1.61126 1.25 2.41668C1.25 3.22209 1.90292 3.87501 2.70833 3.87501Z" fill="#676F83"/>
<path id="Vector_2" d="M7.29158 3.87501C8.097 3.87501 8.74992 3.22209 8.74992 2.41668C8.74992 1.61126 8.097 0.958344 7.29158 0.958344C6.48617 0.958344 5.83325 1.61126 5.83325 2.41668C5.83325 3.22209 6.48617 3.87501 7.29158 3.87501Z" fill="#676F83"/>
<path id="Vector_3" d="M7.29167 4.70835C6.83771 4.70886 6.39118 4.82363 5.99324 5.04208C5.59529 5.26053 5.25874 5.57563 5.01459 5.95835C5.34482 5.9622 5.66011 6.09658 5.89159 6.33215C6.12306 6.56771 6.25191 6.8853 6.24998 7.21555C6.24805 7.5458 6.11551 7.86187 5.8813 8.09472C5.6471 8.32756 5.33026 8.45826 5 8.45826C4.66975 8.45826 4.35291 8.32756 4.1187 8.09472C3.8845 7.86187 3.75195 7.5458 3.75003 7.21555C3.7481 6.8853 3.87695 6.56771 4.10842 6.33215C4.3399 6.09658 4.65519 5.9622 4.98542 5.95835C4.67086 5.46415 4.20432 5.08546 3.656 4.87926C3.10767 4.67306 2.50721 4.6505 1.94496 4.81497C1.3827 4.97944 0.889064 5.32205 0.538306 5.79125C0.187547 6.26045 -0.00135882 6.83086 7.35834e-06 7.41668V10.125C7.35834e-06 10.2355 0.043906 10.3415 0.122046 10.4196C0.200186 10.4978 0.306167 10.5417 0.416674 10.5417H3.33334V9.50001L1.83334 8.37501C1.78957 8.34218 1.75269 8.30105 1.72481 8.25397C1.69693 8.20688 1.6786 8.15477 1.67086 8.1006C1.65523 7.99121 1.6837 7.88008 1.75001 7.79168C1.81631 7.70327 1.91502 7.64483 2.02441 7.6292C2.13381 7.61357 2.24493 7.64204 2.33334 7.70835L3.88875 8.87501H6.11125L7.66667 7.70835C7.75507 7.64204 7.8662 7.61357 7.97559 7.6292C8.08499 7.64483 8.1837 7.70327 8.25 7.79168C8.31631 7.88008 8.34478 7.99121 8.32915 8.1006C8.31352 8.21 8.25507 8.30871 8.16667 8.37501L6.66667 9.50001V10.5417H9.58333C9.69384 10.5417 9.79982 10.4978 9.87796 10.4196C9.9561 10.3415 10 10.2355 10 10.125V7.41668C9.99912 6.69866 9.71349 6.01029 9.20577 5.50257C8.69805 4.99485 8.00969 4.70923 7.29167 4.70835Z" fill="#676F83"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
<path style="fill: rgb(0, 23, 87); stroke: rgb(13, 14, 52);" d="M 247.794 213.903 L 246.81 76.976 L 254.345 76.963 L 254.592 213.989 L 247.794 213.903 Z"/>
<ellipse style="fill: rgb(0, 23, 87); stroke: rgb(0, 23, 87);" cx="250.025" cy="43.859" rx="33.966" ry="33.906"/>
<path style="fill: rgb(30, 141, 166); stroke: rgb(30, 141, 166);" d="M 282.472 260.389 L 414.181 330.253 L 410.563 336.234 L 279.38 265.739 L 282.472 260.389 Z"/>
<path style="fill: rgb(15, 17, 57); stroke: rgb(13, 14, 52);" d="M 255.105 281.394 L 254.485 417.656 L 246.156 417.691 L 246.688 280.51 L 255.105 281.394 Z"/>
<path style="paint-order: fill; fill: rgb(30, 141, 166); stroke: rgb(30, 141, 166);" d="M 279.486 229.517 L 410.351 160.07 L 413.923 167.04 L 283.727 235.998 L 279.486 229.517 Z"/>
<path style="fill: rgb(15, 164, 161); stroke: rgb(15, 164, 161);" d="M 88.545 164.884 L 219.797 236.07 L 222.867 229.568 L 90.887 159.47 L 88.545 164.884 Z"/>
<path style="fill: rgb(15, 164, 161); stroke: rgb(15, 164, 161);" d="M 224.76 266.9 L 95.55 334.829 L 92.878 328.37 L 219.955 261.275 L 224.76 266.9 Z"/>
<ellipse style="paint-order: fill; fill: rgb(2, 181, 225); stroke: rgb(2, 181, 225);" cx="251.242" cy="247.466" rx="33.966" ry="33.906"/>
<path style="fill: rgb(13, 14, 52); stroke: rgb(13, 14, 52);" d="M 279.502 433.617 L 408.666 359.443 C 408.666 359.443 412.398 366.965 412.398 366.916 C 412.398 366.867 281.544 440.217 281.544 440.217 L 279.502 433.617 Z"/>
<path style="fill: rgb(13, 14, 52); stroke: rgb(13, 14, 52);" d="M 223.119 431.408 L 96.643 361.068 L 93.265 368.047 L 218.895 438.099 L 223.119 431.408 Z"/>
<ellipse style="fill: rgb(0, 23, 87); stroke: rgb(0, 23, 87);" cx="250.504" cy="451.168" rx="33.966" ry="33.906"/>
<path style="fill: rgb(90, 191, 187); stroke: rgb(90, 191, 187);" d="M 435.665 180.895 L 435.859 316.869 L 443.103 315.579 L 442.56 180.697 L 435.665 180.895 Z"/>
<ellipse style="fill: rgb(0, 23, 87); stroke: rgb(0, 23, 87);" cx="441.06" cy="349.665" rx="33.966" ry="33.906"/>
<ellipse style="fill: rgb(2, 181, 225); stroke: rgb(2, 181, 225);" cx="441.512" cy="147.767" rx="33.966" ry="33.906"/>
<path style="fill: rgb(84, 187, 181); stroke: rgb(84, 187, 181);" d="M 64.755 314.523 L 57.928 315.006 L 58.307 182.961 L 65.169 182.865 L 64.755 314.523 Z"/>
<ellipse style="fill: rgb(0, 23, 87); stroke: rgb(0, 23, 87);" cx="58.177" cy="149.757" rx="33.966" ry="33.906"/>
<ellipse style="fill: rgb(61, 224, 203); stroke: rgb(61, 224, 203);" cx="65.909" cy="348.17" rx="33.966" ry="33.906"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,3 @@
<svg width="24" height="11" viewBox="0 0 24 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="Arrow Shape" d="M9.87868 1.12132C11.0503 -0.0502525 12.9497 -0.0502525 14.1213 1.12132L23.3137 10.3137H0.686292L9.87868 1.12132Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 258 B

View File

Before

Width:  |  Height:  |  Size: 792 B

After

Width:  |  Height:  |  Size: 792 B

View File

Before

Width:  |  Height:  |  Size: 579 B

After

Width:  |  Height:  |  Size: 579 B

View File

@ -0,0 +1,3 @@
<svg width="6" height="30" viewBox="0 0 6 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="Line 3" d="M1.44595 28.7455L5.49125 1.00004" stroke="#101828" stroke-opacity="0.08" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 220 B

View File

@ -0,0 +1,8 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="piggy-bank-mod">
<g id="Union">
<path d="M12.75 1.80737C12.75 3.18809 11.6307 4.30738 10.25 4.30738C8.8693 4.30738 7.74998 3.18809 7.74998 1.80737L12.75 1.80737Z" fill="#444CE7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.29769 4.71516C6.27454 4.43606 7.31331 4.77702 7.93687 5.55738H11.8125C14.4013 5.55738 16.5 7.65605 16.5 10.2449C16.5 12.5126 14.8896 14.4043 12.75 14.8386V16.1824H11.5V14.9324H7.12498V16.1824H5.87498V14.8508C5.05321 14.6764 4.30548 14.229 3.76111 13.5695L2.99101 12.6365L1.5 12.0103V8.93745L3.29116 8.46851L4 7.56623V5.08593L5.29769 4.71516ZM7.75 7.43238H12.75V8.68239H7.75V7.43238ZM5.875 8.99489C5.875 9.51264 5.45527 9.93239 4.9375 9.93239C4.41973 9.93239 4 9.51264 4 8.99489C4 8.47714 4.41973 8.05739 4.9375 8.05739C5.45527 8.05739 5.875 8.47714 5.875 8.99489Z" fill="#444CE7"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 934 B

View File

@ -0,0 +1,7 @@
<svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group">
<path id="Vector" d="M13.008 4.24996H9.62427V0.866211L13.008 4.24996Z" fill="#676F83"/>
<path id="Vector_2" d="M7.18052 12.375H3.06177C2.97889 12.375 2.8994 12.3421 2.8408 12.2835C2.78219 12.2249 2.74927 12.1454 2.74927 12.0625V11.4375C2.74927 11.3546 2.78219 11.2751 2.8408 11.2165C2.8994 11.1579 2.97889 11.125 3.06177 11.125H7.18052C7.29883 10.4268 7.61311 9.7765 8.08677 9.25H3.06177C2.97889 9.25 2.8994 9.21708 2.8408 9.15847C2.78219 9.09987 2.74927 9.02038 2.74927 8.9375V8.3125C2.74927 8.22962 2.78219 8.15013 2.8408 8.09153C2.8994 8.03292 2.97889 8 3.06177 8H10.8743C11.798 8.00025 12.6888 8.34321 13.3743 8.9625V5.5H8.99927C8.83351 5.5 8.67454 5.43415 8.55733 5.31694C8.44011 5.19973 8.37427 5.04076 8.37427 4.875V0.5H1.49927C1.33351 0.5 1.17454 0.565848 1.05733 0.683058C0.940116 0.800269 0.874268 0.95924 0.874268 1.125V14.875C0.874268 15.0408 0.940116 15.1997 1.05733 15.3169C1.17454 15.4342 1.33351 15.5 1.49927 15.5H10.2493C9.46028 15.4015 8.72775 15.0392 8.17066 14.4719C7.61356 13.9046 7.26462 13.1656 7.18052 12.375ZM3.06177 4.875H6.18677C6.26965 4.875 6.34913 4.90792 6.40774 4.96653C6.46634 5.02513 6.49927 5.10462 6.49927 5.1875V5.8125C6.49927 5.89538 6.46634 5.97487 6.40774 6.03347C6.34913 6.09208 6.26965 6.125 6.18677 6.125H3.06177C2.97889 6.125 2.8994 6.09208 2.8408 6.03347C2.78219 5.97487 2.74927 5.89538 2.74927 5.8125V5.1875C2.74927 5.10462 2.78219 5.02513 2.8408 4.96653C2.8994 4.90792 2.97889 4.875 3.06177 4.875Z" fill="#676F83"/>
<path id="Vector_3" d="M14.4411 14.4331L13.0199 13.0119C13.2496 12.6309 13.3721 12.1949 13.3743 11.75C13.3743 11.2555 13.2276 10.7722 12.9529 10.3611C12.6782 9.94995 12.2878 9.62952 11.831 9.4403C11.3742 9.25108 10.8715 9.20157 10.3865 9.29804C9.90159 9.3945 9.45613 9.6326 9.1065 9.98223C8.75687 10.3319 8.51877 10.7773 8.42231 11.2623C8.32584 11.7472 8.37535 12.2499 8.56457 12.7067C8.75379 13.1635 9.07422 13.554 9.48534 13.8287C9.89647 14.1034 10.3798 14.25 10.8743 14.25C11.3192 14.2478 11.7552 14.1254 12.1361 13.8956L13.5574 15.3169C13.6753 15.4307 13.8331 15.4937 13.997 15.4923C14.1609 15.4909 14.3176 15.4251 14.4335 15.3093C14.5494 15.1934 14.6151 15.0366 14.6166 14.8728C14.618 14.7089 14.555 14.551 14.4411 14.4331ZM9.62427 11.75C9.62427 11.5028 9.69758 11.2611 9.83493 11.0555C9.97228 10.85 10.1675 10.6898 10.3959 10.5952C10.6243 10.5005 10.8757 10.4758 11.1181 10.524C11.3606 10.5723 11.5833 10.6913 11.7582 10.8661C11.933 11.0409 12.052 11.2637 12.1003 11.5061C12.1485 11.7486 12.1237 11.9999 12.0291 12.2284C11.9345 12.4568 11.7743 12.652 11.5687 12.7893C11.3632 12.9267 11.1215 13 10.8743 13C10.5427 13 10.2248 12.8683 9.99039 12.6339C9.75597 12.3995 9.62427 12.0815 9.62427 11.75Z" fill="#676F83"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,12 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="general">
<g id="Vector">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.375 3.75C12.375 3.12868 12.8787 2.625 13.5 2.625H15.75C16.3713 2.625 16.875 3.12868 16.875 3.75C16.875 4.37132 16.3713 4.875 15.75 4.875H13.5C12.8787 4.875 12.375 4.37132 12.375 3.75Z" fill="#444CE7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.125 3.75C1.125 3.12868 1.62868 2.625 2.25 2.625H9.75C10.3713 2.625 10.875 3.12868 10.875 3.75C10.875 4.37132 10.3713 4.875 9.75 4.875H2.25C1.62868 4.875 1.125 4.37132 1.125 3.75Z" fill="#444CE7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.125 9C1.125 8.37868 1.62868 7.875 2.25 7.875H4.5C5.12132 7.875 5.625 8.37868 5.625 9C5.625 9.62132 5.12132 10.125 4.5 10.125H2.25C1.62868 10.125 1.125 9.62132 1.125 9Z" fill="#444CE7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.125 9C7.125 8.37868 7.62868 7.875 8.25 7.875H15.75C16.3713 7.875 16.875 8.37868 16.875 9C16.875 9.62132 16.3713 10.125 15.75 10.125H8.25C7.62868 10.125 7.125 9.62132 7.125 9Z" fill="#444CE7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.375 14.25C12.375 13.6287 12.8787 13.125 13.5 13.125H15.75C16.3713 13.125 16.875 13.6287 16.875 14.25C16.875 14.8713 16.3713 15.375 15.75 15.375H13.5C12.8787 15.375 12.375 14.8713 12.375 14.25Z" fill="#444CE7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.125 14.25C1.125 13.6287 1.62868 13.125 2.25 13.125H9.75C10.3713 13.125 10.875 13.6287 10.875 14.25C10.875 14.8713 10.3713 15.375 9.75 15.375H2.25C1.62868 15.375 1.125 14.8713 1.125 14.25Z" fill="#444CE7"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,5 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="gold-coin">
<path id="Vector" d="M9 1.5C7.51664 1.5 6.06659 1.93987 4.83323 2.76398C3.59986 3.58809 2.63856 4.75943 2.07091 6.12987C1.50325 7.50032 1.35472 9.00832 1.64411 10.4632C1.9335 11.918 2.64781 13.2544 3.6967 14.3033C4.7456 15.3522 6.08197 16.0665 7.53683 16.3559C8.99168 16.6453 10.4997 16.4967 11.8701 15.9291C13.2406 15.3614 14.4119 14.4001 15.236 13.1668C16.0601 11.9334 16.5 10.4834 16.5 9C16.5 7.01087 15.7098 5.10322 14.3033 3.6967C12.8968 2.29018 10.9891 1.5 9 1.5ZM11.0656 5.09312L11.8006 4.08187C11.8489 4.01543 11.9098 3.95915 11.9798 3.91623C12.0498 3.87332 12.1276 3.84462 12.2087 3.83176C12.2898 3.81891 12.3726 3.82215 12.4525 3.84131C12.5323 3.86047 12.6076 3.89518 12.6741 3.94344C12.7405 3.9917 12.7968 4.05257 12.8397 4.12259C12.8826 4.1926 12.9113 4.27038 12.9242 4.35148C12.937 4.43259 12.9338 4.51543 12.9146 4.59529C12.8955 4.67514 12.8608 4.75043 12.8125 4.81687L12.0775 5.82812C11.98 5.96231 11.8333 6.05228 11.6695 6.07824C11.5057 6.1042 11.3382 6.06403 11.2041 5.96656C11.0699 5.86909 10.9799 5.72232 10.954 5.55851C10.928 5.39471 10.9682 5.22731 11.0656 5.09312ZM5.32625 3.94375C5.39271 3.89519 5.4681 3.86023 5.54811 3.84089C5.62811 3.82154 5.71115 3.81819 5.79245 3.83103C5.87375 3.84386 5.95172 3.87263 6.02187 3.91567C6.09203 3.95872 6.15299 4.0152 6.20125 4.08187L6.9375 5.09312C7.03497 5.22772 7.07498 5.39552 7.04872 5.55962C7.02247 5.72371 6.9321 5.87066 6.7975 5.96812C6.66291 6.06559 6.4951 6.1056 6.33101 6.07934C6.16691 6.05309 6.01997 5.96272 5.9225 5.82812L5.1875 4.81687C5.13927 4.75044 5.10459 4.67515 5.08546 4.59531C5.06632 4.51548 5.06311 4.43265 5.07599 4.35157C5.08888 4.27048 5.11761 4.19274 5.16055 4.12276C5.20349 4.05279 5.2598 3.99196 5.32625 3.94375ZM5.0325 10.9437L3.845 11.33C3.76604 11.3594 3.68196 11.3725 3.5978 11.3686C3.51364 11.3646 3.43114 11.3437 3.35527 11.3071C3.2794 11.2705 3.21171 11.2189 3.15629 11.1554C3.10086 11.092 3.05883 11.018 3.03274 10.9379C3.00664 10.8577 2.997 10.7732 3.00442 10.6893C3.01183 10.6053 3.03613 10.5238 3.07587 10.4495C3.1156 10.3752 3.16995 10.3097 3.23564 10.2569C3.30133 10.2042 3.37701 10.1653 3.45813 10.1425L4.64563 9.75625C4.72467 9.72655 4.80892 9.71317 4.89327 9.71693C4.97763 9.72069 5.06035 9.7415 5.13644 9.77811C5.21253 9.81472 5.28042 9.86637 5.33599 9.92994C5.39157 9.99352 5.43369 10.0677 5.45981 10.148C5.48593 10.2283 5.4955 10.3131 5.48796 10.3972C5.48041 10.4813 5.45591 10.563 5.41591 10.6373C5.37592 10.7117 5.32127 10.7772 5.25527 10.8299C5.18926 10.8825 5.11389 10.9213 5.0325 10.9437ZM9.625 14.625C9.625 14.7908 9.55915 14.9497 9.44194 15.0669C9.32473 15.1841 9.16576 15.25 9 15.25C8.83424 15.25 8.67527 15.1841 8.55806 15.0669C8.44085 14.9497 8.375 14.7908 8.375 14.625V13.375C8.375 13.2092 8.44085 13.0503 8.55806 12.9331C8.67527 12.8158 8.83424 12.75 9 12.75C9.16576 12.75 9.32473 12.8158 9.44194 12.9331C9.55915 13.0503 9.625 13.2092 9.625 13.375V14.625ZM9 11L6.6475 12.2375L7.09688 9.61812L5.19375 7.76312L7.82375 7.38125L9 5L10.1763 7.38125L12.8063 7.76312L10.9031 9.61812L11.3525 12.2375L9 11ZM14.1563 11.3312L12.9688 10.945C12.8874 10.9225 12.8114 10.8838 12.7454 10.8311C12.6794 10.7784 12.6247 10.7129 12.5847 10.6386C12.5447 10.5642 12.5202 10.4825 12.5127 10.3984C12.5051 10.3143 12.5147 10.2295 12.5408 10.1492C12.5669 10.0689 12.6091 9.99477 12.6646 9.9312C12.7202 9.86762 12.7881 9.81597 12.8642 9.77936C12.9403 9.74275 13.023 9.72194 13.1074 9.71818C13.1917 9.71442 13.276 9.7278 13.355 9.7575L14.5425 10.1437C14.6236 10.1665 14.6993 10.2054 14.765 10.2582C14.8307 10.311 14.885 10.3764 14.9248 10.4507C14.9645 10.525 14.9888 10.6066 14.9962 10.6905C15.0036 10.7744 14.994 10.859 14.9679 10.9391C14.9418 11.0192 14.8998 11.0932 14.8443 11.1567C14.7889 11.2201 14.7212 11.2717 14.6454 11.3083C14.5695 11.345 14.487 11.3659 14.4028 11.3698C14.3187 11.3738 14.2352 11.3606 14.1563 11.3312Z" fill="#EF6820"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,13 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group">
<path id="Vector" d="M2.54546 15.5001C2.1409 15.5001 1.74544 15.3801 1.40906 15.1554C1.07269 14.9306 0.810517 14.6112 0.655702 14.2374C0.500886 13.8636 0.460379 13.4524 0.539304 13.0556C0.618228 12.6588 0.813039 12.2943 1.0991 12.0083C1.38516 11.7222 1.74963 11.5274 2.14641 11.4485C2.54319 11.3696 2.95446 11.4101 3.32822 11.5649C3.70198 11.7197 4.02143 11.9819 4.24619 12.3182C4.47095 12.6546 4.59091 13.0501 4.59091 13.4546C4.59091 13.9971 4.37541 14.5174 3.99181 14.901C3.60821 15.2846 3.08794 15.5001 2.54546 15.5001Z" fill="#6938EF"/>
<path id="Vector_2" d="M8.00005 5.95459C7.59549 5.95459 7.20003 6.07455 6.86365 6.29931C6.52728 6.52407 6.26511 6.84352 6.11029 7.21728C5.95548 7.59104 5.91497 8.00231 5.99389 8.39909C6.07282 8.79587 6.26763 9.16034 6.55369 9.4464C6.83975 9.73246 7.20422 9.92727 7.601 10.0062C7.99778 10.0851 8.40905 10.0446 8.78281 9.8898C9.15656 9.73498 9.47602 9.47281 9.70078 9.13644C9.92554 8.80006 10.0455 8.4046 10.0455 8.00004C10.0455 7.45756 9.83 6.93729 9.4464 6.55369C9.0628 6.17009 8.54253 5.95459 8.00005 5.95459Z" fill="#6938EF"/>
<path id="Vector_3" d="M2.54546 0.5C2.1409 0.5 1.74544 0.619964 1.40906 0.844721C1.07269 1.06948 0.810517 1.38893 0.655702 1.76269C0.500886 2.13645 0.460379 2.54772 0.539304 2.9445C0.618228 3.34128 0.813039 3.70575 1.0991 3.99181C1.38516 4.27787 1.74963 4.47268 2.14641 4.55161C2.54319 4.63053 2.95446 4.59002 3.32822 4.43521C3.70198 4.28039 4.02143 4.01822 4.24619 3.68185C4.47095 3.34547 4.59091 2.95001 4.59091 2.54545C4.59091 2.00297 4.37541 1.4827 3.99181 1.0991C3.60821 0.715503 3.08794 0.5 2.54546 0.5Z" fill="#6938EF"/>
<path id="Vector_4" d="M13.4545 0.5C13.05 0.5 12.6545 0.619964 12.3181 0.844721C11.9817 1.06948 11.7196 1.38893 11.5648 1.76269C11.4099 2.13645 11.3694 2.54772 11.4484 2.9445C11.5273 3.34128 11.7221 3.70575 12.0082 3.99181C12.2942 4.27787 12.6587 4.47268 13.0555 4.55161C13.4522 4.63053 13.8635 4.59002 14.2373 4.43521C14.611 4.28039 14.9305 4.01822 15.1552 3.68185C15.38 3.34547 15.5 2.95001 15.5 2.54545C15.5 2.00297 15.2845 1.4827 14.9009 1.0991C14.5173 0.715503 13.997 0.5 13.4545 0.5Z" fill="#6938EF"/>
<path id="Vector_5" d="M13.4545 11.4092C13.05 11.4092 12.6545 11.5291 12.3181 11.7539C11.9817 11.9787 11.7196 12.2981 11.5648 12.6719C11.4099 13.0456 11.3694 13.4569 11.4484 13.8537C11.5273 14.2505 11.7221 14.6149 12.0082 14.901C12.2942 15.1871 12.6587 15.3819 13.0555 15.4608C13.4522 15.5397 13.8635 15.4992 14.2373 15.3444C14.611 15.1896 14.9305 14.9274 15.1552 14.591C15.38 14.2547 15.5 13.8592 15.5 13.4546C15.5 12.9121 15.2845 12.3919 14.9009 12.0083C14.5173 11.6247 13.997 11.4092 13.4545 11.4092Z" fill="#6938EF"/>
<path id="Vector_6" d="M4.59091 5.95459H0.5V10.0455H4.59091V5.95459Z" fill="#6938EF"/>
<path id="Vector_7" d="M15.5 5.95459H11.4091V10.0455H15.5V5.95459Z" fill="#6938EF"/>
<path id="Vector_8" d="M10.0455 0.5H5.95459V4.59091H10.0455V0.5Z" fill="#6938EF"/>
<path id="Vector_9" d="M10.0455 11.4092H5.95459V15.5001H10.0455V11.4092Z" fill="#6938EF"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,13 @@
export { default as Chunk } from './Chunk'
export { default as Collapse } from './Collapse'
export { default as Divider } from './Divider'
export { default as File } from './File'
export { default as GeneralType } from './GeneralType'
export { default as LayoutRight2LineMod } from './LayoutRight2LineMod'
export { default as OptionCardEffectBlueLight } from './OptionCardEffectBlueLight'
export { default as OptionCardEffectBlue } from './OptionCardEffectBlue'
export { default as OptionCardEffectOrange } from './OptionCardEffectOrange'
export { default as OptionCardEffectPurple } from './OptionCardEffectPurple'
export { default as ParentChildType } from './ParentChildType'
export { default as SelectionMod } from './SelectionMod'
export { default as Watercrawl } from './Watercrawl'

View File

@ -0,0 +1,9 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="parent-child">
<g id="Vector">
<path d="M5.56251 5.8125C6.77063 5.8125 7.75001 4.83312 7.75001 3.625C7.75001 2.41688 6.77063 1.4375 5.56251 1.4375C4.35439 1.4375 3.37501 2.41688 3.37501 3.625C3.37501 4.83312 4.35439 5.8125 5.56251 5.8125Z" fill="#0BA5EC"/>
<path d="M12.4375 5.8125C13.6456 5.8125 14.625 4.83312 14.625 3.625C14.625 2.41688 13.6456 1.4375 12.4375 1.4375C11.2294 1.4375 10.25 2.41688 10.25 3.625C10.25 4.83312 11.2294 5.8125 12.4375 5.8125Z" fill="#0BA5EC"/>
<path d="M12.4375 7.0625C11.7566 7.06326 11.0868 7.23541 10.4899 7.56309C9.89294 7.89077 9.38811 8.36342 9.02188 8.9375C9.51723 8.94327 9.99017 9.14485 10.3374 9.49819C10.6846 9.85154 10.8779 10.3279 10.875 10.8233C10.8721 11.3187 10.6733 11.7928 10.322 12.1421C9.97065 12.4913 9.49539 12.6874 9.00001 12.6874C8.50462 12.6874 8.02937 12.4913 7.67805 12.1421C7.32674 11.7928 7.12793 11.3187 7.12504 10.8233C7.12215 10.3279 7.31542 9.85154 7.66263 9.49819C8.00984 9.14485 8.48278 8.94327 8.97813 8.9375C8.50629 8.1962 7.80649 7.62817 6.984 7.31887C6.16151 7.00957 5.26082 6.97572 4.41744 7.22243C3.57406 7.46914 2.8336 7.98305 2.30746 8.68685C1.78132 9.39065 1.49796 10.2463 1.50001 11.125V15.1875C1.50001 15.3533 1.56586 15.5122 1.68307 15.6294C1.80028 15.7466 1.95925 15.8125 2.12501 15.8125H6.50001V14.25L4.25001 12.5625C4.18435 12.5132 4.12903 12.4515 4.08721 12.3809C4.0454 12.3103 4.0179 12.2321 4.00629 12.1509C3.98285 11.9868 4.02555 11.8201 4.12501 11.6875C4.22447 11.5549 4.37253 11.4672 4.53662 11.4438C4.70072 11.4203 4.8674 11.463 5.00001 11.5625L7.33313 13.3125H10.6669L13 11.5625C13.1326 11.463 13.2993 11.4203 13.4634 11.4438C13.6275 11.4672 13.7755 11.5549 13.875 11.6875C13.9745 11.8201 14.0172 11.9868 13.9937 12.1509C13.9703 12.315 13.8826 12.463 13.75 12.5625L11.5 14.25V15.8125H15.875C16.0408 15.8125 16.1997 15.7466 16.3169 15.6294C16.4342 15.5122 16.5 15.3533 16.5 15.1875V11.125C16.4987 10.048 16.0702 9.01541 15.3087 8.25383C14.5471 7.49226 13.5145 7.06382 12.4375 7.0625Z" fill="#0BA5EC"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,5 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="qa">
<path id="Vector" fill-rule="evenodd" clip-rule="evenodd" d="M16.5015 3.75C16.5015 2.92157 15.8299 2.25 15.0015 2.25H6.00146C5.17304 2.25 4.50146 2.92157 4.50146 3.75V5.25H3.00146C2.17304 5.25 1.50146 5.92157 1.50146 6.75V12.75C1.50146 13.5784 2.17304 14.25 3.00146 14.25H3.75146V15.375C3.75146 15.6407 3.89209 15.8867 4.12112 16.0214C4.35015 16.1562 4.6334 16.1597 4.8657 16.0307L8.07083 14.25H12.0015C12.8299 14.25 13.5015 13.5784 13.5015 12.75V11.25H15.0015C15.8299 11.25 16.5015 10.5785 16.5015 9.75V3.75ZM12.0015 5.25H6.00146V3.75H15.0015V9.75H13.5015V6.75C13.5015 5.92157 12.8299 5.25 12.0015 5.25ZM5.25 7.5C4.83579 7.5 4.5 7.83579 4.5 8.25C4.5 8.66421 4.83579 9 5.25 9H9.75C10.1642 9 10.5 8.66421 10.5 8.25C10.5 7.83579 10.1642 7.5 9.75 7.5H5.25ZM5.25 10.5C4.83579 10.5 4.5 10.8358 4.5 11.25C4.5 11.6642 4.83579 12 5.25 12H7.5C7.91421 12 8.25 11.6642 8.25 11.25C8.25 10.8358 7.91421 10.5 7.5 10.5H5.25Z" fill="#676F83"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,13 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group">
<path id="Vector" d="M4.25 15.5H0.5V11.75H4.25V15.5Z" fill="#676F83"/>
<path id="Vector_2" d="M9.875 9.875H6.125V6.125H9.875V9.875Z" fill="#676F83"/>
<path id="Vector_3" d="M4.25 9.875H0.5V6.125H4.25V9.875Z" fill="#676F83"/>
<path id="Vector_4" d="M9.875 4.25H6.125V0.5H9.875V4.25Z" fill="#676F83"/>
<path id="Vector_5" d="M4.25 4.25H0.5V0.5H4.25V4.25Z" fill="#676F83"/>
<path id="Vector_6" d="M15.5 4.25H11.75V0.5H15.5V4.25Z" fill="#676F83"/>
<path id="Vector_7" d="M14.875 12.375H12.375V14.875H14.875V12.375Z" fill="#676F83"/>
<path id="Vector_8" d="M14.875 6.75H12.375V9.25H14.875V6.75Z" fill="#676F83"/>
<path id="Vector_9" d="M9.25 12.375H6.75V14.875H9.25V12.375Z" fill="#676F83"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 810 B

View File

@ -0,0 +1,9 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon L">
<g id="Subtract">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3333 1.66667C11.3333 1.29848 11.0348 1 10.6666 1C10.2984 1 9.99992 1.29848 9.99992 1.66667V14.3333C9.99992 14.7015 10.2984 15 10.6666 15C11.0348 15 11.3333 14.7015 11.3333 14.3333V13.3333H12.6666C13.7712 13.3333 14.6666 12.4379 14.6666 11.3333V4.66667C14.6666 3.5621 13.7712 2.66667 12.6666 2.66667H11.3333V1.66667ZM12.6666 12H11.3333V4H12.6666C13.0348 4 13.3333 4.29847 13.3333 4.66667V11.3333C13.3333 11.7015 13.0348 12 12.6666 12Z" fill="#354052"/>
<path d="M8.66658 13.3333V12H3.33325C2.96506 12 2.66659 11.7015 2.66659 11.3333V4.66667C2.66659 4.29848 2.96506 4 3.33325 4H8.66658V2.66667H3.33325C2.22868 2.66667 1.33325 3.5621 1.33325 4.66667V11.3333C1.33325 12.4379 2.22869 13.3333 3.33325 13.3333H8.66658Z" fill="#354052"/>
<path d="M8.66658 5.24892C8.63219 5.24484 8.59703 5.24339 8.56132 5.24478L8.51461 5.24659C7.98481 5.26717 7.48663 5.51351 7.15247 5.92673L6.57985 6.63483L6.44192 6.22222C6.26445 5.69137 5.75558 5.35376 5.20721 5.37506L4.4811 5.40327C4.11319 5.41756 3.82653 5.7274 3.84082 6.09531C3.85511 6.46322 4.16494 6.74989 4.53286 6.7356L5.19902 6.70972L5.58518 7.86484L4.47727 9.23487C4.38815 9.34509 4.25098 9.41522 4.10014 9.42108L4.05343 9.4229C3.68552 9.43719 3.39885 9.74702 3.41314 10.1149C3.42743 10.4828 3.73727 10.7695 4.10518 10.7552L4.15189 10.7534C4.68169 10.7328 5.17987 10.4865 5.51403 10.0733L6.08671 9.3651L6.22466 9.77778C6.40213 10.3086 6.911 10.6462 7.45937 10.6249C7.46692 10.6246 7.47448 10.6242 7.48202 10.6237L8.66658 10.5372V9.20033L7.46676 9.2879L7.08138 8.13509L8.18923 6.76513C8.27836 6.65491 8.41553 6.58478 8.56637 6.57892L8.61307 6.5771C8.63111 6.5764 8.64896 6.57499 8.66658 6.5729V5.24892Z" fill="#354052"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,11 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="pipeline-fill">
<g id="Vector">
<path d="M3.7501 2.56885C2.6835 2.56885 1.81885 3.4335 1.81885 4.5001C1.81885 5.5667 2.6835 6.43135 3.7501 6.43135H6.0001C7.0667 6.43135 7.93135 5.5667 7.93135 4.5001C7.93135 3.4335 7.0667 2.56885 6.0001 2.56885H3.7501Z" fill="#155AEF"/>
<path d="M11.2501 7.06885C10.1835 7.06885 9.31885 7.9335 9.31885 9.0001C9.31885 10.0667 10.1835 10.9313 11.2501 10.9313H13.5001C14.5667 10.9313 15.4313 10.0667 15.4313 9.0001C15.4313 7.9335 14.5667 7.06885 13.5001 7.06885H11.2501Z" fill="#155AEF"/>
<path d="M1.81885 13.5001C1.81885 12.4335 2.6835 11.5688 3.7501 11.5688H6.0001C7.0667 11.5688 7.93135 12.4335 7.93135 13.5001C7.93135 14.5667 7.0667 15.4313 6.0001 15.4313H3.7501C2.6835 15.4313 1.81885 14.5667 1.81885 13.5001Z" fill="#155AEF"/>
<path d="M9.75011 5.25011H9.00011V3.75011H9.75011C10.9927 3.75011 12.0001 4.75747 12.0001 6.00011H10.5001C10.5001 5.58589 10.1643 5.25011 9.75011 5.25011Z" fill="#155AEF"/>
<path d="M12.0001 12.0001C12.0001 13.2427 10.9927 14.2501 9.75011 14.2501H9.00011V12.7501H9.75011C10.1643 12.7501 10.5001 12.4143 10.5001 12.0001H12.0001Z" fill="#155AEF"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Some files were not shown because too many files have changed in this diff Show More