Commit bd6248ea authored by StyleZhang's avatar StyleZhang

merge main

parents 52cf3c98 dd1172b5
......@@ -27,4 +27,4 @@ RUN chmod +x /entrypoint.sh
ARG COMMIT_SHA
ENV COMMIT_SHA ${COMMIT_SHA}
ENTRYPOINT ["/entrypoint.sh"]
\ No newline at end of file
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
\ No newline at end of file
......@@ -2,6 +2,7 @@ import datetime
import logging
import random
import string
import time
import click
from flask import current_app
......@@ -13,7 +14,7 @@ from libs.helper import email as email_validate
from extensions.ext_database import db
from libs.rsa import generate_key_pair
from models.account import InvitationCode, Tenant
from models.dataset import Dataset
from models.dataset import Dataset, DatasetQuery, Document, DocumentSegment
from models.model import Account
import secrets
import base64
......@@ -172,7 +173,7 @@ def recreate_all_dataset_indexes():
page = 1
while True:
try:
datasets = db.session.query(Dataset).filter(Dataset.indexing_technique == 'high_quality')\
datasets = db.session.query(Dataset).filter(Dataset.indexing_technique == 'high_quality') \
.order_by(Dataset.created_at.desc()).paginate(page=page, per_page=50)
except NotFound:
break
......@@ -188,12 +189,66 @@ def recreate_all_dataset_indexes():
else:
click.echo('passed.')
except Exception as e:
click.echo(click.style('Recreate dataset index error: {} {}'.format(e.__class__.__name__, str(e)), fg='red'))
click.echo(
click.style('Recreate dataset index error: {} {}'.format(e.__class__.__name__, str(e)), fg='red'))
continue
click.echo(click.style('Congratulations! Recreate {} dataset indexes.'.format(recreate_count), fg='green'))
@click.command('clean-unused-dataset-indexes', help='Clean unused dataset indexes.')
def clean_unused_dataset_indexes():
click.echo(click.style('Start clean unused dataset indexes.', fg='green'))
clean_days = int(current_app.config.get('CLEAN_DAY_SETTING'))
start_at = time.perf_counter()
thirty_days_ago = datetime.datetime.now() - datetime.timedelta(days=clean_days)
page = 1
while True:
try:
datasets = db.session.query(Dataset).filter(Dataset.created_at < thirty_days_ago) \
.order_by(Dataset.created_at.desc()).paginate(page=page, per_page=50)
except NotFound:
break
page += 1
for dataset in datasets:
dataset_query = db.session.query(DatasetQuery).filter(
DatasetQuery.created_at > thirty_days_ago,
DatasetQuery.dataset_id == dataset.id
).all()
if not dataset_query or len(dataset_query) == 0:
documents = db.session.query(Document).filter(
Document.dataset_id == dataset.id,
Document.indexing_status == 'completed',
Document.enabled == True,
Document.archived == False,
Document.updated_at > thirty_days_ago
).all()
if not documents or len(documents) == 0:
try:
# remove index
vector_index = IndexBuilder.get_index(dataset, 'high_quality')
kw_index = IndexBuilder.get_index(dataset, 'economy')
# delete from vector index
if vector_index:
vector_index.delete()
kw_index.delete()
# update document
update_params = {
Document.enabled: False
}
Document.query.filter_by(dataset_id=dataset.id).update(update_params)
db.session.commit()
click.echo(click.style('Cleaned unused dataset {} from db success!'.format(dataset.id),
fg='green'))
except Exception as e:
click.echo(
click.style('clean dataset index error: {} {}'.format(e.__class__.__name__, str(e)),
fg='red'))
end_at = time.perf_counter()
click.echo(click.style('Cleaned unused dataset from db success latency: {}'.format(end_at - start_at), fg='green'))
@click.command('sync-anthropic-hosted-providers', help='Sync anthropic hosted providers.')
def sync_anthropic_hosted_providers():
click.echo(click.style('Start sync anthropic hosted providers.', fg='green'))
......@@ -218,7 +273,9 @@ def sync_anthropic_hosted_providers():
)
count += 1
except Exception as e:
click.echo(click.style('Sync tenant anthropic hosted provider error: {} {}'.format(e.__class__.__name__, str(e)), fg='red'))
click.echo(click.style(
'Sync tenant anthropic hosted provider error: {} {}'.format(e.__class__.__name__, str(e)),
fg='red'))
continue
click.echo(click.style('Congratulations! Synced {} anthropic hosted providers.'.format(count), fg='green'))
......@@ -231,3 +288,4 @@ def register_commands(app):
app.cli.add_command(reset_encrypt_key_pair)
app.cli.add_command(recreate_all_dataset_indexes)
app.cli.add_command(sync_anthropic_hosted_providers)
app.cli.add_command(clean_unused_dataset_indexes)
......@@ -53,7 +53,8 @@ DEFAULTS = {
'DEFAULT_LLM_PROVIDER': 'openai',
'OPENAI_HOSTED_QUOTA_LIMIT': 200,
'ANTHROPIC_HOSTED_QUOTA_LIMIT': 1000,
'TENANT_DOCUMENT_COUNT': 100
'TENANT_DOCUMENT_COUNT': 100,
'CLEAN_DAY_SETTING': 30
}
......@@ -215,6 +216,7 @@ class Config:
self.NOTION_INTEGRATION_TOKEN = get_env('NOTION_INTEGRATION_TOKEN')
self.TENANT_DOCUMENT_COUNT = get_env('TENANT_DOCUMENT_COUNT')
self.CLEAN_DAY_SETTING = get_env('CLEAN_DAY_SETTING')
class CloudEditionConfig(Config):
......
......@@ -3,7 +3,6 @@ from flask import request
from flask_login import login_required, current_user
from flask_restful import Resource, reqparse, fields, marshal, marshal_with
from werkzeug.exceptions import NotFound, Forbidden
import services
from controllers.console import api
from controllers.console.datasets.error import DatasetNameDuplicateError
......
......@@ -2,6 +2,7 @@ import json
import logging
from typing import Optional, Union
import openai
import requests
from core.llm.provider.base import BaseProvider
......@@ -11,30 +12,37 @@ from models.provider import ProviderName
class AzureProvider(BaseProvider):
def get_models(self, model_id: Optional[str] = None, credentials: Optional[dict] = None) -> list[dict]:
credentials = self.get_credentials(model_id) if not credentials else credentials
url = "{}/openai/deployments?api-version={}".format(
str(credentials.get('openai_api_base')),
str(credentials.get('openai_api_version'))
)
headers = {
"api-key": str(credentials.get('openai_api_key')),
"content-type": "application/json; charset=utf-8"
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
result = response.json()
return [{
'id': deployment['id'],
'name': '{} ({})'.format(deployment['id'], deployment['model'])
} for deployment in result['data'] if deployment['status'] == 'succeeded']
else:
if response.status_code == 401:
raise AzureAuthenticationError()
return []
def check_embedding_model(self, credentials: Optional[dict] = None):
credentials = self.get_credentials('text-embedding-ada-002') if not credentials else credentials
try:
result = openai.Embedding.create(input=['test'],
engine='text-embedding-ada-002',
timeout=60,
api_key=str(credentials.get('openai_api_key')),
api_base=str(credentials.get('openai_api_base')),
api_type='azure',
api_version=str(credentials.get('openai_api_version')))["data"][0][
"embedding"]
except openai.error.AuthenticationError as e:
raise AzureAuthenticationError(str(e))
except openai.error.APIConnectionError as e:
raise AzureRequestFailedError(
'Failed to request Azure OpenAI, please check your API Base Endpoint, The format is `https://xxx.openai.azure.com/`')
except openai.error.InvalidRequestError as e:
if e.http_status == 404:
raise AzureRequestFailedError("Please check your 'gpt-3.5-turbo' or 'text-embedding-ada-002' "
"deployment name is exists in Azure AI")
else:
raise AzureRequestFailedError('Failed to request Azure OpenAI. Status code: {}'.format(response.status_code))
raise AzureRequestFailedError(
'Failed to request Azure OpenAI. cause: {}'.format(str(e)))
except openai.error.OpenAIError as e:
raise AzureRequestFailedError(
'Failed to request Azure OpenAI. cause: {}'.format(str(e)))
if not isinstance(result, list):
raise AzureRequestFailedError('Failed to request Azure OpenAI.')
def get_credentials(self, model_id: Optional[str] = None) -> dict:
"""
......@@ -94,31 +102,11 @@ class AzureProvider(BaseProvider):
if 'openai_api_version' not in config:
config['openai_api_version'] = '2023-03-15-preview'
models = self.get_models(credentials=config)
if not models:
raise ValidateFailedError("Please add deployments for "
"'gpt-3.5-turbo', 'text-embedding-ada-002' (required) "
"and 'gpt-4', 'gpt-35-turbo-16k', 'text-davinci-003' (optional).")
fixed_model_ids = [
'gpt-35-turbo',
'text-embedding-ada-002'
]
current_model_ids = [model['id'] for model in models]
missing_model_ids = [fixed_model_id for fixed_model_id in fixed_model_ids if
fixed_model_id not in current_model_ids]
if missing_model_ids:
raise ValidateFailedError("Please add deployments for '{}'.".format(", ".join(missing_model_ids)))
self.check_embedding_model(credentials=config)
except ValidateFailedError as e:
raise e
except AzureAuthenticationError:
raise ValidateFailedError('Validation failed, please check your API Key.')
except (requests.ConnectionError, requests.RequestException):
raise ValidateFailedError('Validation failed, please check your API Base Endpoint.')
except AzureRequestFailedError as ex:
raise ValidateFailedError('Validation failed, error: {}.'.format(str(ex)))
except Exception as ex:
......
......@@ -26,4 +26,4 @@ RUN chmod +x /entrypoint.sh
ARG COMMIT_SHA
ENV COMMIT_SHA ${COMMIT_SHA}
ENTRYPOINT ["/entrypoint.sh"]
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
......@@ -3,6 +3,7 @@ import type { ReactNode } from 'react'
import SwrInitor from '@/app/components/swr-initor'
import { AppContextProvider } from '@/context/app-context'
import GA, { GaType } from '@/app/components/base/ga'
import HeaderWrapper from '@/app/components/header/HeaderWrapper'
import Header from '@/app/components/header'
const Layout = ({ children }: { children: ReactNode }) => {
......@@ -11,7 +12,9 @@ const Layout = ({ children }: { children: ReactNode }) => {
<GA gaType={GaType.admin} />
<SwrInitor>
<AppContextProvider>
<Header />
<HeaderWrapper>
<Header />
</HeaderWrapper>
{children}
</AppContextProvider>
</SwrInitor>
......
......@@ -40,7 +40,7 @@ const Config: FC = () => {
} = useContext(ConfigContext)
const isChatApp = mode === AppType.chat
const { data: userInfo } = useSWR({ url: '/info' }, fetchTenantInfo)
const targetProvider = userInfo?.providers?.find(({ token_is_set, is_valid }) => token_is_set && is_valid)
const openaiProvider = userInfo?.providers?.find(({ token_is_set, is_valid, provider_name }) => token_is_set && is_valid && provider_name === 'openai')
const promptTemplate = modelConfig.configs.prompt_template
const promptVariables = modelConfig.configs.prompt_variables
......@@ -92,7 +92,7 @@ const Config: FC = () => {
},
})
const hasChatConfig = isChatApp && (featureConfig.openingStatement || featureConfig.suggestedQuestionsAfterAnswer || (featureConfig.speechToText && targetProvider?.provider_name === 'openai'))
const hasChatConfig = isChatApp && (featureConfig.openingStatement || featureConfig.suggestedQuestionsAfterAnswer || (featureConfig.speechToText && openaiProvider))
const hasToolbox = false
const [showAutomatic, { setTrue: showAutomaticTrue, setFalse: showAutomaticFalse }] = useBoolean(false)
......@@ -122,7 +122,7 @@ const Config: FC = () => {
isChatApp={isChatApp}
config={featureConfig}
onChange={handleFeatureChange}
showSpeechToTextItem={targetProvider?.provider_name === 'openai'}
showSpeechToTextItem={!!openaiProvider}
/>
)}
{showAutomatic && (
......
......@@ -116,22 +116,22 @@ const Debug: FC<IDebug> = ({
}
const checkCanSend = () => {
let hasEmptyInput = false
let hasEmptyInput = ''
const requiredVars = modelConfig.configs.prompt_variables.filter(({ key, name, required }) => {
const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
return res
}) // compatible with old version
// debugger
requiredVars.forEach(({ key }) => {
requiredVars.forEach(({ key, name }) => {
if (hasEmptyInput)
return
if (!inputs[key])
hasEmptyInput = true
hasEmptyInput = name
})
if (hasEmptyInput) {
logError(t('appDebug.errorMessage.valueOfVarRequired'))
logError(t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }))
return false
}
return !hasEmptyInput
......
......@@ -122,6 +122,7 @@ const PromptValuePanel: FC<IPromptValuePanelProps> = ({
items={(options || []).map(i => ({ name: i, value: i }))}
allowSearch={false}
bgClassName='bg-gray-50'
overlayClassName='z-[11]'
/>
)
: (
......
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import cn from 'classnames'
import style from './style.module.css'
......@@ -43,10 +43,15 @@ const prefixEmbedded = 'appOverview.overview.appInfo.embedded'
type Option = keyof typeof OPTION_MAP
type OptionStatus = {
iframe: boolean
scripts: boolean
}
const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
const { t } = useTranslation()
const [option, setOption] = useState<Option>('iframe')
const [isCopied, setIsCopied] = useState({ iframe: false, scripts: false })
const [isCopied, setIsCopied] = useState<OptionStatus>({ iframe: false, scripts: false })
const [_, copy] = useCopyToClipboard()
const { langeniusVersionInfo } = useAppContext()
......@@ -56,6 +61,19 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
setIsCopied({ ...isCopied, [option]: true })
}
// when toggle option, reset then copy status
const resetCopyStatus = () => {
const cache = { ...isCopied }
Object.keys(cache).forEach((key) => {
cache[key as keyof OptionStatus] = false
})
setIsCopied(cache)
}
useEffect(() => {
resetCopyStatus()
}, [isShow])
return (
<Modal
title={t(`${prefixEmbedded}.title`)}
......@@ -77,7 +95,10 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken }: Props) => {
style[`${v}Icon`],
option === v && style.active,
)}
onClick={() => setOption(v as Option)}
onClick={() => {
setOption(v as Option)
resetCopyStatus()
}}
></div>
)
})}
......
'use client'
import cn from 'classnames'
interface IAvatarProps {
type AvatarProps = {
name: string
avatar?: string
size?: number
className?: string
textClassName?: string
}
const Avatar = ({
name,
avatar,
size = 30,
className
}: IAvatarProps) => {
const avatarClassName = `shrink-0 flex items-center rounded-full bg-primary-600`
const style = { width: `${size}px`, height:`${size}px`, fontSize: `${size}px`, lineHeight: `${size}px` }
className,
textClassName,
}: AvatarProps) => {
const avatarClassName = 'shrink-0 flex items-center rounded-full bg-primary-600'
const style = { width: `${size}px`, height: `${size}px`, fontSize: `${size}px`, lineHeight: `${size}px` }
if (avatar) {
return (
<img
className={cn(avatarClassName, className)}
<img
className={cn(avatarClassName, className)}
style={style}
alt={name}
alt={name}
src={avatar}
/>
)
}
return (
<div
className={cn(avatarClassName, className)}
<div
className={cn(avatarClassName, className)}
style={style}
>
<div
className={`text-center text-white scale-[0.4]`}
<div
className={cn(textClassName, 'text-center text-white scale-[0.4]')}
style={style}
>
{name[0].toLocaleUpperCase()}
......@@ -42,4 +44,4 @@ const Avatar = ({
)
}
export default Avatar
\ No newline at end of file
export default Avatar
<svg width="50" height="26" viewBox="0 0 50 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Dify">
<path d="M6.61784 2.064C8.37784 2.064 9.92184 2.408 11.2498 3.096C12.5938 3.784 13.6258 4.768 14.3458 6.048C15.0818 7.312 15.4498 8.784 15.4498 10.464C15.4498 12.144 15.0818 13.616 14.3458 14.88C13.6258 16.128 12.5938 17.096 11.2498 17.784C9.92184 18.472 8.37784 18.816 6.61784 18.816H0.761841V2.064H6.61784ZM6.49784 15.96C8.25784 15.96 9.61784 15.48 10.5778 14.52C11.5378 13.56 12.0178 12.208 12.0178 10.464C12.0178 8.72 11.5378 7.36 10.5778 6.384C9.61784 5.392 8.25784 4.896 6.49784 4.896H4.12184V15.96H6.49784Z" fill="#1D2939"/>
<path d="M20.869 3.936C20.277 3.936 19.781 3.752 19.381 3.384C18.997 3 18.805 2.528 18.805 1.968C18.805 1.408 18.997 0.944 19.381 0.576C19.781 0.192 20.277 0 20.869 0C21.461 0 21.949 0.192 22.333 0.576C22.733 0.944 22.933 1.408 22.933 1.968C22.933 2.528 22.733 3 22.333 3.384C21.949 3.752 21.461 3.936 20.869 3.936ZM22.525 5.52V18.816H19.165V5.52H22.525Z" fill="#1D2939"/>
<path d="M33.1407 8.28H30.8127V18.816H27.4047V8.28H25.8927V5.52H27.4047V4.848C27.4047 3.216 27.8687 2.016 28.7967 1.248C29.7247 0.48 31.1247 0.12 32.9967 0.168001V3C32.1807 2.984 31.6127 3.12 31.2927 3.408C30.9727 3.696 30.8127 4.216 30.8127 4.968V5.52H33.1407V8.28Z" fill="#1D2939"/>
<path d="M49.2381 5.52L41.0061 25.104H37.4301L40.3101 18.48L34.9821 5.52H38.7501L42.1821 14.808L45.6621 5.52H49.2381Z" fill="#1D2939"/>
</g>
</svg>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="github">
<path id="Vector" d="M9 1.125C4.64906 1.125 1.125 4.64906 1.125 9C1.125 12.4847 3.37922 15.428 6.50953 16.4714C6.90328 16.5403 7.05094 16.3041 7.05094 16.0973C7.05094 15.9103 7.04109 15.2902 7.04109 14.6306C5.0625 14.9948 4.55062 14.1483 4.39312 13.7053C4.30453 13.4789 3.92063 12.78 3.58594 12.593C3.31031 12.4453 2.91656 12.0811 3.57609 12.0712C4.19625 12.0614 4.63922 12.6422 4.78688 12.8784C5.49563 14.0695 6.62766 13.7348 7.08047 13.5281C7.14938 13.0163 7.35609 12.6717 7.5825 12.4748C5.83031 12.278 3.99938 11.5987 3.99938 8.58656C3.99938 7.73016 4.30453 7.02141 4.80656 6.47016C4.72781 6.27328 4.45219 5.46609 4.88531 4.38328C4.88531 4.38328 5.54484 4.17656 7.05094 5.19047C7.68094 5.01328 8.35031 4.92469 9.01969 4.92469C9.68906 4.92469 10.3584 5.01328 10.9884 5.19047C12.4945 4.16672 13.1541 4.38328 13.1541 4.38328C13.5872 5.46609 13.3116 6.27328 13.2328 6.47016C13.7348 7.02141 14.04 7.72031 14.04 8.58656C14.04 11.6086 12.1992 12.278 10.447 12.4748C10.7325 12.7209 10.9786 13.1934 10.9786 13.9317C10.9786 14.985 10.9688 15.8316 10.9688 16.0973C10.9688 16.3041 11.1164 16.5502 11.5102 16.4714C13.0735 15.9436 14.432 14.9389 15.3943 13.5986C16.3567 12.2583 16.8746 10.65 16.875 9C16.875 4.64906 13.3509 1.125 9 1.125Z" fill="#24292F"/>
</g>
</svg>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="arrow-up-right">
<path id="Icon" d="M4.08325 9.91665L9.91659 4.08331M9.91659 4.08331H4.08325M9.91659 4.08331V9.91665" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="chevron-down">
<path id="Icon" d="M3 4.5L6 7.5L9 4.5" stroke="#344054" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="chevron-right">
<path id="Icon" d="M5.25 10.5L8.75 7L5.25 3.5" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="check">
<path id="Icon" d="M13.3334 4L6.00008 11.3333L2.66675 8" stroke="#155EEF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="log-out-01">
<path id="Icon" d="M9.33333 9.91667L12.25 7M12.25 7L9.33333 4.08333M12.25 7H5.25M5.25 1.75H4.55C3.56991 1.75 3.07986 1.75 2.70552 1.94074C2.37623 2.10852 2.10852 2.37623 1.94074 2.70552C1.75 3.07986 1.75 3.56991 1.75 4.55V9.45C1.75 10.4301 1.75 10.9201 1.94074 11.2945C2.10852 11.6238 2.37623 11.8915 2.70552 12.0593C3.07986 12.25 3.56991 12.25 4.55 12.25H5.25" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="terminal-square">
<path id="Solid" fill-rule="evenodd" clip-rule="evenodd" d="M8.91927 1H3.08073C2.81716 0.999992 2.58977 0.999984 2.40249 1.01529C2.20481 1.03144 2.00821 1.06709 1.81902 1.16349C1.53677 1.3073 1.3073 1.53677 1.16349 1.81902C1.06709 2.00821 1.03144 2.20481 1.01529 2.40249C0.999984 2.58977 0.999992 2.81714 1 3.08071V8.91927C0.999992 9.18284 0.999984 9.41023 1.01529 9.59752C1.03144 9.79519 1.06709 9.9918 1.16349 10.181C1.3073 10.4632 1.53677 10.6927 1.81902 10.8365C2.00821 10.9329 2.20481 10.9686 2.40249 10.9847C2.58977 11 2.81715 11 3.08072 11H8.91928C9.18285 11 9.41023 11 9.59752 10.9847C9.79519 10.9686 9.9918 10.9329 10.181 10.8365C10.4632 10.6927 10.6927 10.4632 10.8365 10.181C10.9329 9.9918 10.9686 9.79519 10.9847 9.59752C11 9.41023 11 9.18285 11 8.91928V3.08072C11 2.81715 11 2.58977 10.9847 2.40249C10.9686 2.20481 10.9329 2.00821 10.8365 1.81902C10.6927 1.53677 10.4632 1.3073 10.181 1.16349C9.9918 1.06709 9.79519 1.03144 9.59752 1.01529C9.41023 0.999984 9.18284 0.999992 8.91927 1ZM3.85355 4.14645C3.65829 3.95118 3.34171 3.95118 3.14645 4.14645C2.95118 4.34171 2.95118 4.65829 3.14645 4.85355L4.29289 6L3.14645 7.14645C2.95118 7.34171 2.95118 7.65829 3.14645 7.85355C3.34171 8.04882 3.65829 8.04882 3.85355 7.85355L5.35355 6.35355C5.54882 6.15829 5.54882 5.84171 5.35355 5.64645L3.85355 4.14645ZM6.5 7C6.22386 7 6 7.22386 6 7.5C6 7.77614 6.22386 8 6.5 8H8.5C8.77614 8 9 7.77614 9 7.5C9 7.22386 8.77614 7 8.5 7H6.5Z" fill="#B54708"/>
</g>
</svg>
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="beaker-02">
<path id="Solid" fill-rule="evenodd" clip-rule="evenodd" d="M4.13856 0.500003H7.8617C7.92126 0.49998 7.99238 0.499953 8.05504 0.505073C8.12765 0.511005 8.23165 0.526227 8.34062 0.581751C8.48174 0.653656 8.59648 0.768392 8.66838 0.909513C8.72391 1.01849 8.73913 1.12248 8.74506 1.19509C8.75018 1.25775 8.75015 1.32888 8.75013 1.38844V2.61157C8.75015 2.67113 8.75018 2.74226 8.74506 2.80492C8.73913 2.87753 8.72391 2.98153 8.66838 3.0905C8.59648 3.23162 8.48174 3.34636 8.34062 3.41826C8.23165 3.47379 8.12765 3.48901 8.05504 3.49494C8.03725 3.49639 8.01877 3.49743 8.00006 3.49817V5.2506C8.00006 5.55312 8.00408 5.61265 8.01723 5.66153C8.03245 5.71807 8.05747 5.7715 8.09117 5.81939C8.1203 5.86078 8.16346 5.90197 8.39586 6.09564L10.2807 7.66627C10.4566 7.81255 10.6116 7.94145 10.7267 8.10509C10.8278 8.24875 10.9029 8.40904 10.9486 8.57867C11.0005 8.7719 11.0003 8.97351 11.0001 9.2023C11.0001 9.39886 11.0002 9.59542 11.0002 9.79198C11.0003 9.98232 11.0005 10.1463 10.9713 10.2927C10.853 10.8877 10.3878 11.3529 9.7928 11.4712C9.64637 11.5003 9.48246 11.5002 9.29211 11.5001H2.70822C2.51787 11.5002 2.35396 11.5003 2.20753 11.4712C1.98473 11.4269 1.78014 11.334 1.60515 11.2038C1.42854 11.0725 1.28221 10.9034 1.17753 10.7077C1.10892 10.5796 1.05831 10.4401 1.02899 10.2927C0.999862 10.1463 0.999992 9.98233 1.00014 9.79199C1.00014 9.59542 1.00006 9.39886 1.00003 9.20229C0.999794 8.97351 0.999584 8.7719 1.05157 8.57867C1.09721 8.40904 1.17229 8.24875 1.27338 8.10509C1.38855 7.94145 1.54356 7.81255 1.71947 7.66627L3.60427 6.09564C3.83667 5.90197 3.87983 5.86078 3.90896 5.81939C3.94266 5.7715 3.96768 5.71807 3.9829 5.66153C3.99605 5.61265 4.00006 5.55312 4.00006 5.2506V3.49817C3.9814 3.49743 3.96297 3.49639 3.94521 3.49494C3.8726 3.48901 3.76861 3.47379 3.65964 3.41826C3.51851 3.34636 3.40378 3.23162 3.33187 3.0905C3.27635 2.98153 3.26113 2.87753 3.25519 2.80492C3.25008 2.74226 3.2501 2.67113 3.25013 2.61158V1.38844C3.2501 1.32888 3.25008 1.25775 3.25519 1.19509C3.26113 1.12248 3.27635 1.01849 3.33187 0.909513C3.40378 0.768392 3.51851 0.653656 3.65964 0.581751C3.76861 0.526227 3.8726 0.511005 3.94521 0.505073C4.00787 0.499953 4.079 0.49998 4.13856 0.500003ZM9.11909 8.00004H2.88104L4.28066 6.83373C4.45657 6.68745 4.61158 6.55855 4.72675 6.39491C4.82784 6.25125 4.90292 6.09096 4.94856 5.92133C5.00054 5.7281 5.00033 5.52649 5.0001 5.29771L5.00006 3.50001H7.00006L7.00003 5.29771C6.99979 5.52649 6.99958 5.7281 7.05157 5.92133C7.09721 6.09096 7.17229 6.25125 7.27338 6.39491C7.38855 6.55855 7.54356 6.68745 7.71947 6.83373L9.11909 8.00004Z" fill="#0E7090"/>
</g>
</svg>
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "50",
"height": "26",
"viewBox": "0 0 50 26",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "Dify"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"d": "M6.61784 2.064C8.37784 2.064 9.92184 2.408 11.2498 3.096C12.5938 3.784 13.6258 4.768 14.3458 6.048C15.0818 7.312 15.4498 8.784 15.4498 10.464C15.4498 12.144 15.0818 13.616 14.3458 14.88C13.6258 16.128 12.5938 17.096 11.2498 17.784C9.92184 18.472 8.37784 18.816 6.61784 18.816H0.761841V2.064H6.61784ZM6.49784 15.96C8.25784 15.96 9.61784 15.48 10.5778 14.52C11.5378 13.56 12.0178 12.208 12.0178 10.464C12.0178 8.72 11.5378 7.36 10.5778 6.384C9.61784 5.392 8.25784 4.896 6.49784 4.896H4.12184V15.96H6.49784Z",
"fill": "#1D2939"
},
"children": []
},
{
"type": "element",
"name": "path",
"attributes": {
"d": "M20.869 3.936C20.277 3.936 19.781 3.752 19.381 3.384C18.997 3 18.805 2.528 18.805 1.968C18.805 1.408 18.997 0.944 19.381 0.576C19.781 0.192 20.277 0 20.869 0C21.461 0 21.949 0.192 22.333 0.576C22.733 0.944 22.933 1.408 22.933 1.968C22.933 2.528 22.733 3 22.333 3.384C21.949 3.752 21.461 3.936 20.869 3.936ZM22.525 5.52V18.816H19.165V5.52H22.525Z",
"fill": "#1D2939"
},
"children": []
},
{
"type": "element",
"name": "path",
"attributes": {
"d": "M33.1407 8.28H30.8127V18.816H27.4047V8.28H25.8927V5.52H27.4047V4.848C27.4047 3.216 27.8687 2.016 28.7967 1.248C29.7247 0.48 31.1247 0.12 32.9967 0.168001V3C32.1807 2.984 31.6127 3.12 31.2927 3.408C30.9727 3.696 30.8127 4.216 30.8127 4.968V5.52H33.1407V8.28Z",
"fill": "#1D2939"
},
"children": []
},
{
"type": "element",
"name": "path",
"attributes": {
"d": "M49.2381 5.52L41.0061 25.104H37.4301L40.3101 18.48L34.9821 5.52H38.7501L42.1821 14.808L45.6621 5.52H49.2381Z",
"fill": "#1D2939"
},
"children": []
}
]
}
]
},
"name": "Dify"
}
\ No newline at end of file
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './Dify.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
export default Icon
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "18",
"height": "18",
"viewBox": "0 0 18 18",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "github"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Vector",
"d": "M9 1.125C4.64906 1.125 1.125 4.64906 1.125 9C1.125 12.4847 3.37922 15.428 6.50953 16.4714C6.90328 16.5403 7.05094 16.3041 7.05094 16.0973C7.05094 15.9103 7.04109 15.2902 7.04109 14.6306C5.0625 14.9948 4.55062 14.1483 4.39312 13.7053C4.30453 13.4789 3.92063 12.78 3.58594 12.593C3.31031 12.4453 2.91656 12.0811 3.57609 12.0712C4.19625 12.0614 4.63922 12.6422 4.78688 12.8784C5.49563 14.0695 6.62766 13.7348 7.08047 13.5281C7.14938 13.0163 7.35609 12.6717 7.5825 12.4748C5.83031 12.278 3.99938 11.5987 3.99938 8.58656C3.99938 7.73016 4.30453 7.02141 4.80656 6.47016C4.72781 6.27328 4.45219 5.46609 4.88531 4.38328C4.88531 4.38328 5.54484 4.17656 7.05094 5.19047C7.68094 5.01328 8.35031 4.92469 9.01969 4.92469C9.68906 4.92469 10.3584 5.01328 10.9884 5.19047C12.4945 4.16672 13.1541 4.38328 13.1541 4.38328C13.5872 5.46609 13.3116 6.27328 13.2328 6.47016C13.7348 7.02141 14.04 7.72031 14.04 8.58656C14.04 11.6086 12.1992 12.278 10.447 12.4748C10.7325 12.7209 10.9786 13.1934 10.9786 13.9317C10.9786 14.985 10.9688 15.8316 10.9688 16.0973C10.9688 16.3041 11.1164 16.5502 11.5102 16.4714C13.0735 15.9436 14.432 14.9389 15.3943 13.5986C16.3567 12.2583 16.8746 10.65 16.875 9C16.875 4.64906 13.3509 1.125 9 1.125Z",
"fill": "#24292F"
},
"children": []
}
]
}
]
},
"name": "Github"
}
\ No newline at end of file
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './Github.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
export default Icon
export { default as Dify } from './Dify'
export { default as Github } from './Github'
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "14",
"height": "14",
"viewBox": "0 0 14 14",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "arrow-up-right"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Icon",
"d": "M4.08325 9.91665L9.91659 4.08331M9.91659 4.08331H4.08325M9.91659 4.08331V9.91665",
"stroke": "currentColor",
"stroke-width": "1.25",
"stroke-linecap": "round",
"stroke-linejoin": "round"
},
"children": []
}
]
}
]
},
"name": "ArrowUpRight"
}
\ No newline at end of file
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './ArrowUpRight.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
export default Icon
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "12",
"height": "12",
"viewBox": "0 0 12 12",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "chevron-down"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Icon",
"d": "M3 4.5L6 7.5L9 4.5",
"stroke": "currentColor",
"stroke-width": "1.5",
"stroke-linecap": "round",
"stroke-linejoin": "round"
},
"children": []
}
]
}
]
},
"name": "ChevronDown"
}
\ No newline at end of file
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './ChevronDown.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
export default Icon
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "14",
"height": "14",
"viewBox": "0 0 14 14",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "chevron-right"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Icon",
"d": "M5.25 10.5L8.75 7L5.25 3.5",
"stroke": "currentColor",
"stroke-width": "1.25",
"stroke-linecap": "round",
"stroke-linejoin": "round"
},
"children": []
}
]
}
]
},
"name": "ChevronRight"
}
\ No newline at end of file
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './ChevronRight.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
export default Icon
export { default as ArrowNarrowLeft } from './ArrowNarrowLeft'
export { default as ArrowUpRight } from './ArrowUpRight'
export { default as ChevronDown } from './ChevronDown'
export { default as ChevronRight } from './ChevronRight'
export { default as RefreshCw05 } from './RefreshCw05'
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "16",
"height": "16",
"viewBox": "0 0 16 16",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "check"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Icon",
"d": "M13.3334 4L6.00008 11.3333L2.66675 8",
"stroke": "currentColor",
"stroke-width": "1.5",
"stroke-linecap": "round",
"stroke-linejoin": "round"
},
"children": []
}
]
}
]
},
"name": "Check"
}
\ No newline at end of file
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './Check.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
export default Icon
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "14",
"height": "14",
"viewBox": "0 0 14 14",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "log-out-01"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Icon",
"d": "M9.33333 9.91667L12.25 7M12.25 7L9.33333 4.08333M12.25 7H5.25M5.25 1.75H4.55C3.56991 1.75 3.07986 1.75 2.70552 1.94074C2.37623 2.10852 2.10852 2.37623 1.94074 2.70552C1.75 3.07986 1.75 3.56991 1.75 4.55V9.45C1.75 10.4301 1.75 10.9201 1.94074 11.2945C2.10852 11.6238 2.37623 11.8915 2.70552 12.0593C3.07986 12.25 3.56991 12.25 4.55 12.25H5.25",
"stroke": "currentColor",
"stroke-width": "1.25",
"stroke-linecap": "round",
"stroke-linejoin": "round"
},
"children": []
}
]
}
]
},
"name": "LogOut01"
}
\ No newline at end of file
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './LogOut01.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
export default Icon
export { default as Edit03 } from './Edit03'
export { default as Check } from './Check'
export { default as Loading02 } from './Loading02'
export { default as LogOut01 } from './LogOut01'
export { default as Trash03 } from './Trash03'
export { default as XClose } from './XClose'
export { default as X } from './X'
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "12",
"height": "12",
"viewBox": "0 0 12 12",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "terminal-square"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Solid",
"fill-rule": "evenodd",
"clip-rule": "evenodd",
"d": "M8.91927 1H3.08073C2.81716 0.999992 2.58977 0.999984 2.40249 1.01529C2.20481 1.03144 2.00821 1.06709 1.81902 1.16349C1.53677 1.3073 1.3073 1.53677 1.16349 1.81902C1.06709 2.00821 1.03144 2.20481 1.01529 2.40249C0.999984 2.58977 0.999992 2.81714 1 3.08071V8.91927C0.999992 9.18284 0.999984 9.41023 1.01529 9.59752C1.03144 9.79519 1.06709 9.9918 1.16349 10.181C1.3073 10.4632 1.53677 10.6927 1.81902 10.8365C2.00821 10.9329 2.20481 10.9686 2.40249 10.9847C2.58977 11 2.81715 11 3.08072 11H8.91928C9.18285 11 9.41023 11 9.59752 10.9847C9.79519 10.9686 9.9918 10.9329 10.181 10.8365C10.4632 10.6927 10.6927 10.4632 10.8365 10.181C10.9329 9.9918 10.9686 9.79519 10.9847 9.59752C11 9.41023 11 9.18285 11 8.91928V3.08072C11 2.81715 11 2.58977 10.9847 2.40249C10.9686 2.20481 10.9329 2.00821 10.8365 1.81902C10.6927 1.53677 10.4632 1.3073 10.181 1.16349C9.9918 1.06709 9.79519 1.03144 9.59752 1.01529C9.41023 0.999984 9.18284 0.999992 8.91927 1ZM3.85355 4.14645C3.65829 3.95118 3.34171 3.95118 3.14645 4.14645C2.95118 4.34171 2.95118 4.65829 3.14645 4.85355L4.29289 6L3.14645 7.14645C2.95118 7.34171 2.95118 7.65829 3.14645 7.85355C3.34171 8.04882 3.65829 8.04882 3.85355 7.85355L5.35355 6.35355C5.54882 6.15829 5.54882 5.84171 5.35355 5.64645L3.85355 4.14645ZM6.5 7C6.22386 7 6 7.22386 6 7.5C6 7.77614 6.22386 8 6.5 8H8.5C8.77614 8 9 7.77614 9 7.5C9 7.22386 8.77614 7 8.5 7H6.5Z",
"fill": "currentColor"
},
"children": []
}
]
}
]
},
"name": "TerminalSquare"
}
\ No newline at end of file
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './TerminalSquare.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
export default Icon
......@@ -2,3 +2,4 @@ export { default as Container } from './Container'
export { default as Database02 } from './Database02'
export { default as Database03 } from './Database03'
export { default as PuzzlePiece01 } from './PuzzlePiece01'
export { default as TerminalSquare } from './TerminalSquare'
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "12",
"height": "12",
"viewBox": "0 0 12 12",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "beaker-02"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Solid",
"fill-rule": "evenodd",
"clip-rule": "evenodd",
"d": "M4.13856 0.500003H7.8617C7.92126 0.49998 7.99238 0.499953 8.05504 0.505073C8.12765 0.511005 8.23165 0.526227 8.34062 0.581751C8.48174 0.653656 8.59648 0.768392 8.66838 0.909513C8.72391 1.01849 8.73913 1.12248 8.74506 1.19509C8.75018 1.25775 8.75015 1.32888 8.75013 1.38844V2.61157C8.75015 2.67113 8.75018 2.74226 8.74506 2.80492C8.73913 2.87753 8.72391 2.98153 8.66838 3.0905C8.59648 3.23162 8.48174 3.34636 8.34062 3.41826C8.23165 3.47379 8.12765 3.48901 8.05504 3.49494C8.03725 3.49639 8.01877 3.49743 8.00006 3.49817V5.2506C8.00006 5.55312 8.00408 5.61265 8.01723 5.66153C8.03245 5.71807 8.05747 5.7715 8.09117 5.81939C8.1203 5.86078 8.16346 5.90197 8.39586 6.09564L10.2807 7.66627C10.4566 7.81255 10.6116 7.94145 10.7267 8.10509C10.8278 8.24875 10.9029 8.40904 10.9486 8.57867C11.0005 8.7719 11.0003 8.97351 11.0001 9.2023C11.0001 9.39886 11.0002 9.59542 11.0002 9.79198C11.0003 9.98232 11.0005 10.1463 10.9713 10.2927C10.853 10.8877 10.3878 11.3529 9.7928 11.4712C9.64637 11.5003 9.48246 11.5002 9.29211 11.5001H2.70822C2.51787 11.5002 2.35396 11.5003 2.20753 11.4712C1.98473 11.4269 1.78014 11.334 1.60515 11.2038C1.42854 11.0725 1.28221 10.9034 1.17753 10.7077C1.10892 10.5796 1.05831 10.4401 1.02899 10.2927C0.999862 10.1463 0.999992 9.98233 1.00014 9.79199C1.00014 9.59542 1.00006 9.39886 1.00003 9.20229C0.999794 8.97351 0.999584 8.7719 1.05157 8.57867C1.09721 8.40904 1.17229 8.24875 1.27338 8.10509C1.38855 7.94145 1.54356 7.81255 1.71947 7.66627L3.60427 6.09564C3.83667 5.90197 3.87983 5.86078 3.90896 5.81939C3.94266 5.7715 3.96768 5.71807 3.9829 5.66153C3.99605 5.61265 4.00006 5.55312 4.00006 5.2506V3.49817C3.9814 3.49743 3.96297 3.49639 3.94521 3.49494C3.8726 3.48901 3.76861 3.47379 3.65964 3.41826C3.51851 3.34636 3.40378 3.23162 3.33187 3.0905C3.27635 2.98153 3.26113 2.87753 3.25519 2.80492C3.25008 2.74226 3.2501 2.67113 3.25013 2.61158V1.38844C3.2501 1.32888 3.25008 1.25775 3.25519 1.19509C3.26113 1.12248 3.27635 1.01849 3.33187 0.909513C3.40378 0.768392 3.51851 0.653656 3.65964 0.581751C3.76861 0.526227 3.8726 0.511005 3.94521 0.505073C4.00787 0.499953 4.079 0.49998 4.13856 0.500003ZM9.11909 8.00004H2.88104L4.28066 6.83373C4.45657 6.68745 4.61158 6.55855 4.72675 6.39491C4.82784 6.25125 4.90292 6.09096 4.94856 5.92133C5.00054 5.7281 5.00033 5.52649 5.0001 5.29771L5.00006 3.50001H7.00006L7.00003 5.29771C6.99979 5.52649 6.99958 5.7281 7.05157 5.92133C7.09721 6.09096 7.17229 6.25125 7.27338 6.39491C7.38855 6.55855 7.54356 6.68745 7.71947 6.83373L9.11909 8.00004Z",
"fill": "currentColor"
},
"children": []
}
]
}
]
},
"name": "Beaker02"
}
\ No newline at end of file
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './Beaker02.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
export default Icon
export { default as Beaker02 } from './Beaker02'
......@@ -15,6 +15,7 @@ export type Attrs = {
export function normalizeAttrs(attrs: Attrs = {}): Attrs {
return Object.keys(attrs).reduce((acc: Attrs, key) => {
const val = attrs[key]
key = key.replace(/([-]\w)/g, (g: string) => g[1].toUpperCase())
switch (key) {
case 'class':
acc.className = val
......
......@@ -31,6 +31,7 @@ export type ISelectProps = {
allowSearch?: boolean
bgClassName?: string
placeholder?: string
overlayClassName?: string
}
const Select: FC<ISelectProps> = ({
className,
......@@ -40,6 +41,7 @@ const Select: FC<ISelectProps> = ({
onSelect,
allowSearch = true,
bgClassName = 'bg-gray-100',
overlayClassName,
}) => {
const [query, setQuery] = useState('')
const [open, setOpen] = useState(false)
......@@ -48,9 +50,9 @@ const Select: FC<ISelectProps> = ({
useEffect(() => {
let defaultSelect = null
const existed = items.find((item: Item) => item.value === defaultValue)
if (existed) {
if (existed)
defaultSelect = existed
}
setSelectedItem(defaultSelect)
}, [defaultValue])
......@@ -104,7 +106,7 @@ const Select: FC<ISelectProps> = ({
</div>
{filteredItems.length > 0 && (
<Combobox.Options className="absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm">
<Combobox.Options className={`absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm ${overlayClassName}`}>
{filteredItems.map((item: Item) => (
<Combobox.Option
key={item.value}
......@@ -155,9 +157,9 @@ const SimpleSelect: FC<ISelectProps> = ({
useEffect(() => {
let defaultSelect = null
const existed = items.find((item: Item) => item.value === defaultValue)
if (existed) {
if (existed)
defaultSelect = existed
}
setSelectedItem(defaultSelect)
}, [defaultValue])
......@@ -173,7 +175,7 @@ const SimpleSelect: FC<ISelectProps> = ({
>
<div className={`relative h-9 ${wrapperClassName}`}>
<Listbox.Button className={`w-full h-full rounded-lg border-0 bg-gray-100 py-1.5 pl-3 pr-10 shadow-sm sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover:bg-gray-200 cursor-pointer ${className}`}>
<span className={classNames("block truncate text-left", !selectedItem?.name && 'text-gray-400')}>{selectedItem?.name ?? localPlaceholder}</span>
<span className={classNames('block truncate text-left', !selectedItem?.name && 'text-gray-400')}>{selectedItem?.name ?? localPlaceholder}</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronDownIcon
className="h-5 w-5 text-gray-400"
......
'use client'
import React, { useEffect, useState } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import copy from 'copy-to-clipboard'
import { t } from 'i18next'
import s from './style.module.css'
import { randomString } from '@/app/components/app-sidebar/basic'
import Tooltip from '@/app/components/base/tooltip'
type IInputCopyProps = {
......@@ -13,13 +14,15 @@ type IInputCopyProps = {
}
const InputCopy = ({
value,
value = '',
className,
readOnly = true,
children,
}: IInputCopyProps) => {
const [isCopied, setIsCopied] = useState(false)
const selector = useRef(`input-tooltip-${randomString(4)}`)
useEffect(() => {
if (isCopied) {
const timeout = setTimeout(() => {
......@@ -38,7 +41,7 @@ const InputCopy = ({
{children}
<div className='flex-grow bg-gray-50 text-[13px] relative h-full'>
<Tooltip
selector="top-uniq"
selector={selector.current}
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10'
>
......@@ -50,7 +53,7 @@ const InputCopy = ({
</div>
<div className="flex-shrink-0 h-4 bg-gray-200 border" />
<Tooltip
selector="top-uniq"
selector={selector.current}
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10'
>
......
......@@ -18,7 +18,7 @@ const SecretKeyGenerateModal = ({
isShow = false,
onClose,
newKey,
className
className,
}: ISecretKeyGenerateModalProps) => {
const { t } = useTranslation()
return (
......
......@@ -6,6 +6,7 @@ import {
import { useTranslation } from 'react-i18next'
import { PlusIcon, XMarkIcon } from '@heroicons/react/20/solid'
import useSWR, { useSWRConfig } from 'swr'
import { useContext } from 'use-context-selector'
import SecretKeyGenerateModal from './secret-key-generate'
import s from './style.module.css'
import Modal from '@/app/components/base/modal'
......@@ -16,7 +17,6 @@ import Tooltip from '@/app/components/base/tooltip'
import Loading from '@/app/components/base/loading'
import Confirm from '@/app/components/base/confirm'
import useCopyToClipboard from '@/hooks/use-copy-to-clipboard'
import { useContext } from 'use-context-selector'
import I18n from '@/context/i18n'
type ISecretKeyModalProps = {
......@@ -58,12 +58,11 @@ const SecretKeyModal = ({
}
}, [copyValue])
const onDel = async () => {
setShowConfirmDelete(false)
if (!delKeyID) {
if (!delKeyID)
return
}
await delApikey({ url: `/apps/${appId}/api-keys/${delKeyID}`, params: {} })
mutate(commonParams)
}
......@@ -80,11 +79,10 @@ const SecretKeyModal = ({
}
const formatDate = (timestamp: any) => {
if (locale === 'en') {
if (locale === 'en')
return new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long', day: 'numeric' }).format((+timestamp) * 1000)
} else {
else
return new Intl.DateTimeFormat('fr-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }).format((+timestamp) * 1000)
}
}
return (
......@@ -111,7 +109,7 @@ const SecretKeyModal = ({
<div className='flex-shrink-0 px-3 truncate w-28'>{api.last_used_at ? formatDate(api.last_used_at) : t('appApi.never')}</div>
<div className='flex flex-grow px-3'>
<Tooltip
selector="top-uniq"
selector={`key-${api.token}`}
content={copyValue === api.token ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10'
>
......
'use client'
import classNames from 'classnames'
import { usePathname } from 'next/navigation'
import s from './index.module.css'
import { useAppContext } from '@/context/app-context'
type HeaderWrapperProps = {
children: React.ReactNode
}
const HeaderWrapper = ({
children,
}: HeaderWrapperProps) => {
const pathname = usePathname()
const { langeniusVersionInfo } = useAppContext()
const isBordered = ['/apps', '/datasets'].includes(pathname)
return (
<div className={classNames(
'sticky top-0 left-0 right-0 z-20 flex bg-gray-100 grow-0 shrink-0 basis-auto h-14',
s.header,
isBordered ? 'border-b border-gray-200' : '',
)}
>
<div className={classNames(
s[`header-${langeniusVersionInfo.current_env}`],
'flex flex-1 items-center justify-between px-4',
)}>
{children}
</div>
</div>
)
}
export default HeaderWrapper
.logo-icon {
background: url(../assets/logo-icon.png) center center no-repeat;
background-size: 32px;
box-shadow: 0px 4px 6px -1px rgba(0, 0, 0, 0.05), 0px 2px 4px -2px rgba(0, 0, 0, 0.05);
box-shadow: 0px 2px 4px -2px rgba(0, 0, 0, 0.05), 0px 4px 6px -1px rgba(0, 0, 0, 0.05);
}
.logo-text {
......
'use client'
import { useTranslation } from 'react-i18next'
import { XMarkIcon } from '@heroicons/react/24/outline'
import classNames from 'classnames'
import Link from 'next/link'
import s from './index.module.css'
import Modal from '@/app/components/base/modal'
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
import { Dify } from '@/app/components/base/icons/src/public/common'
import type { LangGeniusVersionResponse } from '@/models/common'
import { IS_CE_EDITION } from '@/config'
......@@ -30,16 +31,15 @@ export default function AccountAbout({
className={s.modal}
>
<div className='relative'>
<XMarkIcon className='absolute top-0 -right-2 w-4 h-4 cursor-pointer' onClick={onCancel} />
<div className='absolute -top-2 -right-4 flex justify-center items-center w-8 h-8 cursor-pointer' onClick={onCancel}>
<XClose className='w-4 h-4 text-gray-500' />
</div>
<div>
<div className={classNames(
s['logo-icon'],
'mx-auto mb-3 w-12 h-12 bg-white rounded-xl border border-gray-200',
)} />
<div className={classNames(
s['logo-text'],
'mx-auto mb-2',
'mx-auto mb-3 w-12 h-12 bg-white rounded-xl border-[0.5px] border-gray-200',
)} />
<Dify className='mx-auto mb-2' />
<div className='mb-3 text-center text-xs font-normal text-gray-500'>Version {langeniusVersionInfo?.current_version}</div>
<div className='mb-4 text-center text-xs font-normal text-gray-700'>
<div>© 2023 LangGenius, Inc., Contributors.</div>
......@@ -55,7 +55,7 @@ export default function AccountAbout({
</div>
</div>
</div>
<div className='mb-4 h-0 border-[0.5px] border-gray-200' />
<div className='mb-4 -mx-8 h-[0.5px] bg-gray-200' />
<div className='flex justify-between items-center'>
<div className='text-xs font-medium text-gray-800'>
{
......
import { Fragment } from 'react'
import { switchWorkspace } from '@/service/common'
import { Menu, Transition } from '@headlessui/react'
import { ChevronRightIcon, CheckIcon } from '@heroicons/react/24/outline'
import cn from 'classnames'
import s from './index.module.css'
import { useContext } from 'use-context-selector'
import { ToastContext } from '@/app/components/base/toast'
import { useTranslation } from 'react-i18next'
import { useRouter } from 'next/navigation'
import { Menu, Transition } from '@headlessui/react'
import cn from 'classnames'
import s from './index.module.css'
import { switchWorkspace } from '@/service/common'
import { useWorkspacesContext } from '@/context/workspace-context'
import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
import { Check } from '@/app/components/base/icons/src/vender/line/general'
import { ToastContext } from '@/app/components/base/toast'
const itemClassName = `
flex items-center px-3 py-2 h-10 cursor-pointer
`
const itemIconClassName = `
shrink-0 mr-2 w-6 h-6 bg-[#EFF4FF] rounded-md
shrink-0 mr-2 flex items-center justify-center w-6 h-6 bg-[#EFF4FF] rounded-md text-xs font-medium text-primary-600
`
const itemNameClassName = `
grow mr-2 text-sm text-gray-700 text-left
......@@ -32,12 +33,12 @@ const WorkplaceSelector = () => {
const handleSwitchWorkspace = async (tenant_id: string) => {
try {
await switchWorkspace({ url: `/workspaces/switch`, body: { tenant_id } })
await switchWorkspace({ url: '/workspaces/switch', body: { tenant_id } })
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
router.replace('/apps')
} catch (e) {
}
catch (e) {
notify({ type: 'error', message: t('common.provider.saveFailed') })
} finally {
}
}
......@@ -49,12 +50,12 @@ const WorkplaceSelector = () => {
<Menu.Button className={cn(
`
${itemClassName} w-full
group hover:bg-gray-50 cursor-pointer ${open && 'bg-gray-50'}
`
group hover:bg-gray-50 cursor-pointer ${open && 'bg-gray-50'} rounded-lg
`,
)}>
<div className={itemIconClassName} />
<div className={itemIconClassName}>{currentWrokspace?.name[0].toLocaleUpperCase()}</div>
<div className={`${itemNameClassName} truncate`}>{currentWrokspace?.name}</div>
<ChevronRightIcon className='shrink-0 w-[14px] h-[14px]' />
<ChevronRight className='shrink-0 w-[14px] h-[14px] text-gray-500' />
</Menu.Button>
<Transition
as={Fragment}
......@@ -71,16 +72,16 @@ const WorkplaceSelector = () => {
absolute top-[1px] min-w-[200px] z-10 bg-white border-[0.5px] border-gray-200
divide-y divide-gray-100 origin-top-right rounded-xl
`,
s.popup
s.popup,
)}
>
<div className="px-1 py-1">
{
workspaces.map(workspace => (
<div className={itemClassName} key={workspace.id} onClick={() => handleSwitchWorkspace(workspace.id)}>
<div className={itemIconClassName} />
<div className={itemIconClassName}>{workspace.name[0].toLocaleUpperCase()}</div>
<div className={itemNameClassName}>{workspace.name}</div>
{workspace.current && <CheckIcon className={itemCheckClassName} />}
{workspace.current && <Check className={itemCheckClassName} />}
</div>
))
}
......@@ -94,4 +95,4 @@ const WorkplaceSelector = () => {
)
}
export default WorkplaceSelector
\ No newline at end of file
export default WorkplaceSelector
'use client'
import React, { useCallback, useEffect, useState } from 'react'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { t } from 'i18next'
import s from './index.module.css'
import Tooltip from '@/app/components/base/tooltip'
import useCopyToClipboard from '@/hooks/use-copy-to-clipboard'
import { randomString } from '@/app/components/app-sidebar/basic'
type IInvitationLinkProps = {
value?: string
......@@ -13,6 +14,7 @@ const InvitationLink = ({
value = '',
}: IInvitationLinkProps) => {
const [isCopied, setIsCopied] = useState(false)
const selector = useRef(`invite-link-${randomString(4)}`)
const [_, copy] = useCopyToClipboard()
const copyHandle = useCallback(() => {
......@@ -37,7 +39,7 @@ const InvitationLink = ({
<div className="flex items-center flex-grow h-5">
<div className='flex-grow bg-gray-100 text-[13px] relative h-full'>
<Tooltip
selector="top-uniq"
selector={selector.current}
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10'
>
......@@ -46,7 +48,7 @@ const InvitationLink = ({
</div>
<div className="flex-shrink-0 h-4 bg-gray-200 border" />
<Tooltip
selector="top-uniq"
selector={selector.current}
content={isCopied ? `${t('appApi.copied')}` : `${t('appApi.copy')}`}
className='z-10'
>
......
'use client'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useParams, usePathname } from 'next/navigation'
......
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.13856 0.500003H7.8617C7.92126 0.49998 7.99238 0.499953 8.05504 0.505073C8.12765 0.511005 8.23165 0.526227 8.34062 0.581751C8.48174 0.653656 8.59648 0.768392 8.66838 0.909513C8.72391 1.01849 8.73913 1.12248 8.74506 1.19509C8.75018 1.25775 8.75015 1.32888 8.75013 1.38844V2.61157C8.75015 2.67113 8.75018 2.74226 8.74506 2.80492C8.73913 2.87753 8.72391 2.98153 8.66838 3.0905C8.59648 3.23162 8.48174 3.34636 8.34062 3.41826C8.23165 3.47379 8.12765 3.48901 8.05504 3.49494C8.03725 3.49639 8.01877 3.49743 8.00006 3.49817V5.2506C8.00006 5.55312 8.00408 5.61265 8.01723 5.66153C8.03245 5.71807 8.05747 5.7715 8.09117 5.81939C8.1203 5.86078 8.16346 5.90197 8.39586 6.09564L10.2807 7.66627C10.4566 7.81255 10.6116 7.94145 10.7267 8.10509C10.8278 8.24875 10.9029 8.40904 10.9486 8.57867C11.0005 8.7719 11.0003 8.97351 11.0001 9.2023C11.0001 9.39886 11.0002 9.59542 11.0002 9.79198C11.0003 9.98232 11.0005 10.1463 10.9713 10.2927C10.853 10.8877 10.3878 11.3529 9.7928 11.4712C9.64637 11.5003 9.48246 11.5002 9.29211 11.5001H2.70822C2.51787 11.5002 2.35396 11.5003 2.20753 11.4712C1.98473 11.4269 1.78014 11.334 1.60515 11.2038C1.42854 11.0725 1.28221 10.9034 1.17753 10.7077C1.10892 10.5796 1.05831 10.4401 1.02899 10.2927C0.999862 10.1463 0.999992 9.98233 1.00014 9.79199C1.00014 9.59542 1.00006 9.39886 1.00003 9.20229C0.999794 8.97351 0.999584 8.7719 1.05157 8.57867C1.09721 8.40904 1.17229 8.24875 1.27338 8.10509C1.38855 7.94145 1.54356 7.81255 1.71947 7.66627L3.60427 6.09564C3.83667 5.90197 3.87983 5.86078 3.90896 5.81939C3.94266 5.7715 3.96768 5.71807 3.9829 5.66153C3.99605 5.61265 4.00006 5.55312 4.00006 5.2506V3.49817C3.9814 3.49743 3.96297 3.49639 3.94521 3.49494C3.8726 3.48901 3.76861 3.47379 3.65964 3.41826C3.51851 3.34636 3.40378 3.23162 3.33187 3.0905C3.27635 2.98153 3.26113 2.87753 3.25519 2.80492C3.25008 2.74226 3.2501 2.67113 3.25013 2.61158V1.38844C3.2501 1.32888 3.25008 1.25775 3.25519 1.19509C3.26113 1.12248 3.27635 1.01849 3.33187 0.909513C3.40378 0.768392 3.51851 0.653656 3.65964 0.581751C3.76861 0.526227 3.8726 0.511005 3.94521 0.505073C4.00787 0.499953 4.079 0.49998 4.13856 0.500003ZM9.11909 8.00004H2.88104L4.28066 6.83373C4.45657 6.68745 4.61158 6.55855 4.72675 6.39491C4.82784 6.25125 4.90292 6.09096 4.94856 5.92133C5.00054 5.7281 5.00033 5.52649 5.0001 5.29771L5.00006 3.50001H7.00006L7.00003 5.29771C6.99979 5.52649 6.99958 5.7281 7.05157 5.92133C7.09721 6.09096 7.17229 6.25125 7.27338 6.39491C7.38855 6.55855 7.54356 6.68745 7.71947 6.83373L9.11909 8.00004Z" fill="#0E7090"/>
</svg>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 1.125C4.64906 1.125 1.125 4.64906 1.125 9C1.125 12.4847 3.37922 15.428 6.50953 16.4714C6.90328 16.5403 7.05094 16.3041 7.05094 16.0973C7.05094 15.9103 7.04109 15.2902 7.04109 14.6306C5.0625 14.9948 4.55062 14.1483 4.39312 13.7053C4.30453 13.4789 3.92062 12.78 3.58594 12.593C3.31031 12.4453 2.91656 12.0811 3.57609 12.0712C4.19625 12.0614 4.63922 12.6422 4.78688 12.8784C5.49563 14.0695 6.62766 13.7348 7.08047 13.5281C7.14937 13.0163 7.35609 12.6717 7.5825 12.4748C5.83031 12.278 3.99938 11.5988 3.99938 8.58656C3.99938 7.73016 4.30453 7.02141 4.80656 6.47016C4.72781 6.27328 4.45219 5.46609 4.88531 4.38328C4.88531 4.38328 5.54484 4.17656 7.05094 5.19047C7.68094 5.01328 8.35031 4.92469 9.01969 4.92469C9.68906 4.92469 10.3584 5.01328 10.9884 5.19047C12.4945 4.16672 13.1541 4.38328 13.1541 4.38328C13.5872 5.46609 13.3116 6.27328 13.2328 6.47016C13.7348 7.02141 14.04 7.72031 14.04 8.58656C14.04 11.6086 12.1992 12.278 10.447 12.4748C10.7325 12.7209 10.9786 13.1934 10.9786 13.9317C10.9786 14.985 10.9688 15.8316 10.9688 16.0973C10.9688 16.3041 11.1164 16.5502 11.5102 16.4714C13.0735 15.9436 14.432 14.9389 15.3943 13.5986C16.3567 12.2583 16.8746 10.65 16.875 9C16.875 4.64906 13.3509 1.125 9 1.125Z" fill="#24292F"/>
</svg>
'use client'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useParams, useRouter } from 'next/navigation'
......
'use client'
import { useTranslation } from 'react-i18next'
import { useAppContext } from '@/context/app-context'
import { Beaker02 } from '@/app/components/base/icons/src/vender/solid/education'
import { TerminalSquare } from '@/app/components/base/icons/src/vender/solid/development'
const headerEnvClassName: { [k: string]: string } = {
DEVELOPMENT: 'bg-[#FEC84B] border-[#FDB022] text-[#93370D]',
TESTING: 'bg-[#A5F0FC] border-[#67E3F9] text-[#164C63]',
}
const EnvNav = () => {
const { t } = useTranslation()
const { langeniusVersionInfo } = useAppContext()
const showEnvTag = langeniusVersionInfo.current_env === 'TESTING' || langeniusVersionInfo.current_env === 'DEVELOPMENT'
if (!showEnvTag)
return null
return (
<div className={`
flex items-center h-[22px] mr-4 rounded-md px-2 text-xs font-medium border
${headerEnvClassName[langeniusVersionInfo.current_env]}
`}>
{
langeniusVersionInfo.current_env === 'TESTING' && (
<>
<Beaker02 className='w-3 h-3 mr-1' />
{t('common.environment.testing')}
</>
)
}
{
langeniusVersionInfo.current_env === 'DEVELOPMENT' && (
<>
<TerminalSquare className='w-3 h-3 mr-1' />
{t('common.environment.development')}
</>
)
}
</div>
)
}
export default EnvNav
'use client'
import { useTranslation } from 'react-i18next'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
import classNames from 'classnames'
import { Grid01 } from '@/app/components/base/icons/src/vender/line/layout'
import { Grid01 as Grid01Solid } from '@/app/components/base/icons/src/vender/solid/layout'
type ExploreNavProps = {
className?: string
}
const ExploreNav = ({
className,
}: ExploreNavProps) => {
const { t } = useTranslation()
const selectedSegment = useSelectedLayoutSegment()
const actived = selectedSegment === 'explore'
return (
<Link href="/explore/apps" className={classNames(
className, 'group',
actived && 'bg-white shadow-[0_2px_5px_-1px_rgba(0,0,0,0.05),0_2px_4px_-2px_rgba(0,0,0,0.05)]',
actived ? 'text-primary-600' : 'text-gray-500 hover:bg-gray-200',
)}>
{
actived
? <Grid01Solid className='mr-2 w-4 h-4' />
: <Grid01 className='mr-2 w-4 h-4' />
}
{t('common.menus.explore')}
</Link>
)
}
export default ExploreNav
import { Github } from '@/app/components/base/icons/src/public/common'
import type { GithubRepo } from '@/models/common'
const getStar = async () => {
const res = await fetch('https://api.github.com/repos/langgenius/dify')
if (!res.ok)
throw new Error('Failed to fetch data')
return res.json()
}
const GithubStar = async () => {
let githubRepo: GithubRepo = { stargazers_count: 0 }
if (process.env.NODE_ENV === 'development')
return null
try {
githubRepo = await getStar()
}
catch (e) {
return null
}
return (
<a
href='https://github.com/langgenius/dify'
target='_blank'
className='flex items-center leading-[18px] border border-gray-200 rounded-md text-xs text-gray-700 font-semibold overflow-hidden'>
<div className='flex items-center px-2 py-1 bg-gray-100'>
<Github className='mr-1 w-[18px] h-[18px]' />
Star
</div>
<div className='px-2 py-1 bg-white border-l border-gray-200'>{`${githubRepo.stargazers_count}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}</div>
</a>
)
}
export default GithubStar
......@@ -15,23 +15,8 @@
background-size: contain;
}
.github-icon {
width: 18px;
height: 18px;
background: url(./assets/github-icon.svg) center center no-repeat;
background-size: contain;
}
.alpha {
width: 12px;
height: 12px;
background: url(./assets/alpha.svg) center center no-repeat;
}
.beaker-icon {
width: 12px;
height: 12px;
margin-right: 4px;
background: url(./assets/beaker.svg) center center no-repeat;
background-size: contain;
}
\ No newline at end of file
'use client'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { usePathname, useSelectedLayoutSegment } from 'next/navigation'
import classNames from 'classnames'
import { CommandLineIcon } from '@heroicons/react/24/solid'
import Link from 'next/link'
import AccountDropdown from './account-dropdown'
import AppNav from './app-nav'
import DatasetNav from './dataset-nav'
import EnvNav from './env-nav'
import ExploreNav from './explore-nav'
import GithubStar from './github-star'
import PluginNav from './plugin-nav'
import s from './index.module.css'
import type { GithubRepo } from '@/models/common'
import { WorkspaceProvider } from '@/context/workspace-context'
import { useAppContext } from '@/context/app-context'
import { Grid01 } from '@/app/components/base/icons/src/vender/line/layout'
import { Grid01 as Grid01Solid } from '@/app/components/base/icons/src/vender/solid/layout'
import { PuzzlePiece01 } from '@/app/components/base/icons/src/vender/line/development'
import { PuzzlePiece01 as PuzzlePiece01Solid } from '@/app/components/base/icons/src/vender/solid/development'
const navClassName = `
flex items-center relative mr-3 px-3 h-8 rounded-xl
font-medium text-sm
cursor-pointer
`
const headerEnvClassName: { [k: string]: string } = {
DEVELOPMENT: 'bg-[#FEC84B] border-[#FDB022] text-[#93370D]',
TESTING: 'bg-[#A5F0FC] border-[#67E3F9] text-[#164C63]',
}
const Header = () => {
const { t } = useTranslation()
const pathname = usePathname()
const { userProfile, langeniusVersionInfo } = useAppContext()
const showEnvTag = langeniusVersionInfo.current_env === 'TESTING' || langeniusVersionInfo.current_env === 'DEVELOPMENT'
const selectedSegment = useSelectedLayoutSegment()
const isPluginsComingSoon = selectedSegment === 'plugins-coming-soon'
const isExplore = selectedSegment === 'explore'
const [starCount, setStarCount] = useState(0)
const isBordered = ['/apps', '/datasets'].includes(pathname)
useEffect(() => {
globalThis.fetch('https://api.github.com/repos/langgenius/dify').then(res => res.json()).then((data: GithubRepo) => {
setStarCount(data.stargazers_count)
})
}, [])
const Header = () => {
return (
<div className={classNames(
'sticky top-0 left-0 right-0 z-20 flex bg-gray-100 grow-0 shrink-0 basis-auto h-14',
s.header,
isBordered ? 'border-b border-gray-200' : '',
)}
>
<div className={classNames(
s[`header-${langeniusVersionInfo.current_env}`],
'flex flex-1 items-center justify-between px-4',
)}>
<div className='flex items-center'>
<Link href="/apps" className='flex items-center mr-4'>
<div className={s.logo} />
</Link>
{
starCount > 0 && (
<Link
href='https://github.com/langgenius/dify'
target='_blank'
className='flex items-center leading-[18px] border border-gray-200 rounded-md text-xs text-gray-700 font-semibold overflow-hidden'>
<div className='flex items-center px-2 py-1 bg-gray-100'>
<div className={`${s['github-icon']} mr-1 rounded-full`} />
Star
</div>
<div className='px-2 py-1 bg-white border-l border-gray-200'>{`${starCount}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}</div>
</Link>
)
}
</div>
<div className='flex items-center'>
<Link href="/explore/apps" className={classNames(
navClassName, 'group',
isExplore && 'bg-white shadow-[0_2px_5px_-1px_rgba(0,0,0,0.05),0_2px_4px_-2px_rgba(0,0,0,0.05)]',
isExplore ? 'text-primary-600' : 'text-gray-500 hover:bg-gray-200',
)}>
{
isExplore
? <Grid01Solid className='mr-2 w-4 h-4' />
: <Grid01 className='mr-2 w-4 h-4' />
}
{t('common.menus.explore')}
</Link>
<AppNav />
<Link href="/plugins-coming-soon" className={classNames(
navClassName, 'group',
isPluginsComingSoon && 'bg-white shadow-[0_2px_5px_-1px_rgba(0,0,0,0.05),0_2px_4px_-2px_rgba(0,0,0,0.05)]',
isPluginsComingSoon ? 'text-primary-600' : 'text-gray-500 hover:bg-gray-200',
)}>
{
isPluginsComingSoon
? <PuzzlePiece01Solid className='mr-2 w-4 h-4' />
: <PuzzlePiece01 className='mr-2 w-4 h-4' />
}
{t('common.menus.plugins')}
</Link>
<DatasetNav />
</div>
<div className='flex items-center flex-shrink-0'>
{
showEnvTag && (
<div className={`
flex items-center h-[22px] mr-4 rounded-md px-2 text-xs font-medium border
${headerEnvClassName[langeniusVersionInfo.current_env]}
`}>
{
langeniusVersionInfo.current_env === 'TESTING' && (
<>
<div className={s['beaker-icon']} />
{t('common.environment.testing')}
</>
)
}
{
langeniusVersionInfo.current_env === 'DEVELOPMENT' && (
<>
<CommandLineIcon className='w-3 h-3 mr-1' />
{t('common.environment.development')}
</>
)
}
</div>
)
}
<WorkspaceProvider>
<AccountDropdown userProfile={userProfile} langeniusVersionInfo={langeniusVersionInfo} />
</WorkspaceProvider>
</div>
<>
<div className='flex items-center'>
<Link href="/apps" className='flex items-center mr-4'>
<div className={s.logo} />
</Link>
{/* @ts-expect-error Async Server Component */}
<GithubStar />
</div>
<div className='flex items-center'>
<ExploreNav className={navClassName} />
<AppNav />
<PluginNav className={navClassName} />
<DatasetNav />
</div>
<div className='flex items-center flex-shrink-0'>
<EnvNav />
<WorkspaceProvider>
<AccountDropdown />
</WorkspaceProvider>
</div>
</div>
</>
)
}
export default Header
'use client'
import { useTranslation } from 'react-i18next'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
import classNames from 'classnames'
import { PuzzlePiece01 } from '@/app/components/base/icons/src/vender/line/development'
import { PuzzlePiece01 as PuzzlePiece01Solid } from '@/app/components/base/icons/src/vender/solid/development'
type PluginNavProps = {
className?: string
}
const PluginNav = ({
className,
}: PluginNavProps) => {
const { t } = useTranslation()
const selectedSegment = useSelectedLayoutSegment()
const isPluginsComingSoon = selectedSegment === 'plugins-coming-soon'
return (
<Link href="/plugins-coming-soon" className={classNames(
className, 'group',
isPluginsComingSoon && 'bg-white shadow-[0_2px_5px_-1px_rgba(0,0,0,0.05),0_2px_4px_-2px_rgba(0,0,0,0.05)]',
isPluginsComingSoon ? 'text-primary-600' : 'text-gray-500 hover:bg-gray-200',
)}>
{
isPluginsComingSoon
? <PuzzlePiece01Solid className='mr-2 w-4 h-4' />
: <PuzzlePiece01 className='mr-2 w-4 h-4' />
}
{t('common.menus.plugins')}
</Link>
)
}
export default PluginNav
......@@ -395,21 +395,21 @@ const Main: FC<IMainProps> = ({
if (!inputs || !prompt_variables || prompt_variables?.length === 0)
return true
let hasEmptyInput = false
let hasEmptyInput = ''
const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
return res
}) || [] // compatible with old version
requiredVars.forEach(({ key }) => {
requiredVars.forEach(({ key, name }) => {
if (hasEmptyInput)
return
if (!inputs?.[key])
hasEmptyInput = true
hasEmptyInput = name
})
if (hasEmptyInput) {
logError(t('appDebug.errorMessage.valueOfVarRequired'))
logError(t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }))
return false
}
return !hasEmptyInput
......
......@@ -48,7 +48,10 @@ const List: FC<IListProps> = ({
useInfiniteScroll(
async () => {
if (!isNoMore) {
const lastId = !isClearConversationList ? list[list.length - 1]?.id : undefined
let lastId = !isClearConversationList ? list[list.length - 1]?.id : undefined
if (lastId === '-1')
lastId = undefined
const { data: conversations, has_more }: any = await fetchConversations(isInstalledApp, installedAppId, lastId, isPinned)
onMoreLoaded({ data: conversations, has_more })
}
......
'use client'
import type { FC } from 'react'
import React, { useState, useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import TemplateVarPanel, { PanelTitle, VarOpBtnGroup } from '../value-panel'
import s from './style.module.css'
import { AppInfo, ChatBtn, EditBtn, FootLogo, PromptTemplate } from './massive-component'
import type { SiteInfo } from '@/models/share'
import type { PromptConfig } from '@/models/debug'
import { ToastContext } from '@/app/components/base/toast'
import Select from '@/app/components/base/select'
import { DEFAULT_VALUE_MAX_LEN } from '@/config'
import TemplateVarPanel, { PanelTitle, VarOpBtnGroup } from '../value-panel'
import { AppInfo, PromptTemplate, ChatBtn, EditBtn, FootLogo } from './massive-component'
// regex to match the {{}} and replace it with a span
const regex = /\{\{([^}]+)\}\}/g
......@@ -38,15 +38,15 @@ const Welcome: FC<IWelcomeProps> = ({
onStartChat,
canEidtInpus,
savedInputs,
onInputsChange
onInputsChange,
}) => {
const { t } = useTranslation()
const hasVar = promptConfig.prompt_variables.length > 0
const [isFold, setIsFold] = useState<boolean>(true)
const [inputs, setInputs] = useState<Record<string, any>>((() => {
if (hasSetInputs) {
if (hasSetInputs)
return savedInputs
}
const res: Record<string, any> = {}
if (promptConfig) {
promptConfig.prompt_variables.forEach((item) => {
......@@ -65,7 +65,8 @@ const Welcome: FC<IWelcomeProps> = ({
})
}
setInputs(res)
} else {
}
else {
setInputs(savedInputs)
}
}, [savedInputs])
......@@ -98,24 +99,26 @@ const Welcome: FC<IWelcomeProps> = ({
{promptConfig.prompt_variables.map(item => (
<div className='tablet:flex tablet:!h-9 mobile:space-y-2 tablet:space-y-0 mobile:text-xs tablet:text-sm' key={item.key}>
<label className={`flex-shrink-0 flex items-center mobile:text-gray-700 tablet:text-gray-900 mobile:font-medium pc:font-normal ${s.formLabel}`}>{item.name}</label>
{item.type === 'select' ? (
<Select
className='w-full'
defaultValue={inputs?.[item.key]}
onSelect={(i) => { setInputs({ ...inputs, [item.key]: i.value }) }}
items={(item.options || []).map(i => ({ name: i, value: i }))}
allowSearch={false}
bgClassName='bg-gray-50'
/>
) : (
<input
placeholder={`${item.name}${!item.required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
value={inputs?.[item.key] || ''}
onChange={(e) => { setInputs({ ...inputs, [item.key]: e.target.value }) }}
className={`w-full flex-grow py-2 pl-3 pr-3 box-border rounded-lg bg-gray-50`}
maxLength={item.max_length || DEFAULT_VALUE_MAX_LEN}
/>
)}
{item.type === 'select'
? (
<Select
className='w-full'
defaultValue={inputs?.[item.key]}
onSelect={(i) => { setInputs({ ...inputs, [item.key]: i.value }) }}
items={(item.options || []).map(i => ({ name: i, value: i }))}
allowSearch={false}
bgClassName='bg-gray-50'
/>
)
: (
<input
placeholder={`${item.name}${!item.required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
value={inputs?.[item.key] || ''}
onChange={(e) => { setInputs({ ...inputs, [item.key]: e.target.value }) }}
className={'w-full flex-grow py-2 pl-3 pr-3 box-border rounded-lg bg-gray-50'}
maxLength={item.max_length || DEFAULT_VALUE_MAX_LEN}
/>
)}
</div>
))}
</div>
......@@ -124,34 +127,33 @@ const Welcome: FC<IWelcomeProps> = ({
const canChat = () => {
const prompt_variables = promptConfig?.prompt_variables
if (!inputs || !prompt_variables || prompt_variables?.length === 0) {
if (!inputs || !prompt_variables || prompt_variables?.length === 0)
return true
}
let hasEmptyInput = false
let hasEmptyInput = ''
const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
return res
}) || [] // compatible with old version
requiredVars.forEach(({ key }) => {
if (hasEmptyInput) {
requiredVars.forEach(({ key, name }) => {
if (hasEmptyInput)
return
}
if (!inputs?.[key]) {
hasEmptyInput = true
}
if (!inputs?.[key])
hasEmptyInput = name
})
if (hasEmptyInput) {
logError(t('appDebug.errorMessage.valueOfVarRequired'))
logError(t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }))
return false
}
return !hasEmptyInput
}
const handleChat = () => {
if (!canChat()) {
if (!canChat())
return
}
onStartChat(inputs)
}
......@@ -211,9 +213,9 @@ const Welcome: FC<IWelcomeProps> = ({
return (
<VarOpBtnGroup
onConfirm={() => {
if (!canChat()) {
if (!canChat())
return
}
onInputsChange(inputs)
setIsFold(true)
}}
......@@ -269,9 +271,9 @@ const Welcome: FC<IWelcomeProps> = ({
}
const renderHasSetInputsPrivate = () => {
if (!canEidtInpus || !hasVar) {
if (!canEidtInpus || !hasVar)
return null
}
return (
<TemplateVarPanel
isFold={isFold}
......@@ -293,9 +295,9 @@ const Welcome: FC<IWelcomeProps> = ({
}
const renderHasSetInputs = () => {
if (!isPublicVersion && !canEidtInpus || !hasVar) {
if ((!isPublicVersion && !canEidtInpus) || !hasVar)
return null
}
return (
<div
className='pt-[88px] mb-5'
......@@ -312,11 +314,13 @@ const Welcome: FC<IWelcomeProps> = ({
{
!hasSetInputs && (
<div className='mobile:pt-[72px] tablet:pt-[128px] pc:pt-[200px]'>
{hasVar ? (
renderVarPanel()
) : (
renderNoVarPanel()
)}
{hasVar
? (
renderVarPanel()
)
: (
renderNoVarPanel()
)}
</div>
)
}
......
......@@ -390,21 +390,21 @@ const Main: FC<IMainProps> = ({
if (!inputs || !prompt_variables || prompt_variables?.length === 0)
return true
let hasEmptyInput = false
let hasEmptyInput = ''
const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
return res
}) || [] // compatible with old version
requiredVars.forEach(({ key }) => {
requiredVars.forEach(({ key, name }) => {
if (hasEmptyInput)
return
if (!inputs?.[key])
hasEmptyInput = true
hasEmptyInput = name
})
if (hasEmptyInput) {
logError(t('appDebug.errorMessage.valueOfVarRequired'))
logError(t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }))
return false
}
return !hasEmptyInput
......
......@@ -130,21 +130,21 @@ const Welcome: FC<IWelcomeProps> = ({
if (!inputs || !prompt_variables || prompt_variables?.length === 0)
return true
let hasEmptyInput = false
let hasEmptyInput = ''
const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
return res
}) || [] // compatible with old version
requiredVars.forEach(({ key }) => {
requiredVars.forEach(({ key, name }) => {
if (hasEmptyInput)
return
if (!inputs?.[key])
hasEmptyInput = true
hasEmptyInput = name
})
if (hasEmptyInput) {
logError(t('appDebug.errorMessage.valueOfVarRequired'))
logError(t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }))
return false
}
return !hasEmptyInput
......
import type { FC } from 'react'
import React from 'react'
import {
Bars3Icon,
PencilSquareIcon,
} from '@heroicons/react/24/solid'
import AppIcon from '@/app/components/base/app-icon'
export type IHeaderProps = {
title: string
customerIcon?: React.ReactNode
......@@ -8,6 +13,8 @@ export type IHeaderProps = {
icon_background: string
isMobile?: boolean
isEmbedScene?: boolean
onShowSideBar?: () => void
onCreateNewChat?: () => void
}
const Header: FC<IHeaderProps> = ({
title,
......@@ -16,22 +23,25 @@ const Header: FC<IHeaderProps> = ({
icon,
icon_background,
isEmbedScene = false,
onShowSideBar,
onCreateNewChat,
}) => {
return !isMobile
? null
: (
if (!isMobile)
return null
if (isEmbedScene) {
return (
<div
className={`shrink-0 flex items-center justify-between h-12 px-3 bg-gray-100 ${
isEmbedScene ? 'bg-gradient-to-r from-blue-600 to-sky-500' : ''
}`}
className={`
shrink-0 flex items-center justify-between h-12 px-3 bg-gray-100
bg-gradient-to-r from-blue-600 to-sky-500
`}
>
<div></div>
<div className="flex items-center space-x-2">
{customerIcon || <AppIcon size="small" icon={icon} background={icon_background} />}
<div
className={`text-sm text-gray-800 font-bold ${
isEmbedScene ? 'text-white' : ''
}`}
className={'text-sm font-bold text-white'}
>
{title}
</div>
......@@ -39,6 +49,27 @@ const Header: FC<IHeaderProps> = ({
<div></div>
</div>
)
}
return (
<div className="shrink-0 flex items-center justify-between h-12 px-3 bg-gray-100">
<div
className='flex items-center justify-center h-8 w-8 cursor-pointer'
onClick={() => onShowSideBar?.()}
>
<Bars3Icon className="h-4 w-4 text-gray-500" />
</div>
<div className='flex items-center space-x-2'>
<AppIcon size="small" icon={icon} background={icon_background} />
<div className=" text-sm text-gray-800 font-bold">{title}</div>
</div>
<div className='flex items-center justify-center h-8 w-8 cursor-pointer'
onClick={() => onCreateNewChat?.()}
>
<PencilSquareIcon className="h-4 w-4 text-gray-500" />
</div>
</div>
)
}
export default React.memo(Header)
......@@ -80,21 +80,21 @@ const Result: FC<IResultProps> = ({
if (!prompt_variables || prompt_variables?.length === 0)
return true
let hasEmptyInput = false
let hasEmptyInput = ''
const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
return res
}) || [] // compatible with old version
requiredVars.forEach(({ key }) => {
requiredVars.forEach(({ key, name }) => {
if (hasEmptyInput)
return
if (!inputs[key])
hasEmptyInput = true
hasEmptyInput = name
})
if (hasEmptyInput) {
logError(t('appDebug.errorMessage.valueOfVarRequired'))
logError(t('appDebug.errorMessage.valueOfVarRequired', { key: hasEmptyInput }))
return false
}
return !hasEmptyInput
......
......@@ -17,6 +17,7 @@ const InstallForm = () => {
const [email, setEmail] = React.useState('')
const [name, setName] = React.useState('')
const [password, setPassword] = React.useState('')
const [showPassword, setShowPassword] = React.useState(false)
const showErrorMessage = (message: string) => {
Toast.notify({
type: 'error',
......@@ -108,12 +109,21 @@ const InstallForm = () => {
<div className="mt-1 relative rounded-md shadow-sm">
<input
id="password"
type='password'
type={showPassword ? 'text' : 'password'}
value={password}
onChange={e => setPassword(e.target.value)}
placeholder={t('login.passwordPlaceholder') || ''}
className={'appearance-none block w-full rounded-lg pl-[14px] px-3 py-2 border border-gray-200 hover:border-gray-300 hover:shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 placeholder-gray-400 caret-primary-600 sm:text-sm pr-10'}
/>
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="text-gray-400 hover:text-gray-500 focus:outline-none focus:text-gray-500"
>
{showPassword ? '👀' : '😝'}
</button>
</div>
</div>
<div className='mt-1 text-xs text-gray-500'>{t('login.error.passwordInvalid')}</div>
......
......@@ -67,7 +67,7 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
const result = await userProfileResponse.json()
setUserProfile(result)
const current_version = userProfileResponse.headers.get('x-version')
const current_env = userProfileResponse.headers.get('x-env')
const current_env = process.env.NODE_ENV === 'development' ? 'DEVELOPMENT' : userProfileResponse.headers.get('x-env')
const versionData = await fetchLanggeniusVersion({ url: '/version', params: { current_version } })
setLangeniusVersionInfo({ ...versionData, current_version, latest_version: versionData.version, current_env })
}
......
import { useState } from 'react'
import { useCallback, useState } from 'react'
import writeText from 'copy-to-clipboard'
type CopiedValue = string | null
type CopyFn = (text: string) => Promise<boolean>
function useCopyToClipboard(): [CopiedValue, CopyFn] {
const [copiedText, setCopiedText] = useState<CopiedValue>(null)
const [copiedText, setCopiedText] = useState<CopiedValue>(null)
const copy: CopyFn = async text => {
if (!navigator?.clipboard) {
console.warn('Clipboard not supported')
return false
}
const copy: CopyFn = useCallback(async (text: string) => {
if (!navigator?.clipboard) {
console.warn('Clipboard not supported')
return false
}
try {
await navigator.clipboard.writeText(text)
setCopiedText(text)
return true
} catch (error) {
console.warn('Copy failed', error)
setCopiedText(null)
return false
}
try {
writeText(text)
setCopiedText(text)
return true
}
catch (error) {
console.warn('Copy failed', error)
setCopiedText(null)
return false
}
}, [])
return [copiedText, copy]
return [copiedText, copy]
}
export default useCopyToClipboard
\ No newline at end of file
export default useCopyToClipboard
......@@ -87,7 +87,7 @@ const translation = {
},
errorMessage: {
nameOfKeyRequired: 'name of the key: {{key}} required',
valueOfVarRequired: 'Variables value can not be empty',
valueOfVarRequired: '{{key}} value can not be empty',
queryRequired: 'Request text is required.',
waitForResponse:
'Please wait for the response to the previous message to complete.',
......
......@@ -86,7 +86,7 @@ const translation = {
},
errorMessage: {
nameOfKeyRequired: '变量 {{key}} 对应的名称必填',
valueOfVarRequired: '变量值必填',
valueOfVarRequired: '{{key}}必填',
queryRequired: '主要文本必填',
waitForResponse: '请等待上条信息响应完成',
waitForBatchResponse: '请等待批量任务完成',
......
......@@ -43,7 +43,7 @@ const translation = {
'较高的 Temperature 设置将导致更多样和创造性的输出,而较低的 Temperature 将产生更保守的输出并且类似于训练数据。',
topP: '采样范围',
topPTip:
'Top P值越高,输出与训练文本越相似,Top P值越低,输出越有创意和变化。它可用于使输出更适合特定用例。',
'Top P值越低,输出与训练文本越相似,Top P值越高,输出越有创意和变化。它可用于使输出更适合特定用例。',
presencePenalty: '词汇控制',
presencePenaltyTip:
'Presence penalty 是根据新词是否出现在目前的文本中来对其进行惩罚。正值将降低模型谈论新话题的可能性。',
......
......@@ -54,7 +54,7 @@
"katex": "^0.16.7",
"lodash-es": "^4.17.21",
"negotiator": "^0.6.3",
"next": "^13.4.7",
"next": "13.3.0",
"qs": "^6.11.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
......
......@@ -15,6 +15,7 @@ async function embedChatbot () {
return;
}
const isDev = !!difyChatbotConfig.isDev
const baseUrl = difyChatbotConfig.baseUrl || `https://${isDev ? 'dev.' : ''}udify.app`
const openIcon = `<svg
id="openIcon"
width="24"
......@@ -53,7 +54,7 @@ async function embedChatbot () {
iframe.allow = "fullscreen;microphone"
iframe.title = "dify chatbot bubble window"
iframe.id = 'dify-chatbot-bubble-window'
iframe.src = `https://${isDev ? 'dev.' : ''}udify.app/chatbot/${difyChatbotConfig.token}`;
iframe.src = `${baseUrl}/chatbot/${difyChatbotConfig.token}`
iframe.style.cssText = 'border: none; position: fixed; flex-direction: column; justify-content: space-between; box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px; bottom: 5rem; right: 1rem; width: 24rem; height: 40rem; border-radius: 0.75rem; display: flex; z-index: 2147483647; overflow: hidden; left: unset; background-color: #F3F4F6;'
document.body.appendChild(iframe);
}
......
async function embedChatbot(){const t=window.difyChatbotConfig;if(t&&t.token){const o=!!t.isDev,n=`<svg
async function embedChatbot(){const t=window.difyChatbotConfig;if(t&&t.token){var e=!!t.isDev;const o=t.baseUrl||`https://${e?"dev.":""}udify.app`,n=`<svg
id="openIcon"
width="24"
height="24"
......@@ -27,4 +27,4 @@ async function embedChatbot(){const t=window.difyChatbotConfig;if(t&&t.token){co
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>`;if(!document.getElementById("dify-chatbot-bubble-button")){var e=document.createElement("div");e.id="dify-chatbot-bubble-button",e.style.cssText="position: fixed; bottom: 1rem; right: 1rem; width: 50px; height: 50px; border-radius: 25px; background-color: #155EEF; box-shadow: rgba(0, 0, 0, 0.2) 0px 4px 8px 0px; cursor: pointer; z-index: 2147483647; transition: all 0.2s ease-in-out 0s; left: unset; transform: scale(1); :hover {transform: scale(1.1);}";const d=document.createElement("div");d.style.cssText="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; z-index: 2147483647;",d.innerHTML=n,e.appendChild(d),document.body.appendChild(e),e.addEventListener("click",function(){var e=document.getElementById("dify-chatbot-bubble-window");e?"none"===e.style.display?(e.style.display="block",d.innerHTML=i):(e.style.display="none",d.innerHTML=n):((e=document.createElement("iframe")).allow="fullscreen;microphone",e.title="dify chatbot bubble window",e.id="dify-chatbot-bubble-window",e.src=`https://${o?"dev.":""}udify.app/chatbot/`+t.token,e.style.cssText="border: none; position: fixed; flex-direction: column; justify-content: space-between; box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px; bottom: 5rem; right: 1rem; width: 24rem; height: 40rem; border-radius: 0.75rem; display: flex; z-index: 2147483647; overflow: hidden; left: unset; background-color: #F3F4F6;",document.body.appendChild(e),d.innerHTML=i)})}}else console.error("difyChatbotConfig is empty or token is not provided")}document.body.onload=embedChatbot;
\ No newline at end of file
</svg>`;if(!document.getElementById("dify-chatbot-bubble-button")){e=document.createElement("div");e.id="dify-chatbot-bubble-button",e.style.cssText="position: fixed; bottom: 1rem; right: 1rem; width: 50px; height: 50px; border-radius: 25px; background-color: #155EEF; box-shadow: rgba(0, 0, 0, 0.2) 0px 4px 8px 0px; cursor: pointer; z-index: 2147483647; transition: all 0.2s ease-in-out 0s; left: unset; transform: scale(1); :hover {transform: scale(1.1);}";const d=document.createElement("div");d.style.cssText="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; z-index: 2147483647;",d.innerHTML=n,e.appendChild(d),document.body.appendChild(e),e.addEventListener("click",function(){var e=document.getElementById("dify-chatbot-bubble-window");e?"none"===e.style.display?(e.style.display="block",d.innerHTML=i):(e.style.display="none",d.innerHTML=n):((e=document.createElement("iframe")).allow="fullscreen;microphone",e.title="dify chatbot bubble window",e.id="dify-chatbot-bubble-window",e.src=o+"/chatbot/"+t.token,e.style.cssText="border: none; position: fixed; flex-direction: column; justify-content: space-between; box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px; bottom: 5rem; right: 1rem; width: 24rem; height: 40rem; border-radius: 0.75rem; display: flex; z-index: 2147483647; overflow: hidden; left: unset; background-color: #F3F4F6;",document.body.appendChild(e),d.innerHTML=i)})}}else console.error("difyChatbotConfig is empty or token is not provided")}document.body.onload=embedChatbot;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment