mirror of
https://github.com/langgenius/dify.git
synced 2026-01-02 04:27:16 +00:00
Compare commits
20 Commits
fix/app-no
...
feat/knowl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b31ee1f6f7 | ||
|
|
bb45f646dc | ||
|
|
26bd253c2d | ||
|
|
0756b49a7c | ||
|
|
982bca5d40 | ||
|
|
c8dcde6cd0 | ||
|
|
8f9db61688 | ||
|
|
ebdbaf34e6 | ||
|
|
a081b1e79e | ||
|
|
38c31e64db | ||
|
|
ae6f67420c | ||
|
|
25711ffae2 | ||
|
|
f127e10e0c | ||
|
|
7616ef8c22 | ||
|
|
6c69baf025 | ||
|
|
08bd96f170 | ||
|
|
684f7188f4 | ||
|
|
ebad19c9f7 | ||
|
|
49674507c6 | ||
|
|
80ad81471b |
@@ -498,6 +498,11 @@ class AuthConfig(BaseSettings):
|
||||
default=86400,
|
||||
)
|
||||
|
||||
FORGOT_PASSWORD_LOCKOUT_DURATION: PositiveInt = Field(
|
||||
description="Time (in seconds) a user must wait before retrying password reset after exceeding the rate limit.",
|
||||
default=86400,
|
||||
)
|
||||
|
||||
|
||||
class ModerationConfig(BaseSettings):
|
||||
"""
|
||||
|
||||
@@ -59,3 +59,9 @@ class EmailCodeAccountDeletionRateLimitExceededError(BaseHTTPException):
|
||||
error_code = "email_code_account_deletion_rate_limit_exceeded"
|
||||
description = "Too many account deletion emails have been sent. Please try again in 5 minutes."
|
||||
code = 429
|
||||
|
||||
|
||||
class EmailPasswordResetLimitError(BaseHTTPException):
|
||||
error_code = "email_password_reset_limit"
|
||||
description = "Too many failed password reset attempts. Please try again in 24 hours."
|
||||
code = 429
|
||||
|
||||
@@ -6,7 +6,13 @@ from flask_restful import Resource, reqparse # type: ignore
|
||||
|
||||
from constants.languages import languages
|
||||
from controllers.console import api
|
||||
from controllers.console.auth.error import EmailCodeError, InvalidEmailError, InvalidTokenError, PasswordMismatchError
|
||||
from controllers.console.auth.error import (
|
||||
EmailCodeError,
|
||||
EmailPasswordResetLimitError,
|
||||
InvalidEmailError,
|
||||
InvalidTokenError,
|
||||
PasswordMismatchError,
|
||||
)
|
||||
from controllers.console.error import AccountInFreezeError, AccountNotFound, EmailSendIpLimitError
|
||||
from controllers.console.wraps import setup_required
|
||||
from events.tenant_event import tenant_was_created
|
||||
@@ -62,6 +68,10 @@ class ForgotPasswordCheckApi(Resource):
|
||||
|
||||
user_email = args["email"]
|
||||
|
||||
is_forgot_password_error_rate_limit = AccountService.is_forgot_password_error_rate_limit(args["email"])
|
||||
if is_forgot_password_error_rate_limit:
|
||||
raise EmailPasswordResetLimitError()
|
||||
|
||||
token_data = AccountService.get_reset_password_data(args["token"])
|
||||
if token_data is None:
|
||||
raise InvalidTokenError()
|
||||
@@ -70,8 +80,10 @@ class ForgotPasswordCheckApi(Resource):
|
||||
raise InvalidEmailError()
|
||||
|
||||
if args["code"] != token_data.get("code"):
|
||||
AccountService.add_forgot_password_error_rate_limit(args["email"])
|
||||
raise EmailCodeError()
|
||||
|
||||
AccountService.reset_forgot_password_error_rate_limit(args["email"])
|
||||
return {"is_valid": True, "email": token_data.get("email")}
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,13 @@
|
||||
- deepseek-ai/DeepSeek-V2.5
|
||||
- deepseek-ai/DeepSeek-V3
|
||||
- deepseek-ai/DeepSeek-Coder-V2-Instruct
|
||||
- deepseek-ai/DeepSeek-R1-Distill-Llama-8B
|
||||
- deepseek-ai/DeepSeek-R1-Distill-Llama-70B
|
||||
- deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
|
||||
- deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
|
||||
- deepseek-ai/DeepSeek-R1-Distill-Qwen-14B
|
||||
- deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
|
||||
- deepseek-ai/Janus-Pro-7B
|
||||
- THUDM/glm-4-9b-chat
|
||||
- 01-ai/Yi-1.5-34B-Chat-16K
|
||||
- 01-ai/Yi-1.5-9B-Chat-16K
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
model: deepseek-ai/DeepSeek-R1-Distill-Llama-70B
|
||||
label:
|
||||
zh_Hans: deepseek-ai/DeepSeek-R1-Distill-Llama-70B
|
||||
en_US: deepseek-ai/DeepSeek-R1-Distill-Llama-70B
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 32000
|
||||
parameter_rules:
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
min: 1
|
||||
max: 8192
|
||||
default: 4096
|
||||
pricing:
|
||||
input: "0.00"
|
||||
output: "4.3"
|
||||
unit: "0.000001"
|
||||
currency: RMB
|
||||
@@ -0,0 +1,21 @@
|
||||
model: deepseek-ai/DeepSeek-R1-Distill-Llama-8B
|
||||
label:
|
||||
zh_Hans: deepseek-ai/DeepSeek-R1-Distill-Llama-8B
|
||||
en_US: deepseek-ai/DeepSeek-R1-Distill-Llama-8B
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 32000
|
||||
parameter_rules:
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
min: 1
|
||||
max: 8192
|
||||
default: 4096
|
||||
pricing:
|
||||
input: "0.00"
|
||||
output: "0.00"
|
||||
unit: "0.000001"
|
||||
currency: RMB
|
||||
@@ -0,0 +1,21 @@
|
||||
model: deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
|
||||
label:
|
||||
zh_Hans: deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
|
||||
en_US: deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 32000
|
||||
parameter_rules:
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
min: 1
|
||||
max: 8192
|
||||
default: 4096
|
||||
pricing:
|
||||
input: "0.00"
|
||||
output: "1.26"
|
||||
unit: "0.000001"
|
||||
currency: RMB
|
||||
@@ -0,0 +1,21 @@
|
||||
model: deepseek-ai/DeepSeek-R1-Distill-Qwen-14B
|
||||
label:
|
||||
zh_Hans: deepseek-ai/DeepSeek-R1-Distill-Qwen-14B
|
||||
en_US: deepseek-ai/DeepSeek-R1-Distill-Qwen-14B
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 32000
|
||||
parameter_rules:
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
min: 1
|
||||
max: 8192
|
||||
default: 4096
|
||||
pricing:
|
||||
input: "0.00"
|
||||
output: "0.70"
|
||||
unit: "0.000001"
|
||||
currency: RMB
|
||||
@@ -0,0 +1,21 @@
|
||||
model: deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
|
||||
label:
|
||||
zh_Hans: deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
|
||||
en_US: deepseek-ai/DeepSeek-R1-Distill-Qwen-32B
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 32000
|
||||
parameter_rules:
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
min: 1
|
||||
max: 8192
|
||||
default: 4096
|
||||
pricing:
|
||||
input: "0.00"
|
||||
output: "1.26"
|
||||
unit: "0.000001"
|
||||
currency: RMB
|
||||
@@ -0,0 +1,21 @@
|
||||
model: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
|
||||
label:
|
||||
zh_Hans: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
|
||||
en_US: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 32000
|
||||
parameter_rules:
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
min: 1
|
||||
max: 8192
|
||||
default: 4096
|
||||
pricing:
|
||||
input: "0.00"
|
||||
output: "0.00"
|
||||
unit: "0.000001"
|
||||
currency: RMB
|
||||
@@ -0,0 +1,22 @@
|
||||
model: deepseek-ai/Janus-Pro-7B
|
||||
label:
|
||||
zh_Hans: deepseek-ai/Janus-Pro-7B
|
||||
en_US: deepseek-ai/Janus-Pro-7B
|
||||
model_type: llm
|
||||
features:
|
||||
- agent-thought
|
||||
- vision
|
||||
model_properties:
|
||||
mode: chat
|
||||
context_size: 32000
|
||||
parameter_rules:
|
||||
- name: max_tokens
|
||||
use_template: max_tokens
|
||||
min: 1
|
||||
max: 8192
|
||||
default: 4096
|
||||
pricing:
|
||||
input: "0.00"
|
||||
output: "0.00"
|
||||
unit: "0.000001"
|
||||
currency: RMB
|
||||
@@ -69,6 +69,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -69,6 +69,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -69,6 +69,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -69,6 +69,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -68,6 +68,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -69,6 +69,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -69,6 +69,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -67,6 +67,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -67,6 +67,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -67,6 +67,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -67,6 +67,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -67,6 +67,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -69,6 +69,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -67,6 +67,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -68,6 +68,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -67,6 +67,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -67,6 +67,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -69,6 +69,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -67,6 +67,15 @@ parameter_rules:
|
||||
help:
|
||||
zh_Hans: 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚。
|
||||
en_US: Used to control the repeatability when generating models. Increasing repetition_penalty can reduce the duplication of model generation. 1.0 means no punishment.
|
||||
- name: enable_search
|
||||
type: boolean
|
||||
default: false
|
||||
label:
|
||||
zh_Hans: 联网搜索
|
||||
en_US: Web Search
|
||||
help:
|
||||
zh_Hans: 模型内置了互联网搜索服务,该参数控制模型在生成文本时是否参考使用互联网搜索结果。启用互联网搜索,模型会将搜索结果作为文本生成过程中的参考信息,但模型会基于其内部逻辑“自行判断”是否使用互联网搜索结果。
|
||||
en_US: The model has a built-in Internet search service. This parameter controls whether the model refers to Internet search results when generating text. When Internet search is enabled, the model will use the search results as reference information in the text generation process, but the model will "judge" whether to use Internet search results based on its internal logic.
|
||||
- name: response_format
|
||||
use_template: response_format
|
||||
pricing:
|
||||
|
||||
@@ -32,7 +32,11 @@ class AwsS3Storage(BaseStorage):
|
||||
aws_access_key_id=dify_config.S3_ACCESS_KEY,
|
||||
endpoint_url=dify_config.S3_ENDPOINT,
|
||||
region_name=dify_config.S3_REGION,
|
||||
config=Config(s3={"addressing_style": dify_config.S3_ADDRESS_STYLE}),
|
||||
config=Config(
|
||||
s3={"addressing_style": dify_config.S3_ADDRESS_STYLE},
|
||||
request_checksum_calculation="when_required",
|
||||
response_checksum_validation="when_required",
|
||||
),
|
||||
)
|
||||
# create bucket
|
||||
try:
|
||||
|
||||
@@ -77,6 +77,7 @@ class AccountService:
|
||||
prefix="email_code_account_deletion_rate_limit", max_attempts=1, time_window=60 * 1
|
||||
)
|
||||
LOGIN_MAX_ERROR_LIMITS = 5
|
||||
FORGOT_PASSWORD_MAX_ERROR_LIMITS = 5
|
||||
|
||||
@staticmethod
|
||||
def _get_refresh_token_key(refresh_token: str) -> str:
|
||||
@@ -503,6 +504,32 @@ class AccountService:
|
||||
key = f"login_error_rate_limit:{email}"
|
||||
redis_client.delete(key)
|
||||
|
||||
@staticmethod
|
||||
def add_forgot_password_error_rate_limit(email: str) -> None:
|
||||
key = f"forgot_password_error_rate_limit:{email}"
|
||||
count = redis_client.get(key)
|
||||
if count is None:
|
||||
count = 0
|
||||
count = int(count) + 1
|
||||
redis_client.setex(key, dify_config.FORGOT_PASSWORD_LOCKOUT_DURATION, count)
|
||||
|
||||
@staticmethod
|
||||
def is_forgot_password_error_rate_limit(email: str) -> bool:
|
||||
key = f"forgot_password_error_rate_limit:{email}"
|
||||
count = redis_client.get(key)
|
||||
if count is None:
|
||||
return False
|
||||
|
||||
count = int(count)
|
||||
if count > AccountService.FORGOT_PASSWORD_MAX_ERROR_LIMITS:
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def reset_forgot_password_error_rate_limit(email: str):
|
||||
key = f"forgot_password_error_rate_limit:{email}"
|
||||
redis_client.delete(key)
|
||||
|
||||
@staticmethod
|
||||
def is_email_send_ip_limit(ip_address: str):
|
||||
minute_key = f"email_send_ip_limit_minute:{ip_address}"
|
||||
|
||||
@@ -161,9 +161,9 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn(s.app, 'flex', 'overflow-hidden')}>
|
||||
<div className={cn(s.app, 'flex relative', 'overflow-hidden')}>
|
||||
{appDetail && (
|
||||
<AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background} desc={appDetail.mode} navigation={navigation} />
|
||||
<AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background as string} desc={appDetail.mode} navigation={navigation} />
|
||||
)}
|
||||
<div className="bg-components-panel-bg grow overflow-hidden">
|
||||
{children}
|
||||
|
||||
@@ -24,9 +24,11 @@ import AppContext from '@/context/app-context'
|
||||
|
||||
export type ICardViewProps = {
|
||||
appId: string
|
||||
isInPanel?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
const CardView: FC<ICardViewProps> = ({ appId }) => {
|
||||
const CardView: FC<ICardViewProps> = ({ appId, isInPanel, className }) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useContext(ToastContext)
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
@@ -120,10 +122,11 @@ const CardView: FC<ICardViewProps> = ({ appId }) => {
|
||||
return <Loading />
|
||||
|
||||
return (
|
||||
<div className="grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6">
|
||||
<div className={className || 'grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'}>
|
||||
<AppCard
|
||||
appInfo={appDetail}
|
||||
cardType="webapp"
|
||||
isInPanel={isInPanel}
|
||||
onChangeStatus={onChangeSiteStatus}
|
||||
onGenerateCode={onGenerateCode}
|
||||
onSaveSiteConfig={onSaveSiteConfig}
|
||||
@@ -131,6 +134,7 @@ const CardView: FC<ICardViewProps> = ({ appId }) => {
|
||||
<AppCard
|
||||
cardType="api"
|
||||
appInfo={appDetail}
|
||||
isInPanel={isInPanel}
|
||||
onChangeStatus={onChangeApiStatus}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
// CommandLineIcon as CommandLineSolidIcon,
|
||||
DocumentTextIcon as DocumentTextSolidIcon,
|
||||
} from '@heroicons/react/24/solid'
|
||||
import { RiApps2AddLine, RiInformation2Line } from '@remixicon/react'
|
||||
import { RiApps2AddLine, RiBookOpenLine, RiInformation2Line } from '@remixicon/react'
|
||||
import s from './style.module.css'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { fetchDatasetDetail, fetchDatasetRelatedApps } from '@/service/datasets'
|
||||
@@ -58,13 +58,6 @@ const TargetSolidIcon = ({ className }: SVGProps<SVGElement>) => {
|
||||
</svg>
|
||||
}
|
||||
|
||||
const BookOpenIcon = ({ className }: SVGProps<SVGElement>) => {
|
||||
return <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
|
||||
<path opacity="0.12" d="M1 3.1C1 2.53995 1 2.25992 1.10899 2.04601C1.20487 1.85785 1.35785 1.70487 1.54601 1.60899C1.75992 1.5 2.03995 1.5 2.6 1.5H2.8C3.9201 1.5 4.48016 1.5 4.90798 1.71799C5.28431 1.90973 5.59027 2.21569 5.78201 2.59202C6 3.01984 6 3.5799 6 4.7V10.5L5.94997 10.425C5.60265 9.90398 5.42899 9.64349 5.19955 9.45491C4.99643 9.28796 4.76238 9.1627 4.5108 9.0863C4.22663 9 3.91355 9 3.28741 9H2.6C2.03995 9 1.75992 9 1.54601 8.89101C1.35785 8.79513 1.20487 8.64215 1.10899 8.45399C1 8.24008 1 7.96005 1 7.4V3.1Z" fill="#155EEF" />
|
||||
<path d="M6 10.5L5.94997 10.425C5.60265 9.90398 5.42899 9.64349 5.19955 9.45491C4.99643 9.28796 4.76238 9.1627 4.5108 9.0863C4.22663 9 3.91355 9 3.28741 9H2.6C2.03995 9 1.75992 9 1.54601 8.89101C1.35785 8.79513 1.20487 8.64215 1.10899 8.45399C1 8.24008 1 7.96005 1 7.4V3.1C1 2.53995 1 2.25992 1.10899 2.04601C1.20487 1.85785 1.35785 1.70487 1.54601 1.60899C1.75992 1.5 2.03995 1.5 2.6 1.5H2.8C3.9201 1.5 4.48016 1.5 4.90798 1.71799C5.28431 1.90973 5.59027 2.21569 5.78201 2.59202C6 3.01984 6 3.5799 6 4.7M6 10.5V4.7M6 10.5L6.05003 10.425C6.39735 9.90398 6.57101 9.64349 6.80045 9.45491C7.00357 9.28796 7.23762 9.1627 7.4892 9.0863C7.77337 9 8.08645 9 8.71259 9H9.4C9.96005 9 10.2401 9 10.454 8.89101C10.6422 8.79513 10.7951 8.64215 10.891 8.45399C11 8.24008 11 7.96005 11 7.4V3.1C11 2.53995 11 2.25992 10.891 2.04601C10.7951 1.85785 10.6422 1.70487 10.454 1.60899C10.2401 1.5 9.96005 1.5 9.4 1.5H9.2C8.07989 1.5 7.51984 1.5 7.09202 1.71799C6.71569 1.90973 6.40973 2.21569 6.21799 2.59202C6 3.01984 6 3.5799 6 4.7" stroke="#155EEF" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
}
|
||||
|
||||
type IExtraInfoProps = {
|
||||
isMobile: boolean
|
||||
relatedApps?: RelatedAppResponse
|
||||
@@ -131,7 +124,7 @@ const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => {
|
||||
}
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
>
|
||||
<BookOpenIcon className='mr-1' />
|
||||
<RiBookOpenLine className='mr-1 text-text-accent' />
|
||||
{t('common.datasetMenus.viewDoc')}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -31,8 +31,6 @@ const ApiServer: FC<ApiServerProps> = ({
|
||||
</div>
|
||||
<SecretKeyButton
|
||||
className='flex-shrink-0 !h-8 bg-white'
|
||||
textCls='!text-gray-700 font-medium'
|
||||
iconCls='stroke-[1.2px]'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -192,7 +192,7 @@ const DatasetCard = ({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px] bg-gray-200' />
|
||||
<div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px] bg-divider-regular' />
|
||||
<div className='!hidden group-hover:!flex shrink-0'>
|
||||
<CustomPopover
|
||||
htmlContent={<Operations showDelete={!isCurrentWorkspaceDatasetOperator} />}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useContext, useContextSelector } from 'use-context-selector'
|
||||
import { RiArrowDownSLine } from '@remixicon/react'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import {
|
||||
RiDeleteBinLine,
|
||||
RiEditLine,
|
||||
RiEqualizer2Line,
|
||||
RiFileCopy2Line,
|
||||
RiFileDownloadLine,
|
||||
RiFileUploadLine,
|
||||
} from '@remixicon/react'
|
||||
import AppIcon from '../base/app-icon'
|
||||
import SwitchAppModal from '../app/switch-app-modal'
|
||||
import s from './style.module.css'
|
||||
import cn from '@/utils/classnames'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
@@ -22,8 +22,6 @@ import { copyApp, deleteApp, exportAppConfig, updateAppInfo } from '@/service/ap
|
||||
import DuplicateAppModal from '@/app/components/app/duplicate-modal'
|
||||
import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal'
|
||||
import CreateAppModal from '@/app/components/explore/create-app-modal'
|
||||
import { AiText, ChatBot, CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication'
|
||||
import { Route } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
|
||||
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
@@ -31,6 +29,9 @@ import UpdateDSLModal from '@/app/components/workflow/update-dsl-modal'
|
||||
import type { EnvironmentVariable } from '@/app/components/workflow/types'
|
||||
import DSLExportConfirmModal from '@/app/components/workflow/dsl-export-confirm-modal'
|
||||
import { fetchWorkflowDraft } from '@/service/workflow'
|
||||
import ContentDialog from '@/app/components/base/content-dialog'
|
||||
import Button from '@/app/components/base/button'
|
||||
import CardView from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView'
|
||||
|
||||
export type IAppInfoProps = {
|
||||
expand: boolean
|
||||
@@ -47,7 +48,6 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
||||
const [showEditModal, setShowEditModal] = useState(false)
|
||||
const [showDuplicateModal, setShowDuplicateModal] = useState(false)
|
||||
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
||||
const [showSwitchTip, setShowSwitchTip] = useState<string>('')
|
||||
const [showSwitchModal, setShowSwitchModal] = useState<boolean>(false)
|
||||
const [showImportDSLModal, setShowImportDSLModal] = useState<boolean>(false)
|
||||
const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([])
|
||||
@@ -183,291 +183,199 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
||||
return null
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
placement='bottom-start'
|
||||
offset={4}
|
||||
>
|
||||
<div className='relative'>
|
||||
<PortalToFollowElemTrigger
|
||||
onClick={() => {
|
||||
if (isCurrentWorkspaceEditor)
|
||||
setOpen(v => !v)
|
||||
}}
|
||||
className='block'
|
||||
>
|
||||
<div className={cn('flex p-1 rounded-lg', open && 'bg-gray-100', isCurrentWorkspaceEditor && 'hover:bg-gray-100 cursor-pointer')}>
|
||||
<div className='relative shrink-0 mr-2'>
|
||||
<AppIcon
|
||||
size={expand ? 'large' : 'small'}
|
||||
iconType={appDetail.icon_type}
|
||||
icon={appDetail.icon}
|
||||
background={appDetail.icon_background}
|
||||
imageUrl={appDetail.icon_url}
|
||||
/>
|
||||
<span className={cn(
|
||||
'absolute bottom-[-3px] right-[-3px] w-4 h-4 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm',
|
||||
!expand && '!w-3.5 !h-3.5 !bottom-[-2px] !right-[-2px]',
|
||||
)}>
|
||||
{appDetail.mode === 'advanced-chat' && (
|
||||
<ChatBot className={cn('w-3 h-3 text-[#1570EF]', !expand && '!w-2.5 !h-2.5')} />
|
||||
)}
|
||||
{appDetail.mode === 'agent-chat' && (
|
||||
<CuteRobot className={cn('w-3 h-3 text-indigo-600', !expand && '!w-2.5 !h-2.5')} />
|
||||
)}
|
||||
{appDetail.mode === 'chat' && (
|
||||
<ChatBot className={cn('w-3 h-3 text-[#1570EF]', !expand && '!w-2.5 !h-2.5')} />
|
||||
)}
|
||||
{appDetail.mode === 'completion' && (
|
||||
<AiText className={cn('w-3 h-3 text-[#0E9384]', !expand && '!w-2.5 !h-2.5')} />
|
||||
)}
|
||||
{appDetail.mode === 'workflow' && (
|
||||
<Route className={cn('w-3 h-3 text-[#f79009]', !expand && '!w-2.5 !h-2.5')} />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
{expand && (
|
||||
<div className="grow w-0">
|
||||
<div className='flex justify-between items-center text-sm leading-5 font-medium text-text-secondary'>
|
||||
<div className='truncate' title={appDetail.name}>{appDetail.name}</div>
|
||||
{isCurrentWorkspaceEditor && <RiArrowDownSLine className='shrink-0 ml-[2px] w-3 h-3 text-gray-500' />}
|
||||
</div>
|
||||
<div className='flex items-center text-[10px] leading-[18px] font-medium text-gray-500 gap-1'>
|
||||
{appDetail.mode === 'advanced-chat' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
||||
<div title={t('app.types.advanced') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.advanced').toUpperCase()}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'agent-chat' && (
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.agent').toUpperCase()}</div>
|
||||
)}
|
||||
{appDetail.mode === 'chat' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
||||
<div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'completion' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.completion').toUpperCase()}</div>
|
||||
<div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'workflow' && (
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.workflow').toUpperCase()}</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (isCurrentWorkspaceEditor)
|
||||
setOpen(v => !v)
|
||||
}}
|
||||
className='block w-full'
|
||||
>
|
||||
<div className={cn('flex rounded-lg', expand ? 'p-2 pb-2.5 flex-col gap-2' : 'p-1 gap-1 justify-center items-start', open && 'bg-state-base-hover', isCurrentWorkspaceEditor && 'hover:bg-state-base-hover cursor-pointer')}>
|
||||
<div className={`flex items-center self-stretch ${expand ? 'justify-between' : 'flex-col gap-1'}`}>
|
||||
<AppIcon
|
||||
size={expand ? 'large' : 'small'}
|
||||
iconType={appDetail.icon_type}
|
||||
icon={appDetail.icon}
|
||||
background={appDetail.icon_background}
|
||||
imageUrl={appDetail.icon_url}
|
||||
/>
|
||||
<div className='flex p-0.5 justify-center items-center rounded-md'>
|
||||
<div className='flex w-5 h-5 justify-center items-center'>
|
||||
<RiEqualizer2Line className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[1002]'>
|
||||
<div className='relative w-[320px] bg-white rounded-2xl shadow-xl'>
|
||||
{/* header */}
|
||||
<div className={cn('flex pl-4 pt-3 pr-3', !appDetail.description && 'pb-2')}>
|
||||
<div className='relative shrink-0 mr-2'>
|
||||
<AppIcon
|
||||
size="large"
|
||||
iconType={appDetail.icon_type}
|
||||
icon={appDetail.icon}
|
||||
background={appDetail.icon_background}
|
||||
imageUrl={appDetail.icon_url}
|
||||
/>
|
||||
<span className='absolute bottom-[-3px] right-[-3px] w-4 h-4 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm'>
|
||||
{appDetail.mode === 'advanced-chat' && (
|
||||
<ChatBot className='w-3 h-3 text-[#1570EF]' />
|
||||
)}
|
||||
{appDetail.mode === 'agent-chat' && (
|
||||
<CuteRobot className='w-3 h-3 text-indigo-600' />
|
||||
)}
|
||||
{appDetail.mode === 'chat' && (
|
||||
<ChatBot className='w-3 h-3 text-[#1570EF]' />
|
||||
)}
|
||||
{appDetail.mode === 'completion' && (
|
||||
<AiText className='w-3 h-3 text-[#0E9384]' />
|
||||
)}
|
||||
{appDetail.mode === 'workflow' && (
|
||||
<Route className='w-3 h-3 text-[#f79009]' />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className='grow w-0'>
|
||||
<div title={appDetail.name} className='flex justify-between items-center text-sm leading-5 font-medium text-gray-900 truncate'>{appDetail.name}</div>
|
||||
<div className='flex items-center text-[10px] leading-[18px] font-medium text-gray-500 gap-1'>
|
||||
{appDetail.mode === 'advanced-chat' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
||||
<div title={t('app.types.advanced') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.advanced').toUpperCase()}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'agent-chat' && (
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.agent').toUpperCase()}</div>
|
||||
)}
|
||||
{appDetail.mode === 'chat' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
||||
<div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'completion' && (
|
||||
<>
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.completion').toUpperCase()}</div>
|
||||
<div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
|
||||
</>
|
||||
)}
|
||||
{appDetail.mode === 'workflow' && (
|
||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.workflow').toUpperCase()}</div>
|
||||
)}
|
||||
{
|
||||
expand && (
|
||||
<div className='flex flex-col items-start gap-1'>
|
||||
<div className='flex w-full'>
|
||||
<div className='text-text-secondary system-md-semibold truncate'>{appDetail.name}</div>
|
||||
</div>
|
||||
<div className='text-text-tertiary system-2xs-medium-uppercase'>{appDetail.mode === 'advanced-chat' ? t('app.types.chatbot') : appDetail.mode === 'agent-chat' ? t('app.types.agent') : appDetail.mode === 'chat' ? t('app.types.chatbot') : appDetail.mode === 'completion' ? t('app.types.completion') : t('app.types.workflow')}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
<ContentDialog
|
||||
show={open}
|
||||
onClose={() => setOpen(false)}
|
||||
className='!p-0 flex flex-col absolute left-2 top-2 bottom-2 w-[420px] rounded-2xl'
|
||||
>
|
||||
<div className='flex p-4 flex-col justify-center items-start gap-3 self-stretch shrink-0'>
|
||||
<div className='flex items-center gap-3 self-stretch'>
|
||||
<AppIcon
|
||||
size="large"
|
||||
iconType={appDetail.icon_type}
|
||||
icon={appDetail.icon}
|
||||
background={appDetail.icon_background}
|
||||
imageUrl={appDetail.icon_url}
|
||||
/>
|
||||
<div className='flex flex-col justify-center items-start grow w-full'>
|
||||
<div className='text-text-secondary system-md-semibold truncate w-full'>{appDetail.name}</div>
|
||||
<div className='text-text-tertiary system-2xs-medium-uppercase'>{appDetail.mode === 'advanced-chat' ? t('app.types.chatbot') : appDetail.mode === 'agent-chat' ? t('app.types.agent') : appDetail.mode === 'chat' ? t('app.types.chatbot') : appDetail.mode === 'completion' ? t('app.types.completion') : t('app.types.workflow')}</div>
|
||||
</div>
|
||||
{/* description */}
|
||||
{appDetail.description && (
|
||||
<div className='px-4 py-2 text-gray-500 text-xs leading-[18px]'>{appDetail.description}</div>
|
||||
)}
|
||||
{/* operations */}
|
||||
<Divider className="!my-1" />
|
||||
<div className="w-full py-1">
|
||||
<div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={() => {
|
||||
</div>
|
||||
{/* description */}
|
||||
{appDetail.description && (
|
||||
<div className='text-text-tertiary system-xs-regular'>{appDetail.description}</div>
|
||||
)}
|
||||
{/* operations */}
|
||||
<div className='flex items-center gap-1 self-stretch'>
|
||||
<Button
|
||||
size={'small'}
|
||||
variant={'secondary'}
|
||||
className='gap-[1px]'
|
||||
onClick={() => {
|
||||
setOpen(false)
|
||||
setShowEditModal(true)
|
||||
}}>
|
||||
<span className='text-gray-700 text-sm leading-5'>{t('app.editApp')}</span>
|
||||
</div>
|
||||
<div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={() => {
|
||||
}}
|
||||
>
|
||||
<RiEditLine className='w-3.5 h-3.5 text-components-button-secondary-text' />
|
||||
<span className='text-components-button-secondary-text system-xs-medium'>{t('app.editApp')}</span>
|
||||
</Button>
|
||||
<Button
|
||||
size={'small'}
|
||||
variant={'secondary'}
|
||||
className='gap-[1px]'
|
||||
onClick={() => {
|
||||
setOpen(false)
|
||||
setShowDuplicateModal(true)
|
||||
}}>
|
||||
<span className='text-gray-700 text-sm leading-5'>{t('app.duplicate')}</span>
|
||||
</div>
|
||||
{(appDetail.mode === 'completion' || appDetail.mode === 'chat') && (
|
||||
<>
|
||||
<Divider className="!my-1" />
|
||||
<div
|
||||
className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer'
|
||||
onMouseEnter={() => setShowSwitchTip(appDetail.mode)}
|
||||
onMouseLeave={() => setShowSwitchTip('')}
|
||||
onClick={() => {
|
||||
setOpen(false)
|
||||
setShowSwitchModal(true)
|
||||
}}
|
||||
>
|
||||
<span className='text-gray-700 text-sm leading-5'>{t('app.switch')}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<Divider className="!my-1" />
|
||||
<div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={exportCheck}>
|
||||
<span className='text-gray-700 text-sm leading-5'>{t('app.export')}</span>
|
||||
</div>
|
||||
{
|
||||
(appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (
|
||||
<div
|
||||
className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer'
|
||||
onClick={() => {
|
||||
setOpen(false)
|
||||
setShowImportDSLModal(true)
|
||||
}}>
|
||||
<span className='text-gray-700 text-sm leading-5'>{t('workflow.common.importDSL')}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<Divider className="!my-1" />
|
||||
<div className='group h-9 py-2 px-3 mx-1 flex items-center hover:bg-red-50 rounded-lg cursor-pointer' onClick={() => {
|
||||
setOpen(false)
|
||||
setShowConfirmDelete(true)
|
||||
}}>
|
||||
<span className='text-gray-700 text-sm leading-5 group-hover:text-red-500'>
|
||||
{t('common.operation.delete')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* switch tip */}
|
||||
<div
|
||||
className={cn(
|
||||
'hidden absolute left-[324px] top-0 w-[376px] rounded-xl bg-white border-[0.5px] border-[rgba(0,0,0,0.05)] shadow-lg',
|
||||
showSwitchTip && '!block',
|
||||
)}
|
||||
}}
|
||||
>
|
||||
<div className={cn(
|
||||
'w-full h-[256px] bg-center bg-no-repeat bg-contain rounded-xl',
|
||||
showSwitchTip === 'chat' && s.expertPic,
|
||||
showSwitchTip === 'completion' && s.completionPic,
|
||||
)} />
|
||||
<div className='px-4 pb-2'>
|
||||
<div className='flex items-center gap-1 text-gray-700 text-md leading-6 font-semibold'>
|
||||
{showSwitchTip === 'chat' ? t('app.types.advanced') : t('app.types.workflow')}
|
||||
<span className='px-1 rounded-[5px] bg-white border border-black/8 text-gray-500 text-[10px] leading-[18px] font-medium'>BETA</span>
|
||||
</div>
|
||||
<div className='text-orange-500 text-xs leading-[18px] font-medium'>{t('app.newApp.advancedFor').toLocaleUpperCase()}</div>
|
||||
<div className='mt-1 text-gray-500 text-sm leading-5'>{t('app.newApp.advancedDescription')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<RiFileCopy2Line className='w-3.5 h-3.5 text-components-button-secondary-text' />
|
||||
<span className='text-components-button-secondary-text system-xs-medium'>{t('app.duplicate')}</span>
|
||||
</Button>
|
||||
<Button
|
||||
size={'small'}
|
||||
variant={'secondary'}
|
||||
className='gap-[1px]'
|
||||
onClick={exportCheck}
|
||||
>
|
||||
<RiFileDownloadLine className='w-3.5 h-3.5 text-components-button-secondary-text' />
|
||||
<span className='text-components-button-secondary-text system-xs-medium'>{t('app.export')}</span>
|
||||
</Button>
|
||||
{
|
||||
(appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (
|
||||
<Button
|
||||
size={'small'}
|
||||
variant={'secondary'}
|
||||
className='gap-[1px]'
|
||||
onClick={() => {
|
||||
setOpen(false)
|
||||
setShowImportDSLModal(true)
|
||||
}}
|
||||
>
|
||||
<RiFileUploadLine className='w-3.5 h-3.5 text-components-button-secondary-text' />
|
||||
<span className='text-components-button-secondary-text system-xs-medium'>{t('workflow.common.importDSL')}</span>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
{showSwitchModal && (
|
||||
<SwitchAppModal
|
||||
inAppDetail
|
||||
show={showSwitchModal}
|
||||
appDetail={appDetail}
|
||||
onClose={() => setShowSwitchModal(false)}
|
||||
onSuccess={() => setShowSwitchModal(false)}
|
||||
</div>
|
||||
<div className='flex flex-1'>
|
||||
<CardView
|
||||
appId={appDetail.id}
|
||||
isInPanel={true}
|
||||
className='flex flex-col px-2 py-1 gap-2 grow overflow-auto'
|
||||
/>
|
||||
)}
|
||||
{showEditModal && (
|
||||
<CreateAppModal
|
||||
isEditModal
|
||||
appName={appDetail.name}
|
||||
appIconType={appDetail.icon_type}
|
||||
appIcon={appDetail.icon}
|
||||
appIconBackground={appDetail.icon_background}
|
||||
appIconUrl={appDetail.icon_url}
|
||||
appDescription={appDetail.description}
|
||||
appMode={appDetail.mode}
|
||||
appUseIconAsAnswerIcon={appDetail.use_icon_as_answer_icon}
|
||||
show={showEditModal}
|
||||
onConfirm={onEdit}
|
||||
onHide={() => setShowEditModal(false)}
|
||||
/>
|
||||
)}
|
||||
{showDuplicateModal && (
|
||||
<DuplicateAppModal
|
||||
appName={appDetail.name}
|
||||
icon_type={appDetail.icon_type}
|
||||
icon={appDetail.icon}
|
||||
icon_background={appDetail.icon_background}
|
||||
icon_url={appDetail.icon_url}
|
||||
show={showDuplicateModal}
|
||||
onConfirm={onCopy}
|
||||
onHide={() => setShowDuplicateModal(false)}
|
||||
/>
|
||||
)}
|
||||
{showConfirmDelete && (
|
||||
<Confirm
|
||||
title={t('app.deleteAppConfirmTitle')}
|
||||
content={t('app.deleteAppConfirmContent')}
|
||||
isShow={showConfirmDelete}
|
||||
onConfirm={onConfirmDelete}
|
||||
onCancel={() => setShowConfirmDelete(false)}
|
||||
/>
|
||||
)}
|
||||
{showImportDSLModal && (
|
||||
<UpdateDSLModal
|
||||
onCancel={() => setShowImportDSLModal(false)}
|
||||
onBackup={exportCheck}
|
||||
/>
|
||||
)}
|
||||
{secretEnvList.length > 0 && (
|
||||
<DSLExportConfirmModal
|
||||
envList={secretEnvList}
|
||||
onConfirm={onExport}
|
||||
onClose={() => setSecretEnvList([])}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</PortalToFollowElem>
|
||||
</div>
|
||||
<div className='flex p-2 flex-col justify-center items-start gap-3 self-stretch border-t-[0.5px] border-divider-subtle shrink-0 min-h-fit'>
|
||||
<Button
|
||||
size={'medium'}
|
||||
variant={'ghost'}
|
||||
className='gap-0.5'
|
||||
onClick={() => {
|
||||
setOpen(false)
|
||||
setShowConfirmDelete(true)
|
||||
}}
|
||||
>
|
||||
<RiDeleteBinLine className='w-4 h-4 text-text-tertiary' />
|
||||
<span className='text-text-tertiary system-sm-medium'>{t('common.operation.deleteApp')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</ContentDialog>
|
||||
{showSwitchModal && (
|
||||
<SwitchAppModal
|
||||
inAppDetail
|
||||
show={showSwitchModal}
|
||||
appDetail={appDetail}
|
||||
onClose={() => setShowSwitchModal(false)}
|
||||
onSuccess={() => setShowSwitchModal(false)}
|
||||
/>
|
||||
)}
|
||||
{showEditModal && (
|
||||
<CreateAppModal
|
||||
isEditModal
|
||||
appName={appDetail.name}
|
||||
appIconType={appDetail.icon_type}
|
||||
appIcon={appDetail.icon}
|
||||
appIconBackground={appDetail.icon_background}
|
||||
appIconUrl={appDetail.icon_url}
|
||||
appDescription={appDetail.description}
|
||||
appMode={appDetail.mode}
|
||||
appUseIconAsAnswerIcon={appDetail.use_icon_as_answer_icon}
|
||||
show={showEditModal}
|
||||
onConfirm={onEdit}
|
||||
onHide={() => setShowEditModal(false)}
|
||||
/>
|
||||
)}
|
||||
{showDuplicateModal && (
|
||||
<DuplicateAppModal
|
||||
appName={appDetail.name}
|
||||
icon_type={appDetail.icon_type}
|
||||
icon={appDetail.icon}
|
||||
icon_background={appDetail.icon_background}
|
||||
icon_url={appDetail.icon_url}
|
||||
show={showDuplicateModal}
|
||||
onConfirm={onCopy}
|
||||
onHide={() => setShowDuplicateModal(false)}
|
||||
/>
|
||||
)}
|
||||
{showConfirmDelete && (
|
||||
<Confirm
|
||||
title={t('app.deleteAppConfirmTitle')}
|
||||
content={t('app.deleteAppConfirmContent')}
|
||||
isShow={showConfirmDelete}
|
||||
onConfirm={onConfirmDelete}
|
||||
onCancel={() => setShowConfirmDelete(false)}
|
||||
/>
|
||||
)}
|
||||
{showImportDSLModal && (
|
||||
<UpdateDSLModal
|
||||
onCancel={() => setShowImportDSLModal(false)}
|
||||
onBackup={exportCheck}
|
||||
/>
|
||||
)}
|
||||
{secretEnvList.length > 0 && (
|
||||
<DSLExportConfirmModal
|
||||
envList={secretEnvList}
|
||||
onConfirm={onExport}
|
||||
onClose={() => setSecretEnvList([])}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className="flex items-start p-1">
|
||||
<div className="flex items-center grow">
|
||||
{icon && icon_background && iconType === 'app' && (
|
||||
<div className='flex-shrink-0 mr-3'>
|
||||
<AppIcon icon={icon} background={icon_background} />
|
||||
@@ -71,8 +71,10 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
|
||||
|
||||
}
|
||||
{mode === 'expand' && <div className="group">
|
||||
<div className={`flex flex-row items-center text-sm font-semibold text-gray-700 group-hover:text-gray-900 break-all ${textStyle?.main ?? ''}`}>
|
||||
{name}
|
||||
<div className={`flex flex-row items-center system-md-semibold text-text-secondary group-hover:text-text-primary ${textStyle?.main ?? ''}`}>
|
||||
<div className="max-w-[180px] truncate">
|
||||
{name}
|
||||
</div>
|
||||
{hoverTip
|
||||
&& <Tooltip
|
||||
popupContent={
|
||||
@@ -86,7 +88,6 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
<div className={`text-xs font-normal text-gray-500 group-hover:text-gray-700 break-all ${textStyle?.extra ?? ''}`}>{type}</div>
|
||||
<div className='text-text-tertiary system-2xs-medium-uppercase'>{isExternal ? t('dataset.externalTag') : ''}</div>
|
||||
</div>}
|
||||
</div>
|
||||
|
||||
@@ -57,7 +57,7 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
|
||||
<div
|
||||
className={`
|
||||
shrink-0
|
||||
${expand ? 'p-3' : 'p-2'}
|
||||
${expand ? 'p-2' : 'p-1'}
|
||||
`}
|
||||
>
|
||||
{iconType === 'app' && (
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function NavLink({
|
||||
key={name}
|
||||
href={href}
|
||||
className={classNames(
|
||||
isActive ? 'bg-state-accent-active text-text-accent font-semibold' : 'text-components-menu-item-text hover:bg-gray-100 hover:text-components-menu-item-text-hover',
|
||||
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',
|
||||
mode === 'expand' ? 'px-3' : 'px-2.5',
|
||||
)}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
'use client'
|
||||
import type { HTMLProps } from 'react'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import {
|
||||
Cog8ToothIcon,
|
||||
DocumentTextIcon,
|
||||
PaintBrushIcon,
|
||||
RocketLaunchIcon,
|
||||
} from '@heroicons/react/24/outline'
|
||||
import { usePathname, useRouter } from 'next/navigation'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiBookOpenLine,
|
||||
RiEqualizer2Line,
|
||||
RiExternalLinkLine,
|
||||
RiPaintBrushLine,
|
||||
RiWindowLine,
|
||||
} from '@remixicon/react'
|
||||
import SettingsModal from './settings'
|
||||
import EmbeddedModal from './embedded'
|
||||
import CustomizeModal from './customize'
|
||||
@@ -18,7 +18,6 @@ import Tooltip from '@/app/components/base/tooltip'
|
||||
import AppBasic from '@/app/components/app-sidebar/basic'
|
||||
import { asyncRunSafe, randomString } from '@/utils'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Tag from '@/app/components/base/tag'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import CopyFeedback from '@/app/components/base/copy-feedback'
|
||||
@@ -28,10 +27,12 @@ import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-butt
|
||||
import type { AppDetailResponse } from '@/models/app'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import type { AppSSO } from '@/types/app'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
|
||||
export type IAppCardProps = {
|
||||
className?: string
|
||||
appInfo: AppDetailResponse & Partial<AppSSO>
|
||||
isInPanel?: boolean
|
||||
cardType?: 'api' | 'webapp'
|
||||
customBgColor?: string
|
||||
onChangeStatus: (val: boolean) => Promise<void>
|
||||
@@ -39,12 +40,9 @@ export type IAppCardProps = {
|
||||
onGenerateCode?: () => Promise<void>
|
||||
}
|
||||
|
||||
const EmbedIcon = ({ className = '' }: HTMLProps<HTMLDivElement>) => {
|
||||
return <div className={`${style.codeBrowserIcon} ${className}`}></div>
|
||||
}
|
||||
|
||||
function AppCard({
|
||||
appInfo,
|
||||
isInPanel,
|
||||
cardType = 'webapp',
|
||||
customBgColor,
|
||||
onChangeStatus,
|
||||
@@ -66,17 +64,18 @@ function AppCard({
|
||||
const OPERATIONS_MAP = useMemo(() => {
|
||||
const operationsMap = {
|
||||
webapp: [
|
||||
{ opName: t('appOverview.overview.appInfo.preview'), opIcon: RocketLaunchIcon },
|
||||
{ opName: t('appOverview.overview.appInfo.customize.entry'), opIcon: PaintBrushIcon },
|
||||
{ opName: t('appOverview.overview.appInfo.launch'), opIcon: RiExternalLinkLine },
|
||||
] as { opName: string; opIcon: any }[],
|
||||
api: [{ opName: t('appOverview.overview.apiInfo.doc'), opIcon: DocumentTextIcon }],
|
||||
api: [{ opName: t('appOverview.overview.apiInfo.doc'), opIcon: RiBookOpenLine }],
|
||||
app: [],
|
||||
}
|
||||
if (appInfo.mode !== 'completion' && appInfo.mode !== 'workflow')
|
||||
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.embedded.entry'), opIcon: EmbedIcon })
|
||||
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.embedded.entry'), opIcon: RiWindowLine })
|
||||
|
||||
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.customize.entry'), opIcon: RiPaintBrushLine })
|
||||
|
||||
if (isCurrentWorkspaceEditor)
|
||||
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.settings.entry'), opIcon: Cog8ToothIcon })
|
||||
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.settings.entry'), opIcon: RiEqualizer2Line })
|
||||
|
||||
return operationsMap
|
||||
}, [isCurrentWorkspaceEditor, appInfo, t])
|
||||
@@ -92,13 +91,9 @@ function AppCard({
|
||||
const appUrl = `${app_base_url}/${appMode}/${access_token}`
|
||||
const apiUrl = appInfo?.api_base_url
|
||||
|
||||
let bgColor = 'bg-primary-50 bg-opacity-40'
|
||||
if (cardType === 'api')
|
||||
bgColor = 'bg-purple-50'
|
||||
|
||||
const genClickFuncByName = (opName: string) => {
|
||||
switch (opName) {
|
||||
case t('appOverview.overview.appInfo.preview'):
|
||||
case t('appOverview.overview.appInfo.launch'):
|
||||
return () => {
|
||||
window.open(appUrl, '_blank')
|
||||
}
|
||||
@@ -135,49 +130,50 @@ function AppCard({
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
`shadow-xs border-[0.5px] rounded-lg border-gray-200 ${className ?? ''}`}
|
||||
`${isInPanel ? 'border-l-[0.5px] border-t' : 'shadow-xs border-[0.5px]'} rounded-xl border-effects-highlight w-full max-w-full ${className ?? ''}`}
|
||||
>
|
||||
<div className={`px-6 py-5 ${customBgColor ?? bgColor} rounded-lg`}>
|
||||
<div className="mb-2.5 flex flex-row items-start justify-between">
|
||||
<AppBasic
|
||||
iconType={cardType}
|
||||
icon={appInfo.icon}
|
||||
icon_background={appInfo.icon_background}
|
||||
name={basicName}
|
||||
type={
|
||||
isApp
|
||||
? t('appOverview.overview.appInfo.explanation')
|
||||
: t('appOverview.overview.apiInfo.explanation')
|
||||
}
|
||||
/>
|
||||
<div className="flex flex-row items-center h-9">
|
||||
<Tag className="mr-2" color={runningStatus ? 'green' : 'yellow'}>
|
||||
{runningStatus
|
||||
? t('appOverview.overview.status.running')
|
||||
: t('appOverview.overview.status.disable')}
|
||||
</Tag>
|
||||
<div className={`${customBgColor ?? 'bg-background-default'} rounded-xl`}>
|
||||
<div className='flex flex-col p-3 justify-center items-start gap-3 self-stretch border-b-[0.5px] border-divider-subtle w-full'>
|
||||
<div className='flex items-center gap-3 self-stretch w-full'>
|
||||
<AppBasic
|
||||
iconType={cardType}
|
||||
icon={appInfo.icon}
|
||||
icon_background={appInfo.icon_background}
|
||||
name={basicName}
|
||||
type={
|
||||
isApp
|
||||
? t('appOverview.overview.appInfo.explanation')
|
||||
: t('appOverview.overview.apiInfo.explanation')
|
||||
}
|
||||
/>
|
||||
<div className='flex items-center gap-1'>
|
||||
<Indicator color={runningStatus ? 'green' : 'yellow'} />
|
||||
<div className={`${runningStatus ? 'text-text-success' : 'text-text-warning'} system-xs-semibold-uppercase`}>
|
||||
{runningStatus
|
||||
? t('appOverview.overview.status.running')
|
||||
: t('appOverview.overview.status.disable')}
|
||||
</div>
|
||||
</div>
|
||||
<Switch defaultValue={runningStatus} onChange={onChangeStatus} disabled={toggleDisabled} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col justify-center py-2">
|
||||
<div className="py-1">
|
||||
<div className="pb-1 text-xs text-gray-500">
|
||||
<div className='flex flex-col justify-center items-start self-stretch'>
|
||||
<div className="pb-1 system-xs-medium text-text-tertiary">
|
||||
{isApp
|
||||
? t('appOverview.overview.appInfo.accessibleAddress')
|
||||
: t('appOverview.overview.apiInfo.accessibleAddress')}
|
||||
</div>
|
||||
<div className="w-full h-9 pl-2 pr-0.5 py-0.5 bg-black bg-opacity-2 rounded-lg border border-black border-opacity-5 justify-start items-center inline-flex">
|
||||
<div className="h-4 px-2 justify-start items-start gap-2 flex flex-1 min-w-0">
|
||||
<div className="text-gray-700 text-xs font-medium text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
<div className="w-full h-9 pl-2 p-1 bg-components-input-bg-normal rounded-lg items-center inline-flex gap-0.5">
|
||||
<div className="h-4 px-1 justify-start items-start gap-2 flex flex-1 min-w-0">
|
||||
<div className="text-text-secondary text-xs font-medium text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
{isApp ? appUrl : apiUrl}
|
||||
</div>
|
||||
</div>
|
||||
<Divider type="vertical" className="!h-3.5 shrink-0 !mx-0.5" />
|
||||
{isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} selectorId={randomString(8)} className={'hover:bg-gray-200'} />}
|
||||
<CopyFeedback
|
||||
content={isApp ? appUrl : apiUrl}
|
||||
className={'hover:bg-gray-200'}
|
||||
className={'!size-6'}
|
||||
/>
|
||||
{isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} className='z-50 !size-6 hover:bg-state-base-hover rounded-md' selectorId={randomString(8)} />}
|
||||
{isApp && <Divider type="vertical" className="!h-3.5 shrink-0 !mx-0.5" />}
|
||||
{/* button copy link/ button regenerate */}
|
||||
{showConfirmDelete && (
|
||||
<Confirm
|
||||
@@ -197,7 +193,7 @@ function AppCard({
|
||||
popupContent={t('appOverview.overview.appInfo.regenerate') || ''}
|
||||
>
|
||||
<div
|
||||
className="w-8 h-8 ml-0.5 cursor-pointer hover:bg-gray-200 rounded-lg"
|
||||
className="w-6 h-6 cursor-pointer hover:bg-state-base-hover rounded-md"
|
||||
onClick={() => setShowConfirmDelete(true)}
|
||||
>
|
||||
<div
|
||||
@@ -210,8 +206,8 @@ function AppCard({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'pt-2 flex flex-row items-center flex-wrap gap-y-2'}>
|
||||
{!isApp && <SecretKeyButton className='flex-shrink-0 !h-8 bg-white mr-2' textCls='!text-gray-700 font-medium' iconCls='stroke-[1.2px]' appId={appInfo.id} />}
|
||||
<div className={'flex p-3 items-center gap-1 self-stretch'}>
|
||||
{!isApp && <SecretKeyButton appId={appInfo.id} />}
|
||||
{OPERATIONS_MAP[cardType].map((op) => {
|
||||
const disabled
|
||||
= op.opName === t('appOverview.overview.appInfo.settings.entry')
|
||||
@@ -219,7 +215,9 @@ function AppCard({
|
||||
: !runningStatus
|
||||
return (
|
||||
<Button
|
||||
className="mr-2"
|
||||
className="mr-1 min-w-[88px]"
|
||||
size="small"
|
||||
variant={'ghost'}
|
||||
key={op.opName}
|
||||
onClick={genClickFuncByName(op.opName)}
|
||||
disabled={disabled}
|
||||
@@ -230,9 +228,9 @@ function AppCard({
|
||||
}
|
||||
popupClassName={disabled ? 'mt-[-8px]' : '!hidden'}
|
||||
>
|
||||
<div className="flex flex-row items-center">
|
||||
<op.opIcon className="h-4 w-4 mr-1.5 stroke-[1.8px]" />
|
||||
<span className="text-[13px]">{op.opName}</span>
|
||||
<div className="flex items-center justify-center gap-[1px]">
|
||||
<op.opIcon className="h-3.5 w-3.5" />
|
||||
<div className={`${runningStatus ? 'text-text-tertiary' : 'text-components-button-ghost-text-disabled'} system-xs-medium px-[3px]`}>{op.opName}</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Button>
|
||||
|
||||
@@ -34,10 +34,11 @@ export type ButtonProps = {
|
||||
destructive?: boolean
|
||||
loading?: boolean
|
||||
styleCss?: CSSProperties
|
||||
spinnerClassName?: string
|
||||
} & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof buttonVariants>
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, destructive, loading, styleCss, children, ...props }, ref) => {
|
||||
({ className, variant, size, destructive, loading, styleCss, children, spinnerClassName, ...props }, ref) => {
|
||||
return (
|
||||
<button
|
||||
type='button'
|
||||
@@ -50,7 +51,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{loading && <Spinner loading={loading} className='!text-white !h-3 !w-3 !border-2 !ml-1' />}
|
||||
{loading && <Spinner loading={loading} className={classNames('!text-white !h-3 !w-3 !border-2 !ml-1', spinnerClassName)} />}
|
||||
</button>
|
||||
)
|
||||
},
|
||||
|
||||
59
web/app/components/base/content-dialog/index.tsx
Normal file
59
web/app/components/base/content-dialog/index.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Fragment, type ReactNode } from 'react'
|
||||
import { Transition } from '@headlessui/react'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
||||
type ContentDialogProps = {
|
||||
className?: string
|
||||
show: boolean
|
||||
onClose?: () => void
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
const ContentDialog = ({
|
||||
className,
|
||||
show,
|
||||
onClose,
|
||||
children,
|
||||
}: ContentDialogProps) => {
|
||||
return (
|
||||
<Transition
|
||||
show={show}
|
||||
as="div"
|
||||
className="absolute left-0 top-0 w-full h-full z-20 p-2 box-border"
|
||||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div
|
||||
className="absolute left-0 inset-0 w-full bg-app-detail-overlay-bg"
|
||||
onClick={onClose}
|
||||
/>
|
||||
</Transition.Child>
|
||||
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="transform transition ease-out duration-300"
|
||||
enterFrom="-translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transform transition ease-in duration-200"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="-translate-x-full"
|
||||
>
|
||||
<div className={classNames(
|
||||
'absolute left-0 w-full bg-app-detail-bg border-r border-divider-burn',
|
||||
className,
|
||||
)}>
|
||||
{children}
|
||||
</div>
|
||||
</Transition.Child>
|
||||
</Transition>
|
||||
)
|
||||
}
|
||||
|
||||
export default ContentDialog
|
||||
@@ -35,7 +35,7 @@ const CopyFeedback = ({ content, className }: Props) => {
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={`w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg ${
|
||||
className={`w-8 h-8 cursor-pointer hover:bg-state-base-hover rounded-md ${
|
||||
className ?? ''
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"width": "16",
|
||||
"height": "16",
|
||||
"rx": "5",
|
||||
"fill": "#F2F4F7"
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
@@ -33,7 +33,7 @@
|
||||
"width": "233",
|
||||
"height": "10",
|
||||
"rx": "3",
|
||||
"fill": "#EAECF0"
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
@@ -46,7 +46,7 @@
|
||||
"width": "345",
|
||||
"height": "6",
|
||||
"rx": "3",
|
||||
"fill": "#F2F4F7"
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
.setting-icon {
|
||||
background: url(./assets/setting.svg) center center no-repeat;
|
||||
background-size: 14px 14px;
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import s from './base.module.css'
|
||||
import { RiEqualizer2Line } from '@remixicon/react'
|
||||
import WorkspaceSelector from './workspace-selector'
|
||||
import SearchInput from './search-input'
|
||||
import PageSelector from './page-selector'
|
||||
import cn from '@/utils/classnames'
|
||||
import { preImportNotionPages } from '@/service/datasets'
|
||||
import { NotionConnector } from '@/app/components/datasets/create/step-one'
|
||||
import type { DataSourceNotionPageMap, DataSourceNotionWorkspace, NotionPage } from '@/models/common'
|
||||
@@ -88,23 +87,24 @@ const NotionPageSelector = ({
|
||||
}, [firstWorkspaceId])
|
||||
|
||||
return (
|
||||
<div className='bg-gray-25 border border-gray-200 rounded-xl'>
|
||||
<div className='bg-background-default-subtle border border-components-panel-border rounded-xl'>
|
||||
{
|
||||
data?.notion_info?.length
|
||||
? (
|
||||
<>
|
||||
<div className='flex items-center pl-[10px] pr-2 h-11 bg-white border-b border-b-gray-200 rounded-t-xl'>
|
||||
<WorkspaceSelector
|
||||
value={currentWorkspaceId || firstWorkspaceId}
|
||||
items={notionWorkspaces}
|
||||
onSelect={handleSelectWorkspace}
|
||||
/>
|
||||
<div className='mx-1 w-[1px] h-3 bg-gray-200' />
|
||||
<div
|
||||
className={cn(s['setting-icon'], 'w-6 h-6 cursor-pointer')}
|
||||
onClick={() => setShowAccountSettingModal({ payload: 'data-source', onCancelCallback: mutate })}
|
||||
/>
|
||||
<div className='grow' />
|
||||
<div className='flex items-center gap-x-2 p-2 h-12 bg-components-panel-bg border-b border-b-divider-regular rounded-t-xl'>
|
||||
<div className='grow flex items-center gap-x-1'>
|
||||
<WorkspaceSelector
|
||||
value={currentWorkspaceId || firstWorkspaceId}
|
||||
items={notionWorkspaces}
|
||||
onSelect={handleSelectWorkspace}
|
||||
/>
|
||||
<div className='mx-1 w-[1px] h-3 bg-divider-regular' />
|
||||
<RiEqualizer2Line
|
||||
className='w-4 h-4 cursor-pointer text-text-tertiary'
|
||||
onClick={() => setShowAccountSettingModal({ payload: 'data-source', onCancelCallback: mutate })}
|
||||
/>
|
||||
</div>
|
||||
<SearchInput
|
||||
value={searchValue}
|
||||
onChange={handleSearchValueChange}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
.arrow {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: url(../assets/down-arrow.svg) center center no-repeat;
|
||||
background-size: 16px 16px;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.arrow-expand {
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
.preview-item {
|
||||
background-color: #eff4ff;
|
||||
border: 1px solid #D1E0FF;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
}
|
||||
@@ -2,9 +2,9 @@ import { memo, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FixedSizeList as List, areEqual } from 'react-window'
|
||||
import type { ListChildComponentProps } from 'react-window'
|
||||
import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react'
|
||||
import Checkbox from '../../checkbox'
|
||||
import NotionIcon from '../../notion-icon'
|
||||
import s from './index.module.css'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common'
|
||||
|
||||
@@ -94,10 +94,16 @@ const ItemComponent = ({ index, style, data }: ListChildComponentProps<{
|
||||
if (hasChild) {
|
||||
return (
|
||||
<div
|
||||
className={cn(s.arrow, current.expand && s['arrow-expand'], 'shrink-0 mr-1 w-5 h-5 hover:bg-gray-200 rounded-md')}
|
||||
className='flex items-center justify-center shrink-0 mr-1 w-5 h-5 hover:bg-components-button-ghost-bg-hover rounded-md'
|
||||
style={{ marginLeft: current.depth * 8 }}
|
||||
onClick={() => handleToggle(index)}
|
||||
/>
|
||||
>
|
||||
{
|
||||
current.expand
|
||||
? <RiArrowDownSLine className='w-4 h-4 text-text-tertiary' />
|
||||
: <RiArrowRightSLine className='w-4 h-4 text-text-tertiary' />
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (current.parent_id === 'root' || !pagesMap[current.parent_id]) {
|
||||
@@ -112,14 +118,12 @@ const ItemComponent = ({ index, style, data }: ListChildComponentProps<{
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('group flex items-center pl-2 pr-[2px] rounded-md border border-transparent hover:bg-gray-100 cursor-pointer', previewPageId === current.page_id && s['preview-item'])}
|
||||
className={cn('group flex items-center pl-2 pr-[2px] rounded-md hover:bg-state-base-hover cursor-pointer',
|
||||
previewPageId === current.page_id && 'bg-state-base-hover')}
|
||||
style={{ ...style, top: style.top as number + 8, left: 8, right: 8, width: 'calc(100% - 16px)' }}
|
||||
>
|
||||
<Checkbox
|
||||
className={cn(
|
||||
'shrink-0 mr-2 group-hover:border-primary-600 group-hover:border-[2px]',
|
||||
disabled && 'group-hover:border-transparent',
|
||||
)}
|
||||
className='shrink-0 mr-2'
|
||||
checked={checkedIds.has(current.page_id)}
|
||||
disabled={disabled}
|
||||
onCheck={() => {
|
||||
@@ -135,7 +139,7 @@ const ItemComponent = ({ index, style, data }: ListChildComponentProps<{
|
||||
src={current.page_icon}
|
||||
/>
|
||||
<div
|
||||
className='grow text-sm font-medium text-gray-700 truncate'
|
||||
className='grow text-[13px] leading-4 font-medium text-text-secondary truncate'
|
||||
title={current.page_name}
|
||||
>
|
||||
{current.page_name}
|
||||
@@ -143,7 +147,9 @@ const ItemComponent = ({ index, style, data }: ListChildComponentProps<{
|
||||
{
|
||||
canPreview && (
|
||||
<div
|
||||
className='shrink-0 hidden group-hover:flex items-center ml-1 px-2 h-6 rounded-md text-xs font-medium text-gray-500 cursor-pointer hover:bg-gray-50 hover:text-gray-700'
|
||||
className='shrink-0 hidden group-hover:flex items-center ml-1 px-2 h-6 rounded-md text-xs leading-4 font-medium text-components-button-secondary-text
|
||||
cursor-pointer bg-components-button-secondary-bg border-[0.5px] border-components-button-secondary-border shadow-xs shadow-shadow-shadow-3
|
||||
backdrop-blur-[10px] hover:bg-components-button-secondary-bg-hover hover:border-components-button-secondary-border-hover'
|
||||
onClick={() => handlePreview(index)}>
|
||||
{t('common.dataSource.notion.selector.preview')}
|
||||
</div>
|
||||
@@ -152,7 +158,7 @@ const ItemComponent = ({ index, style, data }: ListChildComponentProps<{
|
||||
{
|
||||
searchValue && (
|
||||
<div
|
||||
className='shrink-0 ml-1 max-w-[120px] text-xs text-gray-400 truncate'
|
||||
className='shrink-0 ml-1 max-w-[120px] text-xs text-text-quaternary truncate'
|
||||
title={breadCrumbs.join(' / ')}
|
||||
>
|
||||
{breadCrumbs.join(' / ')}
|
||||
@@ -278,7 +284,7 @@ const PageSelector = ({
|
||||
|
||||
if (!currentDataList.length) {
|
||||
return (
|
||||
<div className='flex items-center justify-center h-[296px] text-[13px] text-gray-500'>
|
||||
<div className='flex items-center justify-center h-[296px] text-[13px] text-text-tertiary'>
|
||||
{t('common.dataSource.notion.selector.noSearchResult')}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
.search-icon {
|
||||
background: url(../assets/search.svg) center center;
|
||||
background-size: 14px 14px;
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
background: url(../assets/clear.svg) center center;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
flex-basis: 200px;
|
||||
width: 0;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback } from 'react'
|
||||
import type { ChangeEvent } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import s from './index.module.css'
|
||||
import { RiCloseCircleFill, RiSearchLine } from '@remixicon/react'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type SearchInputProps = {
|
||||
@@ -19,18 +19,18 @@ const SearchInput = ({
|
||||
}, [onChange])
|
||||
|
||||
return (
|
||||
<div className={cn(s['input-wrapper'], 'flex items-center px-2 h-7 rounded-md', `${value ? 'bg-white' : 'bg-gray-100'}`)}>
|
||||
<div className={cn(s['search-icon'], 'mr-[6px] w-4 h-4')} />
|
||||
<div className={cn('w-[200px] flex items-center p-2 h-8 rounded-lg bg-components-input-bg-normal')}>
|
||||
<RiSearchLine className={'w-4 h-4 mr-0.5 shrink-0 text-components-input-text-placeholder'} />
|
||||
<input
|
||||
className='grow text-[13px] bg-inherit border-0 outline-0 appearance-none'
|
||||
className='min-w-0 grow px-1 text-[13px] leading-[16px] bg-transparent text-components-input-text-filled placeholder:text-components-input-text-placeholder border-0 outline-0 appearance-none'
|
||||
value={value}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => onChange(e.target.value)}
|
||||
placeholder={t('common.dataSource.notion.selector.searchPages') || ''}
|
||||
/>
|
||||
{
|
||||
value && (
|
||||
<div
|
||||
className={cn(s['clear-icon'], 'ml-1 w-4 h-4 cursor-pointer')}
|
||||
<RiCloseCircleFill
|
||||
className={'w-4 h-4 shrink-0 cursor-pointer text-components-input-text-placeholder'}
|
||||
onClick={handleClear}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
.down-arrow {
|
||||
background: url(../assets/down-arrow.svg) center center no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.popup {
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
z-index: 10;
|
||||
}
|
||||
@@ -2,9 +2,8 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Fragment } from 'react'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { RiArrowDownSLine } from '@remixicon/react'
|
||||
import NotionIcon from '../../notion-icon'
|
||||
import s from './index.module.css'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { DataSourceNotionWorkspace } from '@/models/common'
|
||||
|
||||
type WorkspaceSelectorProps = {
|
||||
@@ -25,15 +24,15 @@ export default function WorkspaceSelector({
|
||||
{
|
||||
({ open }) => (
|
||||
<>
|
||||
<Menu.Button className={`flex items-center justify-center h-7 rounded-md hover:bg-gray-50 ${open && 'bg-gray-50'} cursor-pointer`}>
|
||||
<Menu.Button className={`flex items-center justify-center h-7 p-1 pr-2 rounded-md hover:bg-state-base-hover ${open && 'bg-state-base-hover'} cursor-pointer`}>
|
||||
<NotionIcon
|
||||
className='ml-1 mr-2'
|
||||
className='mr-2'
|
||||
src={currentWorkspace?.workspace_icon}
|
||||
name={currentWorkspace?.workspace_name}
|
||||
/>
|
||||
<div className='mr-1 w-[90px] text-left text-sm font-medium text-gray-700 truncate' title={currentWorkspace?.workspace_name}>{currentWorkspace?.workspace_name}</div>
|
||||
<div className='mr-1 px-1 h-[18px] bg-primary-50 rounded-lg text-xs font-medium text-primary-600'>{currentWorkspace?.pages.length}</div>
|
||||
<div className={cn(s['down-arrow'], 'mr-2 w-3 h-3')} />
|
||||
<div className='mr-1 w-[90px] text-left text-sm font-medium text-text-secondary truncate' title={currentWorkspace?.workspace_name}>{currentWorkspace?.workspace_name}</div>
|
||||
{/* <div className='mr-1 px-1 h-[18px] bg-primary-50 rounded-lg text-xs font-medium text-text-accent'>{currentWorkspace?.pages.length}</div> */}
|
||||
<RiArrowDownSLine className='w-4 h-4 text-text-secondary' />
|
||||
</Menu.Button>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
@@ -45,19 +44,16 @@ export default function WorkspaceSelector({
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
className={cn(
|
||||
s.popup,
|
||||
`absolute left-0 top-8 w-80
|
||||
origin-top-right rounded-lg bg-white
|
||||
border-[0.5px] border-gray-200`,
|
||||
)}
|
||||
className='absolute left-0 top-8 z-10 w-80
|
||||
origin-top-right rounded-lg bg-components-panel-bg-blur
|
||||
border-[0.5px] border-components-panel-border shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]'
|
||||
>
|
||||
<div className="p-1 max-h-50 overflow-auto">
|
||||
{
|
||||
items.map(item => (
|
||||
<Menu.Item key={item.workspace_id}>
|
||||
<div
|
||||
className='flex items-center px-3 h-9 hover:bg-gray-50 cursor-pointer'
|
||||
className='flex items-center px-3 h-9 rounded-lg hover:bg-state-base-hover cursor-pointer'
|
||||
onClick={() => onSelect(item.workspace_id)}
|
||||
>
|
||||
<NotionIcon
|
||||
@@ -65,8 +61,8 @@ export default function WorkspaceSelector({
|
||||
src={item.workspace_icon}
|
||||
name={item.workspace_name}
|
||||
/>
|
||||
<div className='grow mr-2 text-sm text-gray-700 truncate' title={item.workspace_name}>{item.workspace_name}</div>
|
||||
<div className='shrink-0 text-xs font-medium text-primary-600'>
|
||||
<div className='grow mr-2 system-sm-medium text-text-secondary truncate' title={item.workspace_name}>{item.workspace_name}</div>
|
||||
<div className='shrink-0 system-xs-medium text-text-accent'>
|
||||
{item.pages.length} {t('common.dataSource.notion.selector.pageSelected')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import QRCode from 'qrcode.react'
|
||||
import { QRCodeSVG } from 'qrcode.react'
|
||||
import QrcodeStyle from './style.module.css'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
|
||||
@@ -54,20 +54,20 @@ const ShareQRCode = ({ content, selectorId, className }: Props) => {
|
||||
popupContent={t(`${prefixEmbedded}`) || ''}
|
||||
>
|
||||
<div
|
||||
className={`w-8 h-8 cursor-pointer rounded-lg ${className ?? ''}`}
|
||||
className={`w-8 h-8 cursor-pointer rounded-lg relative ${className ?? ''}`}
|
||||
onClick={toggleQRCode}
|
||||
>
|
||||
<div className={`w-full h-full ${QrcodeStyle.QrcodeIcon} ${isShow ? QrcodeStyle.show : ''}`} />
|
||||
{isShow && (
|
||||
<div
|
||||
ref={qrCodeRef}
|
||||
className={QrcodeStyle.qrcodeform}
|
||||
className={`${QrcodeStyle.qrcodeform} !absolute right-0 top-0`}
|
||||
onClick={handlePanelClick}
|
||||
>
|
||||
<QRCode size={160} value={content} className={QrcodeStyle.qrcodeimage}/>
|
||||
<QRCodeSVG size={160} value={content} className={QrcodeStyle.qrcodeimage}/>
|
||||
<div className={QrcodeStyle.text}>
|
||||
<div className={`text-gray-500 ${QrcodeStyle.scan}`}>{t('appOverview.overview.appInfo.qrcode.scan')}</div>
|
||||
<div className={`text-gray-500 ${QrcodeStyle.scan}`}>·</div>
|
||||
<div className={`text-text-tertiary ${QrcodeStyle.scan}`}>{t('appOverview.overview.appInfo.qrcode.scan')}</div>
|
||||
<div className={`text-text-tertiary ${QrcodeStyle.scan}`}>·</div>
|
||||
<div className={QrcodeStyle.download} onClick={downloadQR}>{t('appOverview.overview.appInfo.qrcode.download')}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { FC } from 'react'
|
||||
import { useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiSearchLine } from '@remixicon/react'
|
||||
import { RiCloseCircleFill, RiSearchLine } from '@remixicon/react'
|
||||
import cn from '@/utils/classnames'
|
||||
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
|
||||
type SearchInputProps = {
|
||||
placeholder?: string
|
||||
@@ -27,22 +26,19 @@ const SearchInput: FC<SearchInputProps> = ({
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
'group flex items-center px-2 h-8 rounded-lg bg-gray-200 hover:bg-gray-300 border border-transparent overflow-hidden',
|
||||
focus && '!bg-white hover:bg-white shadow-xs !border-gray-300',
|
||||
!focus && value && 'hover:!bg-gray-200 hover:!shadow-xs hover:!border-black/5',
|
||||
'group flex items-center px-2 h-8 rounded-lg bg-components-input-bg-normal hover:bg-components-input-bg-hover border-none overflow-hidden',
|
||||
focus && '!bg-components-input-bg-active',
|
||||
white && '!bg-white hover:!bg-white shadow-xs !border-gray-300 hover:!border-gray-300',
|
||||
className,
|
||||
)}>
|
||||
<div className="pointer-events-none shrink-0 flex items-center mr-1.5 justify-center w-4 h-4">
|
||||
<RiSearchLine className="h-3.5 w-3.5 text-gray-500" aria-hidden="true" />
|
||||
<RiSearchLine className="h-4 w-4 text-components-input-text-placeholder" aria-hidden="true" />
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
name="query"
|
||||
className={cn(
|
||||
'grow block h-[18px] bg-gray-200 border-0 text-gray-700 text-[13px] placeholder:text-gray-500 appearance-none outline-none group-hover:bg-gray-300 caret-blue-600',
|
||||
focus && '!bg-white hover:bg-white group-hover:bg-white placeholder:!text-gray-400',
|
||||
!focus && value && 'hover:!bg-gray-200 group-hover:!bg-gray-200',
|
||||
'grow block h-[18px] bg-transparent border-0 text-components-input-text-filled system-sm-regular placeholder:text-components-input-text-placeholder appearance-none outline-none caret-#295EFF',
|
||||
white && '!bg-white hover:!bg-white group-hover:!bg-white placeholder:!text-gray-400',
|
||||
)}
|
||||
placeholder={placeholder || t('common.operation.search')!}
|
||||
@@ -71,7 +67,7 @@ const SearchInput: FC<SearchInputProps> = ({
|
||||
setInternalValue('')
|
||||
}}
|
||||
>
|
||||
<XCircle className='w-3.5 h-3.5 text-gray-400 group-hover/clear:text-gray-600' />
|
||||
<RiCloseCircleFill className='w-4 h-4 text-text-quaternary group-hover/clear:text-text-tertiary' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -231,10 +231,10 @@ const TagSelector: FC<TagSelectorProps> = ({
|
||||
const Trigger = () => {
|
||||
return (
|
||||
<div className={cn(
|
||||
'group/tip relative w-full flex items-center gap-1 px-2 py-[7px] rounded-md cursor-pointer hover:bg-gray-100',
|
||||
'group/tip relative w-full flex items-center gap-1 px-2 py-[7px] rounded-md cursor-pointer',
|
||||
)}>
|
||||
<Tag01 className='shrink-0 w-3 h-3' />
|
||||
<div className='grow text-xs text-start leading-[18px] font-normal truncate'>
|
||||
<Tag01 className='shrink-0 w-3 h-3 text-components-input-text-placeholder' />
|
||||
<div className='grow text-start system-sm-regular text-components-input-text-placeholder truncate'>
|
||||
{!triggerContent ? t('common.tag.addTag') : triggerContent}
|
||||
</div>
|
||||
</div>
|
||||
@@ -260,8 +260,8 @@ const TagSelector: FC<TagSelectorProps> = ({
|
||||
btnElement={<Trigger />}
|
||||
btnClassName={open =>
|
||||
cn(
|
||||
open ? '!bg-gray-100 !text-gray-700' : '!bg-transparent',
|
||||
'!w-full !p-0 !border-0 !text-gray-500 hover:!bg-gray-100 hover:!text-gray-700',
|
||||
open ? '!bg-components-input-bg-normal' : '!bg-transparent',
|
||||
'!w-full !p-0 !border-0 hover:!bg-state-base-hover',
|
||||
)
|
||||
}
|
||||
popupClassName='!w-full !ring-0'
|
||||
|
||||
@@ -78,7 +78,7 @@ const DocumentPicker: FC<Props> = ({
|
||||
<FileIcon name={name} extension={extension} size='lg' />
|
||||
<div className='flex flex-col items-start ml-1 mr-0.5'>
|
||||
<div className='flex items-center space-x-0.5'>
|
||||
<span className={cn('system-md-semibold')}> {name || '--'}</span>
|
||||
<span className={cn('system-md-semibold text-text-primary')}> {name || '--'}</span>
|
||||
<ArrowIcon className={'h-4 w-4 text-text-primary'} />
|
||||
</div>
|
||||
<div className='flex items-center h-3 text-text-tertiary space-x-0.5'>
|
||||
|
||||
@@ -253,9 +253,9 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
|
||||
return (
|
||||
<>
|
||||
<div className="h-5 flex items-center mb-3">
|
||||
<div className="flex items-center justify-between text-gray-900 font-medium text-sm mr-2">
|
||||
<div className="flex items-center justify-between text-text-secondary font-medium text-sm mr-2">
|
||||
{isEmbedding && <div className='flex items-center'>
|
||||
<RiLoader2Fill className='size-4 mr-1 animate-spin' />
|
||||
<RiLoader2Fill className='size-4 mr-1 animate-spin text-text-secondary' />
|
||||
{t('datasetDocuments.embedding.processing')}
|
||||
</div>}
|
||||
{isEmbeddingCompleted && t('datasetDocuments.embedding.completed')}
|
||||
@@ -304,7 +304,7 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
|
||||
/>
|
||||
)}
|
||||
<div className="grow flex items-center gap-1 w-0" title={getSourceName(indexingStatusDetail.id)}>
|
||||
<div className="text-xs truncate">
|
||||
<div className="system-xs-medium text-text-secondary truncate">
|
||||
{getSourceName(indexingStatusDetail.id)}
|
||||
</div>
|
||||
{
|
||||
@@ -314,7 +314,7 @@ const EmbeddingProcess: FC<Props> = ({ datasetId, batchId, documents = [], index
|
||||
}
|
||||
</div>
|
||||
{isSourceEmbedding(indexingStatusDetail) && (
|
||||
<div className="shrink-0 text-xs">{`${getSourcePercent(indexingStatusDetail)}%`}</div>
|
||||
<div className="shrink-0 text-xs text-text-secondary">{`${getSourcePercent(indexingStatusDetail)}%`}</div>
|
||||
)}
|
||||
{indexingStatusDetail.indexing_status === 'error' && (
|
||||
<Tooltip
|
||||
|
||||
@@ -6,11 +6,10 @@
|
||||
@apply flex items-center place-content-between h-8;
|
||||
}
|
||||
.modalHeader .title {
|
||||
@apply grow;
|
||||
@apply grow text-text-primary;
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
line-height: 32px;
|
||||
color: #101828;
|
||||
}
|
||||
.modalHeader .close {
|
||||
@apply shrink-0 h-4 w-4 bg-center bg-no-repeat cursor-pointer;
|
||||
@@ -19,20 +18,18 @@
|
||||
}
|
||||
|
||||
.modal .tip {
|
||||
@apply mt-1 mb-8;
|
||||
@apply mt-1 mb-8 text-text-tertiary;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
color: #667085;
|
||||
}
|
||||
|
||||
.form {
|
||||
@apply mb-8;
|
||||
}
|
||||
.form .label {
|
||||
@apply mb-2;
|
||||
@apply mb-2 text-text-primary;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #101828;
|
||||
}
|
||||
|
||||
@@ -1,43 +1,29 @@
|
||||
.filePreview {
|
||||
@apply flex flex-col border-l border-gray-200 shrink-0;
|
||||
@apply flex flex-col border-l border-components-panel-border shrink-0 bg-background-default-lighter;
|
||||
width: 100%;
|
||||
background-color: #fcfcfd;
|
||||
}
|
||||
|
||||
.previewHeader {
|
||||
@apply border-b border-gray-200 shrink-0;
|
||||
@apply border-b border-divider-subtle shrink-0;
|
||||
margin: 42px 32px 0;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.previewHeader .title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #101828;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
line-height: 28px;
|
||||
@apply flex justify-between items-center text-text-primary;
|
||||
}
|
||||
|
||||
.previewHeader .fileName {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: #1D2939;
|
||||
@apply text-text-tertiary;
|
||||
}
|
||||
|
||||
.previewHeader .filetype {
|
||||
color: #667085;
|
||||
@apply text-text-tertiary;
|
||||
}
|
||||
|
||||
.previewContent {
|
||||
@apply overflow-y-auto grow;
|
||||
@apply overflow-y-auto grow text-text-secondary;
|
||||
padding: 20px 32px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #344054;
|
||||
}
|
||||
|
||||
.previewContent .loading {
|
||||
@@ -46,6 +32,7 @@
|
||||
background: #f9fafb center no-repeat url(../assets/Loading.svg);
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.fileContent {
|
||||
white-space: pre-line;
|
||||
word-break: break-all;
|
||||
|
||||
@@ -46,13 +46,13 @@ const FilePreview = ({
|
||||
return (
|
||||
<div className={cn(s.filePreview, 'h-full')}>
|
||||
<div className={cn(s.previewHeader)}>
|
||||
<div className={cn(s.title)}>
|
||||
<div className={cn(s.title, 'title-md-semi-bold')}>
|
||||
<span>{t('datasetCreation.stepOne.filePreview')}</span>
|
||||
<div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}>
|
||||
<XMarkIcon className='h-4 w-4'></XMarkIcon>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn(s.fileName)}>
|
||||
<div className={cn(s.fileName, 'system-xs-medium')}>
|
||||
<span>{getFileName(file)}</span><span className={cn(s.filetype)}>.{file?.extension}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -256,7 +256,7 @@ const FileUploader = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className={cn('text-text-tertiary text-sm font-semibold leading-6 mb-1', titleClassName)}>{t('datasetCreation.stepOne.uploader.title')}</div>
|
||||
<div className={cn('text-text-secondary text-sm font-semibold leading-6 mb-1', titleClassName)}>{t('datasetCreation.stepOne.uploader.title')}</div>
|
||||
|
||||
{!hideUpload && (
|
||||
<div ref={dropRef} className={cn('relative box-border flex flex-col justify-center items-center gap-1 mb-2 px-4 py-3 max-w-[640px] min-h-20 leading-4 text-xs text-text-tertiary bg-components-dropzone-bg border border-dashed border-components-dropzone-border rounded-xl', dragging && 'bg-components-dropzone-bg-accent border-components-dropzone-border-accent')}>
|
||||
|
||||
@@ -1,54 +1,34 @@
|
||||
.filePreview {
|
||||
@apply flex flex-col border-l border-gray-200 shrink-0;
|
||||
width: 528px;
|
||||
background-color: #fcfcfd;
|
||||
}
|
||||
|
||||
.previewHeader {
|
||||
@apply border-b border-gray-200 shrink-0;
|
||||
margin: 42px 32px 0;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.previewHeader .title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #101828;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.previewHeader .fileName {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
color: #1D2939;
|
||||
}
|
||||
|
||||
.previewHeader .filetype {
|
||||
color: #667085;
|
||||
}
|
||||
|
||||
.previewContent {
|
||||
@apply overflow-y-auto grow;
|
||||
padding: 20px 32px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #344054;
|
||||
}
|
||||
|
||||
.previewContent .loading {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
background: #f9fafb center no-repeat url(../assets/Loading.svg);
|
||||
background-size: contain;
|
||||
}
|
||||
.fileContent {
|
||||
white-space: pre-line;
|
||||
}
|
||||
@apply flex flex-col border-l border-components-panel-border shrink-0 bg-background-default-lighter;
|
||||
width: 528px;
|
||||
}
|
||||
|
||||
.previewHeader {
|
||||
@apply border-b border-divider-subtle shrink-0;
|
||||
margin: 42px 32px 0;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.previewHeader .title {
|
||||
@apply flex justify-between items-center text-text-primary;
|
||||
}
|
||||
|
||||
.previewHeader .fileName {
|
||||
@apply flex items-center text-text-tertiary;
|
||||
}
|
||||
|
||||
.previewContent {
|
||||
@apply overflow-y-auto grow text-text-secondary;
|
||||
padding: 20px 32px;
|
||||
}
|
||||
|
||||
.previewContent .loading {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
background: #f9fafb center no-repeat url(../assets/Loading.svg);
|
||||
background-size: contain;
|
||||
}
|
||||
.fileContent {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
@@ -46,13 +46,13 @@ const NotionPagePreview = ({
|
||||
return (
|
||||
<div className={cn(s.filePreview, 'h-full')}>
|
||||
<div className={cn(s.previewHeader)}>
|
||||
<div className={cn(s.title)}>
|
||||
<div className={cn(s.title, 'title-md-semi-bold')}>
|
||||
<span>{t('datasetCreation.stepOne.pagePreview')}</span>
|
||||
<div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}>
|
||||
<XMarkIcon className='h-4 w-4'></XMarkIcon>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn(s.fileName)}>
|
||||
<div className={cn(s.fileName, 'system-xs-medium')}>
|
||||
<NotionIcon
|
||||
className='shrink-0 mr-1'
|
||||
type='page'
|
||||
@@ -61,7 +61,7 @@ const NotionPagePreview = ({
|
||||
{currentPage?.page_name}
|
||||
</div>
|
||||
</div>
|
||||
<div className={cn(s.previewContent)}>
|
||||
<div className={cn(s.previewContent, 'body-md-regular')}>
|
||||
{loading && <div className={cn(s.loading)} />}
|
||||
{!loading && (
|
||||
<div className={cn(s.fileContent, 'body-md-regular')}>{previewContent}</div>
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
.stepHeader {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 42px 64px 12px 0;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.form {
|
||||
@@ -14,38 +8,22 @@
|
||||
}
|
||||
|
||||
.dataSourceItem {
|
||||
@apply box-border relative grow shrink-0 flex items-center p-3 h-14 bg-white rounded-xl cursor-pointer;
|
||||
border: 0.5px solid #EAECF0;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #101828;
|
||||
@apply relative grow shrink-0 flex items-center p-3 h-14 bg-components-option-card-option-bg rounded-xl
|
||||
cursor-pointer border border-components-option-card-option-border text-text-secondary;
|
||||
}
|
||||
|
||||
.dataSourceItem:hover {
|
||||
background-color: #f5f8ff;
|
||||
border: 0.5px solid #B2CCFF;
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
@apply bg-components-option-card-option-bg-hover border border-components-option-card-option-border-hover shadow-xs shadow-shadow-shadow-3;
|
||||
}
|
||||
|
||||
.dataSourceItem.active {
|
||||
background-color: #f5f8ff;
|
||||
border: 1.5px solid #528BFF;
|
||||
box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06);
|
||||
}
|
||||
|
||||
.dataSourceItem.disabled {
|
||||
background-color: #f9fafb;
|
||||
border: 0.5px solid #EAECF0;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
cursor: default;
|
||||
@apply bg-components-option-card-option-selected-bg border border-components-option-card-option-selected-border
|
||||
ring-[0.5px] ring-components-option-card-option-selected-border;
|
||||
}
|
||||
|
||||
.dataSourceItem.disabled,
|
||||
.dataSourceItem.disabled:hover {
|
||||
background-color: #f9fafb;
|
||||
border: 0.5px solid #EAECF0;
|
||||
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
|
||||
@apply bg-components-option-card-option-bg border-[0.5px] border-components-option-card-option-border cursor-not-allowed text-text-disabled shadow-none;
|
||||
}
|
||||
|
||||
.comingTag {
|
||||
@@ -64,17 +42,9 @@
|
||||
}
|
||||
|
||||
.datasetIcon {
|
||||
@apply flex mr-2 w-8 h-8 rounded-lg bg-center bg-no-repeat;
|
||||
background-color: #F5FAFF;
|
||||
@apply flex mr-2 w-8 h-8 rounded-lg bg-center bg-no-repeat bg-text-primary-on-surface border-[0.5px] border-divider-regular backdrop-blur-sm;
|
||||
background-image: url(../assets/file.svg);
|
||||
background-size: 16px;
|
||||
border: 0.5px solid #D1E9FF;
|
||||
}
|
||||
|
||||
.dataSourceItem:active .datasetIcon,
|
||||
.dataSourceItem:hover .datasetIcon {
|
||||
background-color: #F5F8FF;
|
||||
border: 0.5px solid #E0EAFF;
|
||||
}
|
||||
|
||||
.datasetIcon.notion {
|
||||
@@ -97,54 +67,7 @@
|
||||
background-color: #eaecf0;
|
||||
}
|
||||
|
||||
.notionConnectionTip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 24px;
|
||||
width: 640px;
|
||||
background: #F9FAFB;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.notionIcon {
|
||||
display: flex;
|
||||
padding: 12px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #fff center no-repeat url(../assets/notion.svg);
|
||||
background: var(--color-components-card-bg) center no-repeat url(../assets/notion.svg);
|
||||
background-size: 24px;
|
||||
border: 0.5px solid #EAECF5;
|
||||
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.notionConnectionTip .title {
|
||||
position: relative;
|
||||
margin: 24px 0 4px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.notionConnectionTip .title::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: -12px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: center no-repeat url(../assets/Icon-3-dots.svg);
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.notionConnectionTip .tip {
|
||||
margin-bottom: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
color: #6B7280;
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import { useDatasetDetailContext } from '@/context/dataset-detail'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import VectorSpaceFull from '@/app/components/billing/vector-space-full'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { Icon3Dots } from '@/app/components/base/icons/src/vender/line/others'
|
||||
|
||||
type IStepOneProps = {
|
||||
datasetId?: string
|
||||
@@ -48,10 +49,15 @@ export const NotionConnector = ({ onSetting }: NotionConnectorProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className={s.notionConnectionTip}>
|
||||
<span className={s.notionIcon} />
|
||||
<div className={s.title}>{t('datasetCreation.stepOne.notionSyncTitle')}</div>
|
||||
<div className={s.tip}>{t('datasetCreation.stepOne.notionSyncTip')}</div>
|
||||
<div className='flex flex-col items-start p-6 w-[640px] rounded-2xl bg-workflow-process-bg'>
|
||||
<span className={cn(s.notionIcon, 'w-12 h-12 p-3 border-[0.5px] border-components-card-border rounded-[10px] shadow-lg shadow-shadow-shadow-5 mb-2')} />
|
||||
<div className='flex flex-col gap-y-1 pt-1 pb-3 mb-1'>
|
||||
<span className='text-text-secondary system-md-semibold'>
|
||||
{t('datasetCreation.stepOne.notionSyncTitle')}
|
||||
<Icon3Dots className='inline relative -top-2.5 -left-1.5 w-4 h-4 text-text-secondary' />
|
||||
</span>
|
||||
<div className='text-text-tertiary system-sm-regular'>{t('datasetCreation.stepOne.notionSyncTip')}</div>
|
||||
</div>
|
||||
<Button className='h-8' variant='primary' onClick={onSetting}>{t('datasetCreation.stepOne.connect')}</Button>
|
||||
</div>
|
||||
)
|
||||
@@ -132,7 +138,9 @@ const StepOne = ({
|
||||
<div className={classNames(s.form)}>
|
||||
{
|
||||
shouldShowDataSourceTypeList && (
|
||||
<div className={classNames(s.stepHeader, 'z-10 text-text-secondary bg-components-panel-bg-blur')}>{t('datasetCreation.steps.one')}</div>
|
||||
<div className={classNames(s.stepHeader, 'text-text-secondary system-md-semibold')}>
|
||||
{t('datasetCreation.steps.one')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
@@ -141,6 +149,7 @@ const StepOne = ({
|
||||
<div
|
||||
className={cn(
|
||||
s.dataSourceItem,
|
||||
'system-sm-medium',
|
||||
dataSourceType === DataSourceType.FILE && s.active,
|
||||
dataSourceTypeDisable && dataSourceType !== DataSourceType.FILE && s.disabled,
|
||||
)}
|
||||
@@ -158,6 +167,7 @@ const StepOne = ({
|
||||
<div
|
||||
className={cn(
|
||||
s.dataSourceItem,
|
||||
'system-sm-medium',
|
||||
dataSourceType === DataSourceType.NOTION && s.active,
|
||||
dataSourceTypeDisable && dataSourceType !== DataSourceType.NOTION && s.disabled,
|
||||
)}
|
||||
@@ -175,6 +185,7 @@ const StepOne = ({
|
||||
<div
|
||||
className={cn(
|
||||
s.dataSourceItem,
|
||||
'system-sm-medium',
|
||||
dataSourceType === DataSourceType.WEB && s.active,
|
||||
dataSourceTypeDisable && dataSourceType !== DataSourceType.WEB && s.disabled,
|
||||
)}
|
||||
@@ -190,7 +201,7 @@ const StepOne = ({
|
||||
<>
|
||||
<FileUploader
|
||||
fileList={files}
|
||||
titleClassName={!shouldShowDataSourceTypeList ? 'mt-[30px] !mb-[44px] !text-lg !font-semibold !text-gray-900' : undefined}
|
||||
titleClassName={!shouldShowDataSourceTypeList ? 'mt-[30px] !mb-[44px] !text-lg' : undefined}
|
||||
prepareFileList={updateFileList}
|
||||
onFileListUpdate={updateFileList}
|
||||
onFileUpdate={updateFile}
|
||||
|
||||
@@ -34,8 +34,8 @@ const StepThree = ({ datasetId, datasetName, indexingType, creationCache, retrie
|
||||
<div className="flex gap-4">
|
||||
<AppIcon {...creationCache?.dataset} className="size-14 text-2xl self-center" />
|
||||
<div className="grow flex flex-col gap-1">
|
||||
<div className="text-[13px] leading-6 font-semibold">{t('datasetCreation.stepThree.label')}</div>
|
||||
<div className="w-full px-3 py-2 text-[13px] leading-4 bg-components-input-bg-normal rounded-lg truncate">{datasetName || creationCache?.dataset?.name}</div>
|
||||
<div className="text-[13px] leading-6 font-semibold text-text-secondary">{t('datasetCreation.stepThree.label')}</div>
|
||||
<div className="w-full px-3 py-2 text-[13px] leading-4 bg-components-input-bg-normal rounded-lg truncate text-components-input-text-filled">{datasetName || creationCache?.dataset?.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -581,7 +581,7 @@ const StepTwo = ({
|
||||
return (
|
||||
<div className='flex w-full h-full'>
|
||||
<div className={cn('relative h-full w-1/2 py-6 overflow-y-auto', isMobile ? 'px-4' : 'px-12')}>
|
||||
<div className={'system-md-semibold mb-1'}>{t('datasetCreation.stepTwo.segmentation')}</div>
|
||||
<div className={'system-md-semibold text-text-secondary mb-1'}>{t('datasetCreation.stepTwo.segmentation')}</div>
|
||||
{((isInUpload && [ChunkingMode.text, ChunkingMode.qa].includes(currentDataset!.doc_form))
|
||||
|| isUploadInEmptyDataset
|
||||
|| isInInit)
|
||||
@@ -832,10 +832,10 @@ const StepTwo = ({
|
||||
</div>
|
||||
</OptionCard>}
|
||||
<Divider className='my-5' />
|
||||
<div className={'system-md-semibold mb-1'}>{t('datasetCreation.stepTwo.indexMode')}</div>
|
||||
<div className={'system-md-semibold text-text-secondary mb-1'}>{t('datasetCreation.stepTwo.indexMode')}</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
{(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.QUALIFIED)) && (
|
||||
<OptionCard className='flex-1'
|
||||
<OptionCard className='flex-1 self-stretch'
|
||||
title={<div className='flex items-center'>
|
||||
{t('datasetCreation.stepTwo.qualified')}
|
||||
<Badge className={cn('ml-1 h-[18px]', (!hasSetIndexType && indexType === IndexingType.QUALIFIED) ? 'border-text-accent-secondary text-text-accent-secondary' : '')} uppercase>
|
||||
@@ -889,7 +889,7 @@ const StepTwo = ({
|
||||
placement={'top'}
|
||||
>
|
||||
<PortalToFollowElemTrigger asChild>
|
||||
<OptionCard className='flex-1'
|
||||
<OptionCard className='flex-1 self-stretch'
|
||||
title={t('datasetCreation.stepTwo.economical')}
|
||||
description={t('datasetCreation.stepTwo.economicalTip')}
|
||||
icon={<Image src={indexMethodIcon.economical} alt='' />}
|
||||
@@ -916,11 +916,11 @@ const StepTwo = ({
|
||||
</div>
|
||||
{!hasSetIndexType && indexType === IndexingType.QUALIFIED && (
|
||||
<div className='mt-2 h-10 p-2 flex items-center gap-x-0.5 rounded-xl border-[0.5px] border-components-panel-border overflow-hidden bg-components-panel-bg-blur backdrop-blur-[5px] shadow-xs'>
|
||||
<div className='absolute top-0 left-0 right-0 bottom-0 bg-[linear-gradient(92deg,rgba(247,144,9,0.25)_0%,rgba(255,255,255,0.00)_100%)] opacity-40'></div>
|
||||
<div className='absolute top-0 left-0 right-0 bottom-0 bg-dataset-warning-message-bg opacity-40'></div>
|
||||
<div className='p-1'>
|
||||
<AlertTriangle className='size-4 text-text-warning-secondary' />
|
||||
</div>
|
||||
<span className='system-xs-medium'>{t('datasetCreation.stepTwo.highQualityTip')}</span>
|
||||
<span className='system-xs-medium text-text-primary'>{t('datasetCreation.stepTwo.highQualityTip')}</span>
|
||||
</div>
|
||||
)}
|
||||
{hasSetIndexType && indexType === IndexingType.ECONOMICAL && (
|
||||
@@ -932,7 +932,7 @@ const StepTwo = ({
|
||||
{/* Embedding model */}
|
||||
{indexType === IndexingType.QUALIFIED && (
|
||||
<div className='mt-5'>
|
||||
<div className={cn('system-md-semibold mb-1', datasetId && 'flex justify-between items-center')}>{t('datasetSettings.form.embeddingModel')}</div>
|
||||
<div className={cn('system-md-semibold text-text-secondary mb-1', datasetId && 'flex justify-between items-center')}>{t('datasetSettings.form.embeddingModel')}</div>
|
||||
<ModelSelector
|
||||
readonly={isModelAndRetrievalConfigDisabled}
|
||||
triggerClassName={isModelAndRetrievalConfigDisabled ? 'opacity-50' : ''}
|
||||
@@ -943,7 +943,7 @@ const StepTwo = ({
|
||||
}}
|
||||
/>
|
||||
{isModelAndRetrievalConfigDisabled && (
|
||||
<div className='mt-2 system-xs-medium'>
|
||||
<div className='mt-2 system-xs-medium text-text-tertiary'>
|
||||
{t('datasetCreation.stepTwo.indexSettingTip')}
|
||||
<Link className='text-text-accent' href={`/datasets/${datasetId}/settings`}>{t('datasetCreation.stepTwo.datasetSettingLink')}</Link>
|
||||
</div>
|
||||
@@ -956,7 +956,7 @@ const StepTwo = ({
|
||||
{!isModelAndRetrievalConfigDisabled
|
||||
? (
|
||||
<div className={'mb-1'}>
|
||||
<div className='system-md-semibold mb-0.5'>{t('datasetSettings.form.retrievalSetting.title')}</div>
|
||||
<div className='system-md-semibold text-text-secondary mb-0.5'>{t('datasetSettings.form.retrievalSetting.title')}</div>
|
||||
<div className='body-xs-regular text-text-tertiary'>
|
||||
<a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
|
||||
{t('datasetSettings.form.retrievalSetting.longDescription')}
|
||||
@@ -964,7 +964,7 @@ const StepTwo = ({
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<div className={cn('system-md-semibold mb-0.5', 'flex justify-between items-center')}>
|
||||
<div className={cn('system-md-semibold text-text-secondary mb-0.5', 'flex justify-between items-center')}>
|
||||
<div>{t('datasetSettings.form.retrievalSetting.title')}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -34,7 +34,7 @@ export const OptionCardHeader: FC<OptionCardHeaderProps> = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
<TriangleArrow
|
||||
className='absolute left-4 -bottom-1.5 text-components-panel-bg'
|
||||
className={classNames('absolute left-4 -bottom-1.5 text-transparent', isActive && 'text-components-panel-bg')}
|
||||
/>
|
||||
<div className='flex-1 space-y-0.5 py-3 pr-4'>
|
||||
<div className='text-text-secondary system-md-semibold'>{title}</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ const CheckboxWithLabel: FC<Props> = ({
|
||||
return (
|
||||
<label className={cn(className, 'flex items-center h-7 space-x-2')}>
|
||||
<Checkbox checked={isChecked} onCheck={() => onChange(!isChecked)} />
|
||||
<div className={cn(labelClassName, 'text-sm font-normal text-gray-800')}>{label}</div>
|
||||
<div className={cn('text-sm font-normal text-gray-800', labelClassName)}>{label}</div>
|
||||
{tooltip && (
|
||||
<Tooltip
|
||||
popupContent={
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { CrawlResultItem as CrawlResultItemType } from '@/models/datasets'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
||||
type Props = {
|
||||
payload: CrawlResultItemType
|
||||
@@ -27,13 +28,32 @@ const CrawledResultItem: FC<Props> = ({
|
||||
onCheckChange(!isChecked)
|
||||
}, [isChecked, onCheckChange])
|
||||
return (
|
||||
<div className={cn(isPreview ? 'border-[#D1E0FF] bg-primary-50 shadow-xs' : 'group hover:bg-gray-100', 'rounded-md px-2 py-[5px] cursor-pointer border border-transparent')}>
|
||||
<div className='flex items-center h-5'>
|
||||
<Checkbox className='group-hover:border-2 group-hover:border-primary-600 mr-2 shrink-0' checked={isChecked} onCheck={handleCheckChange} />
|
||||
<div className='grow w-0 truncate text-sm font-medium text-gray-700' title={payload.title}>{payload.title}</div>
|
||||
<div onClick={onPreview} className='hidden group-hover:flex items-center h-6 px-2 text-xs rounded-md font-medium text-gray-500 uppercase hover:bg-gray-50'>{t('datasetCreation.stepOne.website.preview')}</div>
|
||||
<div className={cn(isPreview ? 'bg-state-base-active' : 'group hover:bg-state-base-hover', 'rounded-lg p-2 cursor-pointer')}>
|
||||
<div className='relative flex'>
|
||||
<div className='h-5 flex items-center'>
|
||||
<Checkbox className='mr-2 shrink-0' checked={isChecked} onCheck={handleCheckChange} />
|
||||
</div>
|
||||
<div className='flex flex-col grow min-w-0'>
|
||||
<div
|
||||
className='truncate text-sm font-medium text-text-secondary'
|
||||
title={payload.title}
|
||||
>
|
||||
{payload.title}
|
||||
</div>
|
||||
<div
|
||||
className='mt-0.5 truncate text-xs text-text-tertiary'
|
||||
title={payload.source_url}
|
||||
>
|
||||
{payload.source_url}
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
onClick={onPreview}
|
||||
className='hidden group-hover:block group-hover:absolute top-0 right-0 h-6 px-1.5 text-xs font-medium uppercase'
|
||||
>
|
||||
{t('datasetCreation.stepOne.website.preview')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className='mt-0.5 truncate pl-6 leading-[18px] text-xs font-normal text-gray-500' title={payload.source_url}>{payload.source_url}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -57,17 +57,19 @@ const CrawledResult: FC<Props> = ({
|
||||
}, [list, onPreview])
|
||||
|
||||
return (
|
||||
<div className={cn(className, 'border-t border-gray-200')}>
|
||||
<div className='flex items-center justify-between h-[34px] px-4 bg-gray-50 shadow-xs border-b-[0.5px] border-black/8 text-xs font-normal text-gray-700'>
|
||||
<div className={cn(className, 'border-t-[0.5px] border-divider-regular shadow-xs shadow-shadow-shadow-3')}>
|
||||
<div className='flex items-center justify-between h-[34px] px-4'>
|
||||
<CheckboxWithLabel
|
||||
isChecked={isCheckAll}
|
||||
onChange={handleCheckedAll} label={isCheckAll ? t(`${I18N_PREFIX}.resetAll`) : t(`${I18N_PREFIX}.selectAll`)}
|
||||
labelClassName='!font-medium'
|
||||
labelClassName='system-[13px] leading-[16px] font-medium text-text-secondary'
|
||||
/>
|
||||
<div>{t(`${I18N_PREFIX}.scrapTimeInfo`, {
|
||||
total: list.length,
|
||||
time: usedTime.toFixed(1),
|
||||
})}</div>
|
||||
<div className='text-xs text-text-tertiary'>
|
||||
{t(`${I18N_PREFIX}.scrapTimeInfo`, {
|
||||
total: list.length,
|
||||
time: usedTime.toFixed(1),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className='p-2'>
|
||||
{list.map((item, index) => (
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import cn from '@/utils/classnames'
|
||||
import { RowStruct } from '@/app/components/base/icons/src/public/other'
|
||||
|
||||
type Props = {
|
||||
@@ -19,15 +18,16 @@ const Crawling: FC<Props> = ({
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className={cn(className, 'border-t border-gray-200')}>
|
||||
<div className='flex items-center h-[34px] px-4 bg-gray-50 shadow-xs border-b-[0.5px] border-black/8 text-xs font-normal text-gray-700'>
|
||||
<div className={className}>
|
||||
<div className='flex items-center h-[34px] px-4 shadow-xs shadow-shadow-shadow-3
|
||||
border-y-[0.5px] border-divider-regular text-xs text-text-tertiary'>
|
||||
{t('datasetCreation.stepOne.website.totalPageScraped')} {crawledNum}/{totalNum}
|
||||
</div>
|
||||
|
||||
<div className='p-2'>
|
||||
{['', '', '', ''].map((item, index) => (
|
||||
<div className='py-[5px]' key={index}>
|
||||
<RowStruct />
|
||||
<RowStruct className='text-text-quaternary' />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -16,13 +16,13 @@ const ErrorMessage: FC<Props> = ({
|
||||
errorMsg,
|
||||
}) => {
|
||||
return (
|
||||
<div className={cn(className, 'py-2 px-4 border-t border-gray-200 bg-[#FFFAEB]')}>
|
||||
<div className={cn(className, 'py-2 px-4 border-t border-divider-subtle bg-dataset-warning-message-bg opacity-40')}>
|
||||
<div className='flex items-center h-5'>
|
||||
<AlertTriangle className='mr-2 w-4 h-4 text-text-warning-secondary' />
|
||||
<div className='text-sm font-medium text-[#DC6803]'>{title}</div>
|
||||
<div className='system-md-medium text-text-warning'>{title}</div>
|
||||
</div>
|
||||
{errorMsg && (
|
||||
<div className='mt-1 pl-6 leading-[18px] text-xs font-normal text-gray-700'>{errorMsg}</div>
|
||||
<div className='mt-1 pl-6 system-xs-regular text-text-secondary'>{errorMsg}</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -31,8 +31,8 @@ const Field: FC<Props> = ({
|
||||
return (
|
||||
<div className={cn(className)}>
|
||||
<div className='flex py-[7px]'>
|
||||
<div className={cn(labelClassName, 'flex items-center h-[18px] text-[13px] font-medium text-gray-900')}>{label} </div>
|
||||
{isRequired && <span className='ml-0.5 text-xs font-semibold text-[#D92D20]'>*</span>}
|
||||
<div className={cn(labelClassName, 'flex items-center h-[16px] text-[13px] font-semibold text-text-secondary')}>{label} </div>
|
||||
{isRequired && <span className='ml-0.5 text-xs font-semibold text-text-destructive'>*</span>}
|
||||
{tooltip && (
|
||||
<Tooltip
|
||||
popupContent={
|
||||
|
||||
@@ -50,7 +50,12 @@ const Input: FC<Props> = ({
|
||||
{...otherOption}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
className='flex h-9 w-full py-1 px-2 rounded-lg text-xs leading-normal bg-gray-100 caret-primary-600 hover:bg-gray-100 focus:ring-1 focus:ring-inset focus:ring-gray-200 focus-visible:outline-none focus:bg-gray-50 placeholder:text-gray-400'
|
||||
className='flex h-8 w-full p-2 rounded-lg system-xs-regular text-components-input-text-filled bg-components-input-bg-normal
|
||||
caret-[#295eff] border border-transparent
|
||||
hover:bg-components-input-bg-hover hover:border hover:border-components-input-border-hover
|
||||
focus-visible:outline-none focus:bg-components-inout-border-active focus:border focus:border-components-input-border-active
|
||||
focus:shadow-xs focus:shadow-shadow-shadow-3
|
||||
placeholder:text-components-input-text-placeholder'
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -3,8 +3,8 @@ import { useBoolean } from 'ahooks'
|
||||
import type { FC } from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiEqualizer2Line } from '@remixicon/react'
|
||||
import cn from '@/utils/classnames'
|
||||
import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
@@ -34,14 +34,14 @@ const OptionsWrap: FC<Props> = ({
|
||||
return (
|
||||
<div className={cn(className, !fold ? 'mb-0' : 'mb-3')}>
|
||||
<div
|
||||
className='flex justify-between items-center h-[26px] py-1 cursor-pointer select-none'
|
||||
className='flex items-center gap-x-1 h-[26px] py-1 cursor-pointer select-none'
|
||||
onClick={foldToggle}
|
||||
>
|
||||
<div className='flex items-center text-gray-700'>
|
||||
<Settings04 className='mr-1 w-4 h-4' />
|
||||
<div className='text-[13px] font-semibold text-gray-800 uppercase'>{t(`${I18N_PREFIX}.options`)}</div>
|
||||
<div className='flex items-center grow'>
|
||||
<RiEqualizer2Line className='mr-1 w-4 h-4 text-text-secondary' />
|
||||
<span className='text-[13px] leading-[16px] font-semibold text-text-secondary uppercase'>{t(`${I18N_PREFIX}.options`)}</span>
|
||||
</div>
|
||||
<ChevronRight className={cn(!fold && 'rotate-90', 'w-4 h-4 text-gray-500')} />
|
||||
<ChevronRight className={cn(!fold && 'rotate-90', 'w-4 h-4 shrink-0 text-text-tertiary')} />
|
||||
</div>
|
||||
{!fold && (
|
||||
<div className='mb-4'>
|
||||
|
||||
@@ -28,7 +28,7 @@ const UrlInput: FC<Props> = ({
|
||||
}, [isRunning, onRun, url])
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='flex items-center justify-between gap-x-2'>
|
||||
<Input
|
||||
value={url}
|
||||
onChange={handleUrlChange}
|
||||
@@ -37,8 +37,8 @@ const UrlInput: FC<Props> = ({
|
||||
<Button
|
||||
variant='primary'
|
||||
onClick={handleOnRun}
|
||||
className='ml-2'
|
||||
loading={isRunning}
|
||||
spinnerClassName='!ml-0'
|
||||
>
|
||||
{!isRunning ? t(`${I18N_PREFIX}.run`) : ''}
|
||||
</Button>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education'
|
||||
import { RiBookOpenLine, RiEqualizer2Line } from '@remixicon/react'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
@@ -19,22 +19,23 @@ const Header: FC<Props> = ({
|
||||
return (
|
||||
<div className='flex h-6 items-center justify-between'>
|
||||
<div className='flex items-center'>
|
||||
<div className='text-base font-medium text-gray-700'>{t(`${I18N_PREFIX}.firecrawlTitle`)}</div>
|
||||
<div className='ml-2 mr-1 w-px h-3.5 bg-gray-200'></div>
|
||||
<div
|
||||
className='p-1 rounded-md hover:bg-black/5 cursor-pointer'
|
||||
onClick={onSetting}
|
||||
>
|
||||
<Settings01 className='w-3.5 h-3.5 text-gray-500' />
|
||||
</div>
|
||||
<div className='text-base font-medium text-text-secondary'>{t(`${I18N_PREFIX}.firecrawlTitle`)}</div>
|
||||
<div className='ml-2 mr-2 w-px h-3.5 bg-divider-regular' />
|
||||
<Button className='flex items-center gap-x-[1px] h-6 px-1.5' onClick={onSetting}>
|
||||
<RiEqualizer2Line className='w-3.5 h-3.5 text-components-button-secondary-text' />
|
||||
<span className='text-components-button-secondary-text text-xs font-medium px-[3px]'>
|
||||
{t(`${I18N_PREFIX}.configureFirecrawl`)}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
<a
|
||||
href='https://docs.firecrawl.dev/introduction'
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
className='flex items-center text-xs text-primary-600'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='inline-flex items-center gap-x-1 text-xs font-medium text-text-accent'
|
||||
>
|
||||
<BookOpen01 className='mr-1 w-3.5 h-3.5 text-primary-600' />
|
||||
{t(`${I18N_PREFIX}.firecrawlDoc`)}
|
||||
<RiBookOpenLine className='w-3.5 h-3.5 text-text-accent' />
|
||||
<span>{t(`${I18N_PREFIX}.firecrawlDoc`)}</span>
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -9,7 +9,6 @@ import Crawling from '../base/crawling'
|
||||
import ErrorMessage from '../base/error-message'
|
||||
import Header from './header'
|
||||
import Options from './options'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import type { CrawlOptions, CrawlResultItem } from '@/models/datasets'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
@@ -179,10 +178,10 @@ const FireCrawl: FC<Props> = ({
|
||||
return (
|
||||
<div>
|
||||
<Header onSetting={handleSetting} />
|
||||
<div className={cn('mt-2 p-4 pb-0 rounded-xl border border-gray-200')}>
|
||||
<div className='mt-2 p-4 pb-0 rounded-xl border border-components-panel-border bg-background-default-subtle'>
|
||||
<UrlInput onRun={handleRun} isRunning={isRunning} />
|
||||
<OptionsWrap
|
||||
className={cn('mt-4')}
|
||||
className='mt-4'
|
||||
controlFoldOptions={controlFoldOptions}
|
||||
>
|
||||
<Options className='mt-2' payload={crawlOptions} onChange={onCrawlOptionsChange} />
|
||||
|
||||
@@ -36,6 +36,7 @@ const Options: FC<Props> = ({
|
||||
label={t(`${I18N_PREFIX}.crawlSubPage`)}
|
||||
isChecked={payload.crawl_sub_pages}
|
||||
onChange={handleChange('crawl_sub_pages')}
|
||||
labelClassName='text-[13px] leading-[16px] font-medium text-text-secondary'
|
||||
/>
|
||||
<div className='flex justify-between space-x-4'>
|
||||
<Field
|
||||
@@ -76,6 +77,7 @@ const Options: FC<Props> = ({
|
||||
label={t(`${I18N_PREFIX}.extractOnlyMainContent`)}
|
||||
isChecked={payload.only_main_content}
|
||||
onChange={handleChange('only_main_content')}
|
||||
labelClassName='text-[13px] leading-[16px] font-medium text-text-secondary'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -73,27 +73,29 @@ const Website: FC<Props> = ({
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
<div className="font-medium text-gray-700 mb-2 h-6">
|
||||
<div className="system-md-medium text-text-secondary mb-2">
|
||||
{t('datasetCreation.stepOne.website.chooseProvider')}
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
className={`px-4 py-2 text-sm font-medium rounded-md flex items-center justify-center ${
|
||||
className={cn('px-4 py-2 rounded-lg flex items-center justify-center',
|
||||
selectedProvider === DataSourceProvider.jinaReader
|
||||
? 'bg-primary-50 text-primary-600'
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||
}`}
|
||||
? 'system-sm-medium bg-components-option-card-option-selected-bg text-text-primary border-[1.5px] border-components-option-card-option-selected-border'
|
||||
: `system-sm-regular bg-components-option-card-option-bg text-text-secondary border border-components-option-card-option-border
|
||||
hover:bg-components-option-card-option-bg-hover hover:border-components-option-card-option-border-hover hover:shadow-xs hover:shadow-shadow-shadow-3`,
|
||||
)}
|
||||
onClick={() => setSelectedProvider(DataSourceProvider.jinaReader)}
|
||||
>
|
||||
<span className={cn(s.jinaLogo, 'mr-2')} />
|
||||
<span>Jina Reader</span>
|
||||
</button>
|
||||
<button
|
||||
className={`px-4 py-2 text-sm font-medium rounded-md ${
|
||||
className={cn('px-4 py-2 rounded-lg',
|
||||
selectedProvider === DataSourceProvider.fireCrawl
|
||||
? 'bg-primary-50 text-primary-600'
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||
}`}
|
||||
? 'system-sm-medium bg-components-option-card-option-selected-bg text-text-primary border-[1.5px] border-components-option-card-option-selected-border'
|
||||
: `system-sm-regular bg-components-option-card-option-bg text-text-secondary border border-components-option-card-option-border
|
||||
hover:bg-components-option-card-option-bg-hover hover:border-components-option-card-option-border-hover hover:shadow-xs hover:shadow-shadow-shadow-3`,
|
||||
)}
|
||||
onClick={() => setSelectedProvider(DataSourceProvider.fireCrawl)}
|
||||
>
|
||||
🔥 Firecrawl
|
||||
|
||||
@@ -25,7 +25,7 @@ const CheckboxWithLabel: FC<Props> = ({
|
||||
return (
|
||||
<label className={cn(className, 'flex items-center h-7 space-x-2')}>
|
||||
<Checkbox checked={isChecked} onCheck={() => onChange(!isChecked)} />
|
||||
<div className={cn(labelClassName, 'text-sm font-normal text-gray-800')}>{label}</div>
|
||||
<div className={cn('text-sm font-normal text-gray-800', labelClassName)}>{label}</div>
|
||||
{tooltip && (
|
||||
<Tooltip
|
||||
popupContent={
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { CrawlResultItem as CrawlResultItemType } from '@/models/datasets'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
|
||||
type Props = {
|
||||
payload: CrawlResultItemType
|
||||
isChecked: boolean
|
||||
isPreview: boolean
|
||||
onCheckChange: (checked: boolean) => void
|
||||
onPreview: () => void
|
||||
}
|
||||
|
||||
const CrawledResultItem: FC<Props> = ({
|
||||
isPreview,
|
||||
payload,
|
||||
isChecked,
|
||||
onCheckChange,
|
||||
onPreview,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const handleCheckChange = useCallback(() => {
|
||||
onCheckChange(!isChecked)
|
||||
}, [isChecked, onCheckChange])
|
||||
return (
|
||||
<div className={cn(isPreview ? 'border-[#D1E0FF] bg-primary-50 shadow-xs' : 'group hover:bg-gray-100', 'rounded-md px-2 py-[5px] cursor-pointer border border-transparent')}>
|
||||
<div className='flex items-center h-5'>
|
||||
<Checkbox className='group-hover:border-2 group-hover:border-primary-600 mr-2 shrink-0' checked={isChecked} onCheck={handleCheckChange} />
|
||||
<div className='grow w-0 truncate text-sm font-medium text-gray-700' title={payload.title}>{payload.title}</div>
|
||||
<div onClick={onPreview} className='hidden group-hover:flex items-center h-6 px-2 text-xs rounded-md font-medium text-gray-500 uppercase hover:bg-gray-50'>{t('datasetCreation.stepOne.website.preview')}</div>
|
||||
</div>
|
||||
<div className='mt-0.5 truncate pl-6 leading-[18px] text-xs font-normal text-gray-500' title={payload.source_url}>{payload.source_url}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(CrawledResultItem)
|
||||
@@ -1,87 +0,0 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import CheckboxWithLabel from './base/checkbox-with-label'
|
||||
import CrawledResultItem from './crawled-result-item'
|
||||
import cn from '@/utils/classnames'
|
||||
import type { CrawlResultItem } from '@/models/datasets'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
list: CrawlResultItem[]
|
||||
checkedList: CrawlResultItem[]
|
||||
onSelectedChange: (selected: CrawlResultItem[]) => void
|
||||
onPreview: (payload: CrawlResultItem) => void
|
||||
usedTime: number
|
||||
}
|
||||
|
||||
const CrawledResult: FC<Props> = ({
|
||||
className = '',
|
||||
list,
|
||||
checkedList,
|
||||
onSelectedChange,
|
||||
onPreview,
|
||||
usedTime,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const isCheckAll = checkedList.length === list.length
|
||||
|
||||
const handleCheckedAll = useCallback(() => {
|
||||
if (!isCheckAll)
|
||||
onSelectedChange(list)
|
||||
|
||||
else
|
||||
onSelectedChange([])
|
||||
}, [isCheckAll, list, onSelectedChange])
|
||||
|
||||
const handleItemCheckChange = useCallback((item: CrawlResultItem) => {
|
||||
return (checked: boolean) => {
|
||||
if (checked)
|
||||
onSelectedChange([...checkedList, item])
|
||||
|
||||
else
|
||||
onSelectedChange(checkedList.filter(checkedItem => checkedItem.source_url !== item.source_url))
|
||||
}
|
||||
}, [checkedList, onSelectedChange])
|
||||
|
||||
const [previewIndex, setPreviewIndex] = React.useState<number>(-1)
|
||||
const handlePreview = useCallback((index: number) => {
|
||||
return () => {
|
||||
setPreviewIndex(index)
|
||||
onPreview(list[index])
|
||||
}
|
||||
}, [list, onPreview])
|
||||
|
||||
return (
|
||||
<div className={cn(className, 'border-t border-gray-200')}>
|
||||
<div className='flex items-center justify-between h-[34px] px-4 bg-gray-50 shadow-xs border-b-[0.5px] border-black/8 text-xs font-normal text-gray-700'>
|
||||
<CheckboxWithLabel
|
||||
isChecked={isCheckAll}
|
||||
onChange={handleCheckedAll} label={isCheckAll ? t(`${I18N_PREFIX}.resetAll`) : t(`${I18N_PREFIX}.selectAll`)}
|
||||
labelClassName='!font-medium'
|
||||
/>
|
||||
<div>{t(`${I18N_PREFIX}.scrapTimeInfo`, {
|
||||
total: list.length,
|
||||
time: usedTime.toFixed(1),
|
||||
})}</div>
|
||||
</div>
|
||||
<div className='p-2'>
|
||||
{list.map((item, index) => (
|
||||
<CrawledResultItem
|
||||
key={item.source_url}
|
||||
isPreview={index === previewIndex}
|
||||
onPreview={handlePreview(index)}
|
||||
payload={item}
|
||||
isChecked={checkedList.some(checkedItem => checkedItem.source_url === item.source_url)}
|
||||
onCheckChange={handleItemCheckChange(item)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(CrawledResult)
|
||||
@@ -1,37 +0,0 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import cn from '@/utils/classnames'
|
||||
import { RowStruct } from '@/app/components/base/icons/src/public/other'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
crawledNum: number
|
||||
totalNum: number
|
||||
}
|
||||
|
||||
const Crawling: FC<Props> = ({
|
||||
className = '',
|
||||
crawledNum,
|
||||
totalNum,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className={cn(className, 'border-t border-gray-200')}>
|
||||
<div className='flex items-center h-[34px] px-4 bg-gray-50 shadow-xs border-b-[0.5px] border-black/8 text-xs font-normal text-gray-700'>
|
||||
{t('datasetCreation.stepOne.website.totalPageScraped')} {crawledNum}/{totalNum}
|
||||
</div>
|
||||
|
||||
<div className='p-2'>
|
||||
{['', '', '', ''].map((item, index) => (
|
||||
<div className='py-[5px]' key={index}>
|
||||
<RowStruct />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(Crawling)
|
||||
@@ -2,8 +2,8 @@
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education'
|
||||
import { RiBookOpenLine, RiEqualizer2Line } from '@remixicon/react'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
@@ -19,22 +19,23 @@ const Header: FC<Props> = ({
|
||||
return (
|
||||
<div className='flex h-6 items-center justify-between'>
|
||||
<div className='flex items-center'>
|
||||
<div className='text-base font-medium text-gray-700'>{t(`${I18N_PREFIX}.jinaReaderTitle`)}</div>
|
||||
<div className='ml-2 mr-1 w-px h-3.5 bg-gray-200'></div>
|
||||
<div
|
||||
className='p-1 rounded-md hover:bg-black/5 cursor-pointer'
|
||||
onClick={onSetting}
|
||||
>
|
||||
<Settings01 className='w-3.5 h-3.5 text-gray-500' />
|
||||
</div>
|
||||
<div className='text-sm font-semibold text-text-secondary'>{t(`${I18N_PREFIX}.jinaReaderTitle`)}</div>
|
||||
<div className='ml-2 mr-2 w-px h-3.5 bg-divider-regular' />
|
||||
<Button className='flex items-center gap-x-[1px] h-6 px-1.5' onClick={onSetting}>
|
||||
<RiEqualizer2Line className='w-3.5 h-3.5 text-components-button-secondary-text' />
|
||||
<span className='text-components-button-secondary-text text-xs font-medium px-[3px]'>
|
||||
{t(`${I18N_PREFIX}.configureJinaReader`)}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
<a
|
||||
href='https://jina.ai/reader'
|
||||
target='_blank' rel='noopener noreferrer'
|
||||
className='flex items-center text-xs text-primary-600'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='inline-flex items-center gap-x-1 text-xs font-medium text-text-accent'
|
||||
>
|
||||
<BookOpen01 className='mr-1 w-3.5 h-3.5 text-primary-600' />
|
||||
{t(`${I18N_PREFIX}.jinaReaderDoc`)}
|
||||
<RiBookOpenLine className='w-3.5 h-3.5 text-text-accent' />
|
||||
<span>{t(`${I18N_PREFIX}.jinaReaderDoc`)}</span>
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -9,7 +9,6 @@ import Crawling from '../base/crawling'
|
||||
import ErrorMessage from '../base/error-message'
|
||||
import Header from './header'
|
||||
import Options from './options'
|
||||
import cn from '@/utils/classnames'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { checkJinaReaderTaskStatus, createJinaReaderTask } from '@/service/datasets'
|
||||
@@ -192,10 +191,10 @@ const JinaReader: FC<Props> = ({
|
||||
return (
|
||||
<div>
|
||||
<Header onSetting={handleSetting} />
|
||||
<div className={cn('mt-2 p-4 pb-0 rounded-xl border border-gray-200')}>
|
||||
<div className='mt-2 p-4 pb-0 rounded-xl border border-components-panel-border bg-background-default-subtle'>
|
||||
<UrlInput onRun={handleRun} isRunning={isRunning} />
|
||||
<OptionsWrap
|
||||
className={cn('mt-4')}
|
||||
className='mt-4'
|
||||
controlFoldOptions={controlFoldOptions}
|
||||
>
|
||||
<Options className='mt-2' payload={crawlOptions} onChange={onCrawlOptionsChange} />
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { CrawlResultItem } from '@/models/datasets'
|
||||
|
||||
const result: CrawlResultItem[] = [
|
||||
{
|
||||
title: 'Start the frontend Docker container separately',
|
||||
markdown: 'Markdown 1',
|
||||
description: 'Description 1',
|
||||
source_url: 'https://example.com/1',
|
||||
},
|
||||
{
|
||||
title: 'Advanced Tool Integration',
|
||||
markdown: 'Markdown 2',
|
||||
description: 'Description 2',
|
||||
source_url: 'https://example.com/2',
|
||||
},
|
||||
{
|
||||
title: 'Local Source Code Start | English | Dify',
|
||||
markdown: 'Markdown 3',
|
||||
description: 'Description 3',
|
||||
source_url: 'https://example.com/3',
|
||||
},
|
||||
]
|
||||
|
||||
export default result
|
||||
@@ -36,12 +36,14 @@ const Options: FC<Props> = ({
|
||||
label={t(`${I18N_PREFIX}.crawlSubPage`)}
|
||||
isChecked={payload.crawl_sub_pages}
|
||||
onChange={handleChange('crawl_sub_pages')}
|
||||
labelClassName='text-[13px] leading-[16px] font-medium text-text-secondary'
|
||||
/>
|
||||
<CheckboxWithLabel
|
||||
label={t(`${I18N_PREFIX}.useSitemap`)}
|
||||
isChecked={payload.use_sitemap}
|
||||
onChange={handleChange('use_sitemap')}
|
||||
tooltip={t(`${I18N_PREFIX}.useSitemapTooltip`) as string}
|
||||
labelClassName='text-[13px] leading-[16px] font-medium text-text-secondary'
|
||||
/>
|
||||
<div className='flex justify-between space-x-4'>
|
||||
<Field
|
||||
|
||||
@@ -37,13 +37,17 @@ const NoData: FC<Props> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='max-w-[640px] p-6 rounded-2xl bg-gray-50 mt-4'>
|
||||
<div className='flex w-11 h-11 items-center justify-center bg-gray-50 rounded-xl border-[0.5px] border-gray-100 shadow-lg'>
|
||||
<div className='max-w-[640px] p-6 rounded-2xl bg-workflow-process-bg mt-4'>
|
||||
<div className='flex w-12 h-12 items-center justify-center bg-components-card-bg rounded-[10px]
|
||||
border-[0.5px] border-components-card-border shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]'>
|
||||
{currentProvider.emoji}
|
||||
</div>
|
||||
<div className='my-2'>
|
||||
<span className='text-gray-700 font-semibold'>{currentProvider.title}<Icon3Dots className='inline relative -top-3 -left-1.5' /></span>
|
||||
<div className='mt-1 pb-3 text-gray-500 text-[13px] font-normal'>
|
||||
<div className='mt-2 mb-1 pt-1 pb-3 flex flex-col gap-y-1'>
|
||||
<span className='text-text-secondary system-md-semibold'>
|
||||
{currentProvider.title}
|
||||
<Icon3Dots className='inline relative -top-2.5 -left-1.5' />
|
||||
</span>
|
||||
<div className='text-text-tertiary system-sm-regular'>
|
||||
{currentProvider.description}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,19 +20,19 @@ const WebsitePreview = ({
|
||||
return (
|
||||
<div className={cn(s.filePreview, 'h-full')}>
|
||||
<div className={cn(s.previewHeader)}>
|
||||
<div className={cn(s.title)}>
|
||||
<div className={cn(s.title, 'title-md-semi-bold')}>
|
||||
<span>{t('datasetCreation.stepOne.pagePreview')}</span>
|
||||
<div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}>
|
||||
<XMarkIcon className='h-4 w-4'></XMarkIcon>
|
||||
</div>
|
||||
</div>
|
||||
<div className='leading-5 text-sm font-medium text-gray-900 break-words'>
|
||||
<div className='title-sm-semi-bold text-text-primary break-words'>
|
||||
{payload.title}
|
||||
</div>
|
||||
<div className='truncate leading-[18px] text-xs font-normal text-gray-500' title={payload.source_url}>{payload.source_url}</div>
|
||||
<div className='truncate system-xs-medium text-text-tertiary' title={payload.source_url}>{payload.source_url}</div>
|
||||
</div>
|
||||
<div className={cn(s.previewContent)}>
|
||||
<div className={cn(s.fileContent, 'body-md-regular')}>{payload.markdown}</div>
|
||||
<div className={cn(s.previewContent, 'body-md-regular')}>
|
||||
<div className={cn(s.fileContent)}>{payload.markdown}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -165,7 +165,7 @@ const ChildSegmentList: FC<IChildSegmentCardProps> = ({
|
||||
onDelete={() => onDelete?.(childChunk.segment_id, childChunk.id)}
|
||||
labelClassName={focused ? 'bg-state-accent-solid text-text-primary-on-surface' : ''}
|
||||
labelInnerClassName={'text-[10px] font-semibold align-bottom leading-6'}
|
||||
contentClassName={classNames('!leading-6', focused ? 'bg-state-accent-hover-alt text-text-primary' : '')}
|
||||
contentClassName={classNames('!leading-6', focused ? 'bg-state-accent-hover-alt text-text-primary' : 'text-text-secondary')}
|
||||
showDivider={false}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
|
||||
@@ -16,7 +16,7 @@ const Textarea: FC<IContentProps> = React.memo(({
|
||||
return (
|
||||
<textarea
|
||||
className={classNames(
|
||||
'disabled:bg-transparent inset-0 outline-none border-none appearance-none resize-none w-full overflow-y-auto',
|
||||
'bg-transparent inset-0 outline-none border-none appearance-none resize-none w-full overflow-y-auto',
|
||||
className,
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
@@ -82,7 +82,7 @@ const AutoResizeTextArea: FC<IAutoResizeTextAreaProps> = React.memo(({
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
className={classNames(
|
||||
'disabled:bg-transparent inset-0 outline-none border-none appearance-none resize-none w-full',
|
||||
'bg-transparent inset-0 outline-none border-none appearance-none resize-none w-full',
|
||||
className,
|
||||
)}
|
||||
style={{
|
||||
|
||||
@@ -103,15 +103,13 @@ const SegmentAdd: FC<ISegmentAddProps> = ({
|
||||
manualClose
|
||||
trigger='click'
|
||||
htmlContent={
|
||||
<div className='w-full p-1'>
|
||||
<button
|
||||
type='button'
|
||||
className='w-full py-1.5 px-2 flex items-center hover:bg-state-base-hover rounded-lg text-text-secondary system-md-regular'
|
||||
onClick={showBatchModal}
|
||||
>
|
||||
{t('datasetDocuments.list.action.batchAdd')}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type='button'
|
||||
className='w-full py-1.5 px-2 flex items-center rounded-lg text-text-secondary system-md-regular'
|
||||
onClick={showBatchModal}
|
||||
>
|
||||
{t('datasetDocuments.list.action.batchAdd')}
|
||||
</button>
|
||||
}
|
||||
btnElement={
|
||||
<div className='flex justify-center items-center' >
|
||||
|
||||
@@ -57,7 +57,7 @@ const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => {
|
||||
|
||||
return (
|
||||
<div className='flex' style={{ height: 'calc(100vh - 56px)' }}>
|
||||
<div className="grow bg-white">
|
||||
<div className="grow">
|
||||
{!documentDetail && <Loading type='app' />}
|
||||
{dataset && documentDetail && (
|
||||
<StepTwo
|
||||
|
||||
@@ -16,7 +16,6 @@ import { useContext } from 'use-context-selector'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import dayjs from 'dayjs'
|
||||
import { Edit03 } from '../../base/icons/src/vender/solid/general'
|
||||
import { Globe01 } from '../../base/icons/src/vender/line/mapsAndTravel'
|
||||
import ChunkingModeLabel from '../common/chunking-mode-label'
|
||||
import FileTypeIcon from '../../base/file-uploader/file-type-icon'
|
||||
@@ -340,7 +339,7 @@ export const OperationAction: FC<{
|
||||
position='br'
|
||||
btnElement={
|
||||
<div className={cn(s.commonIcon)}>
|
||||
<RiMoreFill className='w-4 h-4 text-text-components-button-secondary-text' />
|
||||
<RiMoreFill className='w-4 h-4 text-components-button-secondary-text' />
|
||||
</div>
|
||||
}
|
||||
btnClassName={open => cn(isListScene ? s.actionIconWrapperList : s.actionIconWrapperDetail, open ? '!hover:bg-state-base-hover !shadow-none' : '!bg-transparent')}
|
||||
@@ -579,7 +578,7 @@ const DocumentList: FC<IDocumentListProps> = ({
|
||||
handleShowRenameModal(doc)
|
||||
}}
|
||||
>
|
||||
<Edit03 className='w-4 h-4 text-text-tertiary' />
|
||||
<RiEditLine className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
@@ -59,7 +59,7 @@ const RenameModal: FC<Props> = ({
|
||||
isShow
|
||||
onClose={onClose}
|
||||
>
|
||||
<div className={'mt-6 font-medium text-sm leading-[21px] text-gray-900'}>{t('datasetDocuments.list.table.name')}</div>
|
||||
<div className={'mt-6 font-medium text-sm leading-[21px] text-text-primary'}>{t('datasetDocuments.list.table.name')}</div>
|
||||
<Input
|
||||
className='mt-2 h-10'
|
||||
value={newName}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user