Commit 97d3a627 authored by Joel's avatar Joel

feat: init

parent 8acd8f6f
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,tsx}]
charset = utf-8
indent_style = space
indent_size = 2
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2
{
"extends": [
"@antfu",
"plugin:react-hooks/recommended"
],
"rules": {
"@typescript-eslint/consistent-type-definitions": [
"error",
"type"
],
"no-console": "off",
"indent": "off",
"@typescript-eslint/indent": [
"error",
2,
{
"SwitchCase": 1,
"flatTernaryExpressions": false,
"ignoredNodes": [
"PropertyDefinition[decorators]",
"TSUnionType",
"FunctionExpression[params]:has(Identifier[decorators])"
]
}
],
"react-hooks/exhaustive-deps": "warn"
}
}
\ No newline at end of file
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# npm
package-lock.json
# yarn
.pnp.cjs
.pnp.loader.mjs
.yarn/
yarn.lock
.yarnrc.yml
# pmpm
pnpm-lock.yaml
\ No newline at end of file
{
"version": "0.1.0",
"configurations": [
{
"name": "Next.js: debug server-side",
"type": "node-terminal",
"request": "launch",
"command": "npm run dev"
},
{
"name": "Next.js: debug client-side",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000"
},
{
"name": "Next.js: debug full stack",
"type": "node-terminal",
"request": "launch",
"command": "npm run dev",
"serverReadyAction": {
"pattern": "started server on .+, url: (https?://.+)",
"uriFormat": "%s",
"action": "debugWithChrome"
}
}
]
}
\ No newline at end of file
{
"typescript.tsdk": ".yarn/cache/typescript-patch-72dc6f164f-ab417a2f39.zip/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"prettier.enable": false,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"[python]": {
"editor.formatOnType": true
},
"[html]": {
"editor.defaultFormatter": "vscode.html-language-features"
},
"[typescriptreact]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
},
"[javascript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
},
"[javascriptreact]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
},
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
}
}
\ No newline at end of file
# Conversion Web App Template
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Set App Info
Set app info in `config/index.ts`. Includes:
- APP_ID
- API_KEY
- APP_INFO
## Getting Started
First, install dependencies:
```bash
npm install
# or
yarn
# or
pnpm install
```
Then, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
import { type NextRequest } from 'next/server'
import { getInfo, client } from '@/app/api/utils/common'
import { OpenAIStream } from '@/app/api/utils/stream'
export async function POST(request: NextRequest) {
const body = await request.json()
const {
inputs,
query,
conversation_id: conversationId,
response_mode: responseMode
} = body
const { user } = getInfo(request);
const res = await client.createChatMessage(inputs, query, user, responseMode, conversationId)
const stream = await OpenAIStream(res as any)
return new Response(stream as any)
}
\ No newline at end of file
import { type NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
import { getInfo, setSession, client } from '@/app/api/utils/common'
export async function GET(request: NextRequest) {
const { sessionId, user } = getInfo(request);
const { data }: any = await client.getConversations(user);
return NextResponse.json(data, {
headers: setSession(sessionId)
})
}
\ No newline at end of file
import { type NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
import { getInfo, setSession, client } from '@/app/api/utils/common'
export async function GET(request: NextRequest) {
const { sessionId, user } = getInfo(request);
const { searchParams } = new URL(request.url);
const conversationId = searchParams.get('conversation_id')
const { data }: any = await client.getConversationMessages(user, conversationId as string);
return NextResponse.json(data, {
headers: setSession(sessionId)
})
}
\ No newline at end of file
import { type NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
import { getInfo, setSession, client } from '@/app/api/utils/common'
export async function GET(request: NextRequest) {
const { sessionId, user } = getInfo(request);
const { data } = await client.getApplicationParameters(user);
return NextResponse.json(data as object, {
headers: setSession(sessionId)
})
}
\ No newline at end of file
import axios from 'axios'
export class LangGeniusClient {
constructor(apiKey, baseUrl = 'https://api.langgenius.ai/v1') {
this.apiKey = apiKey
this.baseUrl = baseUrl
}
async sendRequest(method, endpoint, data = null, params = null, stream = false) {
const headers = {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
}
const url = `${this.baseUrl}${endpoint}`
let response
if (!stream) {
response = await axios({
method,
url,
data,
params,
headers,
responseType: stream ? 'stream' : 'json',
})
} else {
response = await fetch(url, {
headers,
method,
body: JSON.stringify(data),
})
}
return response
}
messageFeedback(messageId, rating, user) {
const data = {
rating,
user,
}
return this.sendRequest('POST', `/messages/${messageId}/feedbacks`, data)
}
getApplicationParameters(user) {
const params = { user }
return this.sendRequest('GET', '/parameters', null, params)
}
}
export class CompletionClient extends LangGeniusClient {
createCompletionMessage(inputs, query, responseMode, user) {
const data = {
inputs,
query,
responseMode,
user,
}
return this.sendRequest('POST', '/completion-messages', data, null, responseMode === 'streaming')
}
}
export class ChatClient extends LangGeniusClient {
createChatMessage(inputs, query, user, responseMode = 'blocking', conversationId = null) {
const data = {
inputs,
query,
user,
responseMode,
}
if (conversationId)
data.conversation_id = conversationId
return this.sendRequest('POST', '/chat-messages', data, null, responseMode === 'streaming')
}
getConversationMessages(user, conversationId = '', firstId = null, limit = null) {
const params = { user }
if (conversationId)
params.conversation_id = conversationId
if (firstId)
params.first_id = firstId
if (limit)
params.limit = limit
return this.sendRequest('GET', '/messages', null, params)
}
getConversations(user, firstId = null, limit = null, pinned = null) {
const params = { user, first_id: firstId, limit, pinned }
return this.sendRequest('GET', '/conversations', null, params)
}
renameConversation(conversationId, name, user) {
const data = { name, user }
return this.sendRequest('PATCH', `/conversations/${conversationId}`, data)
}
}
import { type NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
import { getInfo, setSession } from '@/app/api/utils/common'
import { APP_INFO } from '@/config'
export async function GET(request: NextRequest) {
const { sessionId } = getInfo(request);
return NextResponse.json(APP_INFO, {
headers: setSession(sessionId)
})
}
\ No newline at end of file
import { type NextRequest } from 'next/server'
import { APP_ID, API_KEY } from '@/config'
import { ChatClient } from '../sdk'
const userPrefix = `user_${APP_ID}:`;
const uuid = require('uuid')
export const getInfo = (request: NextRequest) => {
const sessionId = request.cookies.get('session_id')?.value || uuid.v4();
const user = userPrefix + sessionId;
return {
sessionId,
user
}
}
export const setSession = (sessionId: string) => {
return { 'Set-Cookie': `session_id=${sessionId}` }
}
export const client = new ChatClient(API_KEY)
\ No newline at end of file
export async function OpenAIStream(res: { body: any }) {
const reader = res.body.getReader();
const stream = new ReadableStream({
// https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams
// https://github.com/whichlight/chatgpt-api-streaming/blob/master/pages/api/OpenAIStream.ts
start(controller) {
return pump();
function pump() {
return reader.read().then(({ done, value }: any) => {
// When no more data needs to be consumed, close the stream
if (done) {
controller.close();
return;
}
// Enqueue the next data chunk into our target stream
controller.enqueue(value);
return pump();
});
}
},
});
return stream;
}
\ No newline at end of file
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
type IAppUnavailableProps = {
isUnknwonReason: boolean
errMessage?: string
}
const AppUnavailable: FC<IAppUnavailableProps> = ({
isUnknwonReason,
errMessage,
}) => {
const { t } = useTranslation()
let message = errMessage
if (!errMessage) {
message = (isUnknwonReason ? t('app.common.appUnkonwError') : t('app.common.appUnavailable')) as string
}
return (
<div className='flex items-center justify-center w-screen h-screen'>
<h1 className='mr-5 h-[50px] leading-[50px] pr-5 text-[24px] font-medium'
style={{
borderRight: '1px solid rgba(0,0,0,.3)',
}}>{(errMessage || isUnknwonReason) ? 500 : 404}</h1>
<div className='text-sm'>{message}</div>
</div>
)
}
export default React.memo(AppUnavailable)
import type { FC } from 'react'
import classNames from 'classnames'
import style from './style.module.css'
export type AppIconProps = {
size?: 'tiny' | 'small' | 'medium' | 'large'
rounded?: boolean
icon?: string
background?: string
className?: string
}
const AppIcon: FC<AppIconProps> = ({
size = 'medium',
rounded = false,
background,
className,
}) => {
return (
<span
className={classNames(
style.appIcon,
size !== 'medium' && style[size],
rounded && style.rounded,
className ?? '',
)}
style={{
background,
}}
>
🤖
</span>
)
}
export default AppIcon
.appIcon {
@apply flex items-center justify-center relative w-9 h-9 text-lg bg-teal-100 rounded-lg grow-0 shrink-0;
}
.appIcon.large {
@apply w-10 h-10;
}
.appIcon.small {
@apply w-8 h-8;
}
.appIcon.tiny {
@apply w-6 h-6 text-base;
}
.appIcon.rounded {
@apply rounded-full;
}
import { forwardRef, useEffect, useRef } from 'react'
import cn from 'classnames'
type IProps = {
placeholder?: string
value: string
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void
className?: string
minHeight?: number
maxHeight?: number
autoFocus?: boolean
controlFocus?: number
onKeyDown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void
onKeyUp?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void
}
const AutoHeightTextarea = forwardRef(
(
{ value, onChange, placeholder, className, minHeight = 36, maxHeight = 96, autoFocus, controlFocus, onKeyDown, onKeyUp }: IProps,
outerRef: any,
) => {
const ref = outerRef || useRef<HTMLTextAreaElement>(null)
const doFocus = () => {
if (ref.current) {
ref.current.setSelectionRange(value.length, value.length)
ref.current.focus()
return true
}
return false
}
const focus = () => {
if (!doFocus()) {
let hasFocus = false
const runId = setInterval(() => {
hasFocus = doFocus()
if (hasFocus)
clearInterval(runId)
}, 100)
}
}
useEffect(() => {
if (autoFocus)
focus()
}, [])
useEffect(() => {
if (controlFocus)
focus()
}, [controlFocus])
return (
<div className='relative'>
<div className={cn(className, 'invisible whitespace-pre-wrap break-all overflow-y-auto')} style={{ minHeight, maxHeight }}>
{!value ? placeholder : value.replace(/\n$/, '\n ')}
</div>
<textarea
ref={ref}
autoFocus={autoFocus}
className={cn(className, 'absolute inset-0 resize-none overflow-hidden')}
placeholder={placeholder}
onChange={onChange}
onKeyDown={onKeyDown}
onKeyUp={onKeyUp}
value={value}
/>
</div>
)
},
)
export default AutoHeightTextarea
import type { FC, MouseEventHandler } from 'react'
import React from 'react'
import Spinner from '@/app/components/base/spinner'
export type IButtonProps = {
type?: string
className?: string
disabled?: boolean
loading?: boolean
children: React.ReactNode
onClick?: MouseEventHandler<HTMLDivElement>
}
const Button: FC<IButtonProps> = ({
type,
disabled,
children,
className,
onClick,
loading = false,
}) => {
let style = 'cursor-pointer'
switch (type) {
case 'primary':
style = (disabled || loading) ? 'bg-primary-600/75 cursor-not-allowed text-white' : 'bg-primary-600 hover:bg-primary-600/75 hover:shadow-md cursor-pointer text-white hover:shadow-sm'
break
default:
style = disabled ? 'border-solid border border-gray-200 bg-gray-200 cursor-not-allowed text-gray-800' : 'border-solid border border-gray-200 cursor-pointer text-gray-500 hover:bg-white hover:shadow-sm hover:border-gray-300'
break
}
return (
<div
className={`flex justify-center items-center content-center h-9 leading-5 rounded-lg px-4 py-2 text-base ${style} ${className && className}`}
onClick={disabled ? undefined : onClick}
>
{children}
{/* Spinner is hidden when loading is false */}
<Spinner loading={loading} className='!text-white !h-3 !w-3 !border-2 !ml-1' />
</div>
)
}
export default React.memo(Button)
import React from 'react'
import './style.css'
type ILoadingProps = {
type?: 'area' | 'app'
}
const Loading = (
{ type = 'area' }: ILoadingProps = { type: 'area' },
) => {
return (
<div className={`flex w-full justify-center items-center ${type === 'app' ? 'h-full' : ''}`}>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className='spin-animation'>
<g clipPath="url(#clip0_324_2488)">
<path d="M15 0H10C9.44772 0 9 0.447715 9 1V6C9 6.55228 9.44772 7 10 7H15C15.5523 7 16 6.55228 16 6V1C16 0.447715 15.5523 0 15 0Z" fill="#1C64F2" />
<path opacity="0.5" d="M15 9H10C9.44772 9 9 9.44772 9 10V15C9 15.5523 9.44772 16 10 16H15C15.5523 16 16 15.5523 16 15V10C16 9.44772 15.5523 9 15 9Z" fill="#1C64F2" />
<path opacity="0.1" d="M6 9H1C0.447715 9 0 9.44772 0 10V15C0 15.5523 0.447715 16 1 16H6C6.55228 16 7 15.5523 7 15V10C7 9.44772 6.55228 9 6 9Z" fill="#1C64F2" />
<path opacity="0.2" d="M6 0H1C0.447715 0 0 0.447715 0 1V6C0 6.55228 0.447715 7 1 7H6C6.55228 7 7 6.55228 7 6V1C7 0.447715 6.55228 0 6 0Z" fill="#1C64F2" />
</g>
<defs>
<clipPath id="clip0_324_2488">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</svg>
</div>
)
}
export default Loading
.spin-animation path {
animation: custom 2s linear infinite;
}
@keyframes custom {
0% {
opacity: 0;
}
25% {
opacity: 0.1;
}
50% {
opacity: 0.2;
}
75% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
.spin-animation path:nth-child(1) {
animation-delay: 0s;
}
.spin-animation path:nth-child(2) {
animation-delay: 0.5s;
}
.spin-animation path:nth-child(3) {
animation-delay: 1s;
}
.spin-animation path:nth-child(4) {
animation-delay: 1.5s;
}
\ No newline at end of file
import ReactMarkdown from 'react-markdown'
import 'katex/dist/katex.min.css'
import RemarkMath from 'remark-math'
import RemarkBreaks from 'remark-breaks'
import RehypeKatex from 'rehype-katex'
import RemarkGfm from 'remark-gfm'
import SyntaxHighlighter from 'react-syntax-highlighter'
import { atelierHeathLight } from 'react-syntax-highlighter/dist/esm/styles/hljs'
export function Markdown(props: { content: string }) {
return (
<div className="markdown-body">
<ReactMarkdown
remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
rehypePlugins={[
RehypeKatex,
]}
components={{
code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || '')
return !inline && match
? (
<SyntaxHighlighter
{...props}
children={String(children).replace(/\n$/, '')}
style={atelierHeathLight}
language={match[1]}
showLineNumbers
PreTag="div"
/>
)
: (
<code {...props} className={className}>
{children}
</code>
)
},
}}
linkTarget={'_blank'}
>
{props.content}
</ReactMarkdown>
</div>
)
}
'use client'
import type { FC } from 'react'
import React, { Fragment, useEffect, useState } from 'react'
import { Combobox, Listbox, Transition } from '@headlessui/react'
import classNames from 'classnames'
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/20/solid'
const defaultItems = [
{ value: 1, name: 'option1' },
{ value: 2, name: 'option2' },
{ value: 3, name: 'option3' },
{ value: 4, name: 'option4' },
{ value: 5, name: 'option5' },
{ value: 6, name: 'option6' },
{ value: 7, name: 'option7' },
]
export type Item = {
value: number | string
name: string
}
export type ISelectProps = {
className?: string
items?: Item[]
defaultValue?: number | string
disabled?: boolean
onSelect: (value: Item) => void
allowSearch?: boolean
bgClassName?: string
}
const Select: FC<ISelectProps> = ({
className,
items = defaultItems,
defaultValue = 1,
disabled = false,
onSelect,
allowSearch = true,
bgClassName = 'bg-gray-100',
}) => {
const [query, setQuery] = useState('')
const [open, setOpen] = useState(false)
const [selectedItem, setSelectedItem] = useState<Item | null>(null)
useEffect(() => {
let defaultSelect = null
const existed = items.find((item: Item) => item.value === defaultValue)
if (existed)
defaultSelect = existed
setSelectedItem(defaultSelect)
}, [defaultValue])
const filteredItems: Item[]
= query === ''
? items
: items.filter((item) => {
return item.name.toLowerCase().includes(query.toLowerCase())
})
return (
<Combobox
as="div"
disabled={disabled}
value={selectedItem}
className={className}
onChange={(value: Item) => {
if (!disabled) {
setSelectedItem(value)
setOpen(false)
onSelect(value)
}
}}>
<div className={classNames('relative')}>
<div className='group text-gray-800'>
{allowSearch
? <Combobox.Input
className={`w-full rounded-lg border-0 ${bgClassName} 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-not-allowed`}
onChange={(event) => {
if (!disabled)
setQuery(event.target.value)
}}
displayValue={(item: Item) => item?.name}
/>
: <Combobox.Button onClick={
() => {
if (!disabled)
setOpen(!open)
}
} className={`flex items-center h-9 w-full rounded-lg border-0 ${bgClassName} 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`}>
{selectedItem?.name}
</Combobox.Button>}
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none group-hover:bg-gray-200" onClick={
() => {
if (!disabled)
setOpen(!open)
}
}>
{open ? <ChevronUpIcon className="h-5 w-5" /> : <ChevronDownIcon className="h-5 w-5" />}
</Combobox.Button>
</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">
{filteredItems.map((item: Item) => (
<Combobox.Option
key={item.value}
value={item}
className={({ active }: { active: boolean }) =>
classNames(
'relative cursor-default select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700',
active ? 'bg-gray-100' : '',
)
}
>
{({ /* active, */ selected }) => (
<>
<span className={classNames('block truncate', selected && 'font-normal')}>{item.name}</span>
{selected && (
<span
className={classNames(
'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700',
)}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
)}
</>
)}
</Combobox.Option>
))}
</Combobox.Options>
)}
</div>
</Combobox >
)
}
const SimpleSelect: FC<ISelectProps> = ({
className,
items = defaultItems,
defaultValue = 1,
disabled = false,
onSelect,
}) => {
const [selectedItem, setSelectedItem] = useState<Item | null>(null)
useEffect(() => {
let defaultSelect = null
const existed = items.find((item: Item) => item.value === defaultValue)
if (existed)
defaultSelect = existed
setSelectedItem(defaultSelect)
}, [defaultValue])
return (
<Listbox
value={selectedItem}
onChange={(value: Item) => {
if (!disabled) {
setSelectedItem(value)
onSelect(value)
}
}}
>
<div className="relative h-9">
<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="block truncate text-left">{selectedItem?.name}</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"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.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">
{items.map((item: Item) => (
<Listbox.Option
key={item.value}
className={({ active }) =>
`relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : ''
}`
}
value={item}
disabled={disabled}
>
{({ /* active, */ selected }) => (
<>
<span className={classNames('block truncate', selected && 'font-normal')}>{item.name}</span>
{selected && (
<span
className={classNames(
'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700',
)}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
)}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</Listbox>
)
}
export { SimpleSelect }
export default React.memo(Select)
import type { FC } from 'react'
import React from 'react'
type Props = {
loading?: boolean
className?: string
children?: React.ReactNode | string
}
const Spinner: FC<Props> = ({ loading = false, children, className }) => {
return (
<div
className={`inline-block text-gray-200 h-4 w-4 animate-spin rounded-full border-4 border-solid border-current border-r-transparent align-[-0.125em] ${loading ? 'motion-reduce:animate-[spin_1.5s_linear_infinite]' : 'hidden'} ${className ?? ''}`}
role="status"
>
<span
className="!absolute !-m-px !h-px !w-px !overflow-hidden !whitespace-nowrap !border-0 !p-0 ![clip:rect(0,0,0,0)]"
>Loading...</span>
{children}
</div>
)
}
export default Spinner
'use client'
import classNames from 'classnames'
import type { ReactNode } from 'react'
import React, { useEffect, useState } from 'react'
import { createRoot } from 'react-dom/client'
import {
CheckCircleIcon,
ExclamationTriangleIcon,
InformationCircleIcon,
XCircleIcon,
} from '@heroicons/react/20/solid'
import { createContext } from 'use-context-selector'
export type IToastProps = {
type?: 'success' | 'error' | 'warning' | 'info'
duration?: number
message: string
children?: ReactNode
onClose?: () => void
}
type IToastContext = {
notify: (props: IToastProps) => void
}
const defaultDuring = 3000
export const ToastContext = createContext<IToastContext>({} as IToastContext)
const Toast = ({
type = 'info',
duration,
message,
children,
}: IToastProps) => {
// sometimes message is react node array. Not handle it.
if (typeof message !== 'string')
return null
return <div className={classNames(
'fixed rounded-md p-4 my-4 mx-8 z-50',
'top-0',
'right-0',
type === 'success' ? 'bg-green-50' : '',
type === 'error' ? 'bg-red-50' : '',
type === 'warning' ? 'bg-yellow-50' : '',
type === 'info' ? 'bg-blue-50' : '',
)}>
<div className="flex">
<div className="flex-shrink-0">
{type === 'success' && <CheckCircleIcon className="w-5 h-5 text-green-400" aria-hidden="true" />}
{type === 'error' && <XCircleIcon className="w-5 h-5 text-red-400" aria-hidden="true" />}
{type === 'warning' && <ExclamationTriangleIcon className="w-5 h-5 text-yellow-400" aria-hidden="true" />}
{type === 'info' && <InformationCircleIcon className="w-5 h-5 text-blue-400" aria-hidden="true" />}
</div>
<div className="ml-3">
<h3 className={
classNames(
'text-sm font-medium',
type === 'success' ? 'text-green-800' : '',
type === 'error' ? 'text-red-800' : '',
type === 'warning' ? 'text-yellow-800' : '',
type === 'info' ? 'text-blue-800' : '',
)
}>{message}</h3>
{children && <div className={
classNames(
'mt-2 text-sm',
type === 'success' ? 'text-green-700' : '',
type === 'error' ? 'text-red-700' : '',
type === 'warning' ? 'text-yellow-700' : '',
type === 'info' ? 'text-blue-700' : '',
)
}>
{children}
</div>
}
</div>
</div>
</div>
}
export const ToastProvider = ({
children,
}: {
children: ReactNode
}) => {
const placeholder: IToastProps = {
type: 'info',
message: 'Toast message',
duration: 3000,
}
const [params, setParams] = React.useState<IToastProps>(placeholder)
const [mounted, setMounted] = useState(false)
useEffect(() => {
if (mounted) {
setTimeout(() => {
setMounted(false)
}, params.duration || defaultDuring)
}
}, [mounted])
return <ToastContext.Provider value={{
notify: (props) => {
setMounted(true)
setParams(props)
},
}}>
{mounted && <Toast {...params} />}
{children}
</ToastContext.Provider>
}
Toast.notify = ({
type,
message,
duration,
}: Pick<IToastProps, 'type' | 'message' | 'duration'>) => {
if (typeof window === 'object') {
const holder = document.createElement('div')
const root = createRoot(holder)
root.render(<Toast type={type} message={message} duration={duration} />)
document.body.appendChild(holder)
setTimeout(() => {
if (holder)
holder.remove()
}, duration || defaultDuring)
}
}
export default Toast
.toast {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
width: 1.84rem;
height: 1.80rem;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
background: #000000;
box-shadow: 0 -.04rem .1rem 1px rgba(255, 255, 255, 0.1);
border-radius: .1rem .1rem .1rem .1rem;
}
.main {
width: 2rem;
}
.icon {
margin-bottom: .2rem;
height: .4rem;
background: center center no-repeat;
background-size: contain;
}
/* .success {
background-image: url('./icons/success.svg');
}
.warning {
background-image: url('./icons/warning.svg');
}
.error {
background-image: url('./icons/error.svg');
} */
.text {
text-align: center;
font-size: .2rem;
color: rgba(255, 255, 255, 0.86);
}
\ No newline at end of file
'use client'
import classNames from 'classnames'
import type { FC } from 'react'
import React from 'react'
import { Tooltip as ReactTooltip } from 'react-tooltip' // fixed version to 5.8.3 https://github.com/ReactTooltip/react-tooltip/issues/972
import 'react-tooltip/dist/react-tooltip.css'
type TooltipProps = {
selector: string
content?: string
htmlContent?: React.ReactNode
className?: string // This should use !impornant to override the default styles eg: '!bg-white'
position?: 'top' | 'right' | 'bottom' | 'left'
clickable?: boolean
children: React.ReactNode
}
const Tooltip: FC<TooltipProps> = ({
selector,
content,
position = 'top',
children,
htmlContent,
className,
clickable,
}) => {
return (
<div className='tooltip-container'>
{React.cloneElement(children as React.ReactElement, {
'data-tooltip-id': selector,
})
}
<ReactTooltip
id={selector}
content={content}
className={classNames('!bg-white !text-xs !font-normal !text-gray-700 !shadow-lg !opacity-100', className)}
place={position}
clickable={clickable}
>
{htmlContent && htmlContent}
</ReactTooltip>
</div>
)
}
export default Tooltip
<svg width="8" height="12" viewBox="0 0 8 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.03647 1.5547C0.59343 0.890144 1.06982 0 1.86852 0H8V12L1.03647 1.5547Z" fill="#F3F4F6"/>
</svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 11.9998L13.3332 12.7292C12.9796 13.1159 12.5001 13.3332 12.0001 13.3332C11.5001 13.3332 11.0205 13.1159 10.6669 12.7292C10.3128 12.3432 9.83332 12.1265 9.33345 12.1265C8.83359 12.1265 8.35409 12.3432 7.99998 12.7292M2 13.3332H3.11636C3.44248 13.3332 3.60554 13.3332 3.75899 13.2963C3.89504 13.2637 4.0251 13.2098 4.1444 13.1367C4.27895 13.0542 4.39425 12.9389 4.62486 12.7083L13 4.33316C13.5523 3.78087 13.5523 2.88544 13 2.33316C12.4477 1.78087 11.5523 1.78087 11 2.33316L2.62484 10.7083C2.39424 10.9389 2.27894 11.0542 2.19648 11.1888C2.12338 11.3081 2.0695 11.4381 2.03684 11.5742C2 11.7276 2 11.8907 2 12.2168V13.3332Z" stroke="#6B7280" strokeLinecap="round" strokeLinejoin="round" />
</svg>
\ No newline at end of file
<svg width="8" height="12" viewBox="0 0 8 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.96353 1.5547C7.40657 0.890144 6.93018 0 6.13148 0H0V12L6.96353 1.5547Z" fill="#E1EFFE"/>
</svg>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="40" height="40" rx="20" fill="#D5F5F6"/>
<path d="M11 28.76H29V10.76H11V28.76Z" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_135_973" transform="scale(0.00625)"/>
</pattern>
<image id="image0_135_973" width="160" height="160" xlink:href="data:image/png;base64,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"/>
</defs>
</svg>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.447 16.8939C23.6128 16.8108 23.7523 16.6832 23.8498 16.5253C23.9473 16.3674 23.9989 16.1855 23.9989 15.9999C23.9989 15.8144 23.9473 15.6325 23.8498 15.4746C23.7523 15.3167 23.6128 15.1891 23.447 15.1059L9.44697 8.10595C9.27338 8.01909 9.07827 7.98463 8.88543 8.00677C8.69259 8.02891 8.51036 8.1067 8.36098 8.23064C8.2116 8.35458 8.10151 8.51931 8.04415 8.70475C7.9868 8.89019 7.98465 9.08831 8.03797 9.27495L9.46697 14.2749C9.52674 14.4839 9.65297 14.6677 9.82655 14.7985C10.0001 14.9294 10.2116 15.0001 10.429 14.9999H15C15.2652 14.9999 15.5195 15.1053 15.7071 15.2928C15.8946 15.4804 16 15.7347 16 15.9999C16 16.2652 15.8946 16.5195 15.7071 16.7071C15.5195 16.8946 15.2652 16.9999 15 16.9999H10.429C10.2116 16.9998 10.0001 17.0705 9.82655 17.2013C9.65297 17.3322 9.52674 17.516 9.46697 17.7249L8.03897 22.7249C7.98554 22.9115 7.98756 23.1096 8.04478 23.2951C8.10201 23.4805 8.21195 23.6453 8.36122 23.7693C8.51049 23.8934 8.69263 23.9713 8.88542 23.9936C9.07821 24.0159 9.27332 23.9816 9.44697 23.8949L23.447 16.8949V16.8939Z" fill="#1C64F2"/>
</svg>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.447 16.8939C23.6128 16.8108 23.7523 16.6832 23.8498 16.5253C23.9473 16.3674 23.9989 16.1855 23.9989 15.9999C23.9989 15.8144 23.9473 15.6325 23.8498 15.4746C23.7523 15.3167 23.6128 15.1891 23.447 15.1059L9.44697 8.10595C9.27338 8.01909 9.07827 7.98463 8.88543 8.00677C8.69259 8.02891 8.51036 8.1067 8.36098 8.23064C8.2116 8.35458 8.10151 8.51931 8.04415 8.70475C7.9868 8.89019 7.98465 9.08831 8.03797 9.27495L9.46697 14.2749C9.52674 14.4839 9.65297 14.6677 9.82655 14.7985C10.0001 14.9294 10.2116 15.0001 10.429 14.9999H15C15.2652 14.9999 15.5195 15.1053 15.7071 15.2928C15.8946 15.4804 16 15.7347 16 15.9999C16 16.2652 15.8946 16.5195 15.7071 16.7071C15.5195 16.8946 15.2652 16.9999 15 16.9999H10.429C10.2116 16.9998 10.0001 17.0705 9.82655 17.2013C9.65297 17.3322 9.52674 17.516 9.46697 17.7249L8.03897 22.7249C7.98554 22.9115 7.98756 23.1096 8.04478 23.2951C8.10201 23.4805 8.21195 23.6453 8.36122 23.7693C8.51049 23.8934 8.69263 23.9713 8.88542 23.9936C9.07821 24.0159 9.27332 23.9816 9.44697 23.8949L23.447 16.8949V16.8939Z" fill="#D1D5DB"/>
</svg>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_2358_1380)">
<rect x="2" y="1" width="16" height="16" rx="8" fill="white"/>
<path opacity="0.7" d="M13.5 9H13.505M14 9C14 9.13261 13.9473 9.25979 13.8536 9.35355C13.7598 9.44732 13.6326 9.5 13.5 9.5C13.3674 9.5 13.2402 9.44732 13.1464 9.35355C13.0527 9.25979 13 9.13261 13 9C13 8.86739 13.0527 8.74021 13.1464 8.64645C13.2402 8.55268 13.3674 8.5 13.5 8.5C13.6326 8.5 13.7598 8.55268 13.8536 8.64645C13.9473 8.74021 14 8.86739 14 9Z" stroke="#155EEF" stroke-linecap="round" stroke-linejoin="round"/>
<path opacity="0.6" d="M10 9H10.005M10.5 9C10.5 9.13261 10.4473 9.25979 10.3536 9.35355C10.2598 9.44732 10.1326 9.5 10 9.5C9.86739 9.5 9.74021 9.44732 9.64645 9.35355C9.55268 9.25979 9.5 9.13261 9.5 9C9.5 8.86739 9.55268 8.74021 9.64645 8.64645C9.74021 8.55268 9.86739 8.5 10 8.5C10.1326 8.5 10.2598 8.55268 10.3536 8.64645C10.4473 8.74021 10.5 8.86739 10.5 9Z" stroke="#155EEF" stroke-linecap="round" stroke-linejoin="round"/>
<path opacity="0.3" d="M6.5 9H6.505M7 9C7 9.13261 6.94732 9.25979 6.85355 9.35355C6.75979 9.44732 6.63261 9.5 6.5 9.5C6.36739 9.5 6.24021 9.44732 6.14645 9.35355C6.05268 9.25979 6 9.13261 6 9C6 8.86739 6.05268 8.74021 6.14645 8.64645C6.24021 8.55268 6.36739 8.5 6.5 8.5C6.63261 8.5 6.75979 8.55268 6.85355 8.64645C6.94732 8.74021 7 8.86739 7 9Z" stroke="#155EEF" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<filter id="filter0_d_2358_1380" x="0" y="0" width="20" height="20" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_2358_1380"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_2358_1380" result="shape"/>
</filter>
</defs>
</svg>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="40" height="40" rx="20" fill="white"/>
<rect width="40" height="40" rx="20" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_84_1144" transform="scale(0.00238095)"/>
</pattern>
<image id="image0_84_1144" width="420" height="420" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaQAAAGkCAIAAADxLsZiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDkuMC1jMDAwIDc5LjE3MWMyN2ZhYiwgMjAyMi8wOC8xNi0yMjozNTo0MSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpmNjg5NjkyMy05NDA5LTRjODgtOTE3OC0zOTBmNDAzNWQzYjIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RTI2RDk1MDg3REFEMTFFRDgxQUM4MDk5QUJENjI4QzIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RTI2RDk1MDc3REFEMTFFRDgxQUM4MDk5QUJENjI4QzIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDI0LjEgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5YzQzOGNjNS03MDEzLTRmNDMtOTg0Mi02NmNkNDI5OTVkYmIiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6ZjY4OTY5MjMtOTQwOS00Yzg4LTkxNzgtMzkwZjQwMzVkM2Iy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"/>
</defs>
</svg>
'use client'
import type { FC } from 'react'
import React, { useEffect } from 'react'
import cn from 'classnames'
import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline'
import { useTranslation } from 'react-i18next'
import s from './style.module.css'
import { randomString } from '@/utils/string'
import type { Feedbacktype, MessageRating } from '@/types/app'
import Tooltip from '@/app/components/base/tooltip'
import Toast from '@/app/components/base/toast'
import AutoHeightTextarea from '@/app/components/base/auto-height-textarea'
import { Markdown } from '@/app/components/base/markdown'
export type FeedbackFunc = (messageId: string, feedback: Feedbacktype) => Promise<any>
export type IChatProps = {
chatList: IChatItem[]
/**
* Whether to display the editing area and rating status
*/
feedbackDisabled?: boolean
/**
* Whether to display the input area
*/
isHideSendInput?: boolean
onFeedback?: FeedbackFunc
checkCanSend?: () => boolean
onSend?: (message: string) => void
useCurrentUserAvatar?: boolean
isResponsing?: boolean
controlClearQuery?: number
controlFocus?: number
}
export type IChatItem = {
id: string
content: string
/**
* Specific message type
*/
isAnswer: boolean
/**
* The user feedback result of this message
*/
feedback?: Feedbacktype
/**
* Whether to hide the feedback area
*/
feedbackDisabled?: boolean
isIntroduction?: boolean
useCurrentUserAvatar?: boolean
isOpeningStatement?: boolean
}
const OperationBtn = ({ innerContent, onClick, className }: { innerContent: React.ReactNode; onClick?: () => void; className?: string }) => (
<div
className={`relative box-border flex items-center justify-center h-7 w-7 p-0.5 rounded-lg bg-white cursor-pointer text-gray-500 hover:text-gray-800 ${className ?? ''}`}
style={{ boxShadow: '0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.05)' }}
onClick={onClick && onClick}
>
{innerContent}
</div>
)
const OpeningStatementIcon: FC<{ className?: string }> = ({ className }) => (
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M6.25002 1C3.62667 1 1.50002 3.12665 1.50002 5.75C1.50002 6.28 1.58702 6.79071 1.7479 7.26801C1.7762 7.35196 1.79285 7.40164 1.80368 7.43828L1.80722 7.45061L1.80535 7.45452C1.79249 7.48102 1.77339 7.51661 1.73766 7.58274L0.911727 9.11152C0.860537 9.20622 0.807123 9.30503 0.770392 9.39095C0.733879 9.47635 0.674738 9.63304 0.703838 9.81878C0.737949 10.0365 0.866092 10.2282 1.05423 10.343C1.21474 10.4409 1.38213 10.4461 1.475 10.4451C1.56844 10.444 1.68015 10.4324 1.78723 10.4213L4.36472 10.1549C4.406 10.1506 4.42758 10.1484 4.44339 10.1472L4.44542 10.147L4.45161 10.1492C4.47103 10.1562 4.49738 10.1663 4.54285 10.1838C5.07332 10.3882 5.64921 10.5 6.25002 10.5C8.87338 10.5 11 8.37335 11 5.75C11 3.12665 8.87338 1 6.25002 1ZM4.48481 4.29111C5.04844 3.81548 5.7986 3.9552 6.24846 4.47463C6.69831 3.9552 7.43879 3.82048 8.01211 4.29111C8.58544 4.76175 8.6551 5.562 8.21247 6.12453C7.93825 6.47305 7.24997 7.10957 6.76594 7.54348C6.58814 7.70286 6.49924 7.78255 6.39255 7.81466C6.30103 7.84221 6.19589 7.84221 6.10436 7.81466C5.99767 7.78255 5.90878 7.70286 5.73098 7.54348C5.24694 7.10957 4.55867 6.47305 4.28444 6.12453C3.84182 5.562 3.92117 4.76675 4.48481 4.29111Z" fill="#667085" />
</svg>
)
const RatingIcon: FC<{ isLike: boolean }> = ({ isLike }) => {
return isLike ? <HandThumbUpIcon className='w-4 h-4' /> : <HandThumbDownIcon className='w-4 h-4' />
}
const EditIcon: FC<{ className?: string }> = ({ className }) => {
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
<path d="M14 11.9998L13.3332 12.7292C12.9796 13.1159 12.5001 13.3332 12.0001 13.3332C11.5001 13.3332 11.0205 13.1159 10.6669 12.7292C10.3128 12.3432 9.83332 12.1265 9.33345 12.1265C8.83359 12.1265 8.35409 12.3432 7.99998 12.7292M2 13.3332H3.11636C3.44248 13.3332 3.60554 13.3332 3.75899 13.2963C3.89504 13.2637 4.0251 13.2098 4.1444 13.1367C4.27895 13.0542 4.39425 12.9389 4.62486 12.7083L13 4.33316C13.5523 3.78087 13.5523 2.88544 13 2.33316C12.4477 1.78087 11.5523 1.78087 11 2.33316L2.62484 10.7083C2.39424 10.9389 2.27894 11.0542 2.19648 11.1888C2.12338 11.3081 2.0695 11.4381 2.03684 11.5742C2 11.7276 2 11.8907 2 12.2168V13.3332Z" stroke="#6B7280" strokeLinecap="round" strokeLinejoin="round" />
</svg>
}
export const EditIconSolid: FC<{ className?: string }> = ({ className }) => {
return <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
<path fillRule="evenodd" clip-rule="evenodd" d="M10.8374 8.63108C11.0412 8.81739 11.0554 9.13366 10.8691 9.33747L10.369 9.88449C10.0142 10.2725 9.52293 10.5001 9.00011 10.5001C8.47746 10.5001 7.98634 10.2727 7.63157 9.8849C7.45561 9.69325 7.22747 9.59515 7.00014 9.59515C6.77271 9.59515 6.54446 9.69335 6.36846 9.88517C6.18177 10.0886 5.86548 10.1023 5.66201 9.91556C5.45853 9.72888 5.44493 9.41259 5.63161 9.20911C5.98678 8.82201 6.47777 8.59515 7.00014 8.59515C7.52251 8.59515 8.0135 8.82201 8.36867 9.20911L8.36924 9.20974C8.54486 9.4018 8.77291 9.50012 9.00011 9.50012C9.2273 9.50012 9.45533 9.40182 9.63095 9.20979L10.131 8.66276C10.3173 8.45895 10.6336 8.44476 10.8374 8.63108Z" fill="#6B7280" />
<path fillRule="evenodd" clip-rule="evenodd" d="M7.89651 1.39656C8.50599 0.787085 9.49414 0.787084 10.1036 1.39656C10.7131 2.00604 10.7131 2.99419 10.1036 3.60367L3.82225 9.88504C3.81235 9.89494 3.80254 9.90476 3.79281 9.91451C3.64909 10.0585 3.52237 10.1855 3.3696 10.2791C3.23539 10.3613 3.08907 10.4219 2.93602 10.4587C2.7618 10.5005 2.58242 10.5003 2.37897 10.5001C2.3652 10.5001 2.35132 10.5001 2.33732 10.5001H1.50005C1.22391 10.5001 1.00005 10.2763 1.00005 10.0001V9.16286C1.00005 9.14886 1.00004 9.13497 1.00003 9.1212C0.999836 8.91776 0.999669 8.73838 1.0415 8.56416C1.07824 8.4111 1.13885 8.26479 1.22109 8.13058C1.31471 7.97781 1.44166 7.85109 1.58566 7.70736C1.5954 7.69764 1.60523 7.68783 1.61513 7.67793L7.89651 1.39656Z" fill="#6B7280" />
</svg>
}
const IconWrapper: FC<{ children: React.ReactNode | string }> = ({ children }) => {
return <div className={'rounded-lg h-6 w-6 flex items-center justify-center hover:bg-gray-100'}>
{children}
</div>
}
type IAnswerProps = {
item: IChatItem
feedbackDisabled: boolean
onFeedback?: FeedbackFunc
isResponsing?: boolean
}
// The component needs to maintain its own state to control whether to display input component
const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, onFeedback, isResponsing }) => {
const { id, content, feedback } = item
const { t } = useTranslation()
/**
* Render feedback results (distinguish between users and administrators)
* User reviews cannot be cancelled in Console
* @param rating feedback result
* @param isUserFeedback Whether it is user's feedback
* @returns comp
*/
const renderFeedbackRating = (rating: MessageRating | undefined) => {
if (!rating)
return null
const isLike = rating === 'like'
const ratingIconClassname = isLike ? 'text-primary-600 bg-primary-100 hover:bg-primary-200' : 'text-red-600 bg-red-100 hover:bg-red-200'
// The tooltip is always displayed, but the content is different for different scenarios.
return (
<Tooltip
selector={`user-feedback-${randomString(16)}`}
content={isLike ? '取消赞同' : '取消反对'}
>
<div
className={'relative box-border flex items-center justify-center h-7 w-7 p-0.5 rounded-lg bg-white cursor-pointer text-gray-500 hover:text-gray-800'}
style={{ boxShadow: '0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.05)' }}
onClick={async () => {
await onFeedback?.(id, { rating: null })
}}
>
<div className={`${ratingIconClassname} rounded-lg h-6 w-6 flex items-center justify-center`}>
<RatingIcon isLike={isLike} />
</div>
</div>
</Tooltip>
)
}
/**
* Different scenarios have different operation items.
* @returns comp
*/
const renderItemOperation = () => {
const userOperation = () => {
return feedback?.rating
? null
: <div className='flex gap-1'>
<Tooltip selector={`user-feedback-${randomString(16)}`} content={t('common.operation.like') as string}>
{OperationBtn({ innerContent: <IconWrapper><RatingIcon isLike={true} /></IconWrapper>, onClick: () => onFeedback?.(id, { rating: 'like' }) })}
</Tooltip>
<Tooltip selector={`user-feedback-${randomString(16)}`} content={t('common.operation.dislike') as string}>
{OperationBtn({ innerContent: <IconWrapper><RatingIcon isLike={false} /></IconWrapper>, onClick: () => onFeedback?.(id, { rating: 'dislike' }) })}
</Tooltip>
</div>
}
return (
<div className={`${s.itemOperation} flex gap-2`}>
{userOperation()}
</div>
)
}
return (
<div key={id}>
<div className='flex items-start'>
<div className={`${s.answerIcon} ${isResponsing ? s.typeingIcon : ''} w-10 h-10 shrink-0`}></div>
<div className={`${s.answerWrap}`}>
<div className={`${s.answer} relative text-sm text-gray-900`}>
<div className={'ml-2 py-3 px-4 bg-gray-100 rounded-tr-2xl rounded-b-2xl'}>
{item.isOpeningStatement && (
<div className='flex items-center mb-1 gap-1'>
<OpeningStatementIcon />
<div className='text-xs text-gray-500'>{t('app.chat.openingStatementTitle')}</div>
</div>
)}
<Markdown content={content} />
</div>
<div className='absolute top-[-14px] right-[-14px] flex flex-row justify-end gap-1'>
{!feedbackDisabled && !item.feedbackDisabled && renderItemOperation()}
{/* User feedback must be displayed */}
{!feedbackDisabled && renderFeedbackRating(feedback?.rating)}
</div>
</div>
</div>
</div>
</div>
)
}
type IQuestionProps = Pick<IChatItem, 'id' | 'content' | 'useCurrentUserAvatar'>
const Question: FC<IQuestionProps> = ({ id, content, useCurrentUserAvatar }) => {
const userName = ''
return (
<div className='flex items-start justify-end' key={id}>
<div>
<div className={`${s.question} relative text-sm text-gray-900`}>
<div
className={'mr-2 py-3 px-4 bg-blue-500 rounded-tl-2xl rounded-b-2xl'}
>
<Markdown content={content} />
</div>
</div>
</div>
{useCurrentUserAvatar
? (
<div className='w-10 h-10 shrink-0 leading-10 text-center mr-2 rounded-full bg-primary-600 text-white'>
{userName?.[0].toLocaleUpperCase()}
</div>
)
: (
<div className={`${s.questionIcon} w-10 h-10 shrink-0 `}></div>
)}
</div>
)
}
const Chat: FC<IChatProps> = ({
chatList,
feedbackDisabled = false,
isHideSendInput = false,
onFeedback,
checkCanSend,
onSend = () => { },
useCurrentUserAvatar,
isResponsing,
controlClearQuery,
controlFocus,
}) => {
const { t } = useTranslation()
const { notify } = Toast
const [query, setQuery] = React.useState('')
const handleContentChange = (e: any) => {
const value = e.target.value
setQuery(value)
}
const logError = (message: string) => {
notify({ type: 'error', message, duration: 3000 })
}
const valid = () => {
if (!query || query.trim() === '') {
logError('Message cannot be empty')
return false
}
return true
}
useEffect(() => {
if (controlClearQuery)
setQuery('')
}, [controlClearQuery])
const handleSend = () => {
if (!valid() || (checkCanSend && !checkCanSend()))
return
onSend(query)
if (!isResponsing)
setQuery('')
}
const handleKeyUp = (e: any) => {
if (e.code === 'Enter') {
e.preventDefault()
if (!e.shiftKey)
handleSend()
}
}
const haneleKeyDown = (e: any) => {
if (e.code === 'Enter' && !e.shiftKey) {
setQuery(query.replace(/\n$/, ''))
e.preventDefault()
}
}
return (
<div className={cn(!feedbackDisabled && 'px-3.5', 'h-full')}>
{/* Chat List */}
<div className="h-full space-y-[30px]">
{chatList.map((item) => {
if (item.isAnswer) {
const isLast = item.id === chatList[chatList.length - 1].id
return <Answer
key={item.id}
item={item}
feedbackDisabled={feedbackDisabled}
onFeedback={onFeedback}
isResponsing={isResponsing && isLast}
/>
}
return <Question key={item.id} id={item.id} content={item.content} useCurrentUserAvatar={useCurrentUserAvatar} />
})}
</div>
{
!isHideSendInput && (
<div className={cn(!feedbackDisabled && '!left-3.5 !right-3.5', 'absolute z-10 bottom-0 left-0 right-0')}>
<div className="positive">
<AutoHeightTextarea
value={query}
onChange={handleContentChange}
onKeyUp={handleKeyUp}
onKeyDown={haneleKeyDown}
minHeight={48}
autoFocus
controlFocus={controlFocus}
className={`${cn(s.textArea)} resize-none block w-full pl-3 bg-gray-50 border border-gray-200 rounded-md focus:outline-none sm:text-sm text-gray-700`}
/>
<div className="absolute top-0 right-2 flex items-center h-[48px]">
<div className={`${s.count} mr-4 h-5 leading-5 text-sm bg-gray-50 text-gray-500`}>{query.trim().length}</div>
<Tooltip
selector='send-tip'
htmlContent={
<div>
<div>{t('common.operation.send')} Enter</div>
<div>{t('common.operation.lineBreak')} Shift Enter</div>
</div>
}
>
<div className={`${s.sendBtn} w-8 h-8 cursor-pointer rounded-md`} onClick={handleSend}></div>
</Tooltip>
</div>
</div>
</div>
)
}
</div>
)
}
export default React.memo(Chat)
.answerIcon {
background: url(./icons/robot.svg);
}
.typeingIcon {
position: relative;
}
.typeingIcon::after {
content: '';
position: absolute;
top: -3px;
left: -3px;
width: 16px;
height: 16px;
background: url(./icons/typing.svg) no-repeat;
background-size: contain;
}
.questionIcon {
background: url(./icons/default-avatar.jpg);
background-size: contain;
border-radius: 50%;
}
.answer::before,
.question::before {
content: '';
position: absolute;
top: 0;
width: 8px;
height: 12px;
}
.answer::before {
left: 0;
background: url(./icons/answer.svg) no-repeat;
}
.answerWrap .itemOperation {
display: none;
}
.answerWrap:hover .itemOperation {
display: flex;
}
.question::before {
right: 0;
background: url(./icons/question.svg) no-repeat;
}
.textArea {
padding-top: 13px;
padding-bottom: 13px;
padding-right: 90px;
border-radius: 12px;
line-height: 20px;
background-color: #fff;
}
.textArea:hover {
background-color: #fff;
}
/* .textArea:focus {
box-shadow: 0px 3px 15px -3px rgba(0, 0, 0, 0.1), 0px 4px 6px rgba(0, 0, 0, 0.05);
} */
.count {
/* display: none; */
padding: 0 2px;
}
.sendBtn {
background: url(./icons/send.svg) center center no-repeat;
}
.sendBtn:hover {
background-image: url(./icons/send-active.svg);
background-color: #EBF5FF;
}
.textArea:focus+div .count {
display: block;
}
.textArea:focus+div .sendBtn {
background-image: url(./icons/send-active.svg);
}
\ No newline at end of file
import type { FC } from 'react'
import React from 'react'
import type { IWelcomeProps } from '../welcome'
import Welcome from '../welcome'
const ConfigSence: FC<IWelcomeProps> = (props) => {
return (
<div className='mb-5 antialiased font-sans overflow-hidden shrink-0'>
<Welcome {...props} />
</div>
)
}
export default React.memo(ConfigSence)
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
isMobile?: boolean
onShowSideBar?: () => void
onCreateNewChat?: () => void
}
const Header: FC<IHeaderProps> = ({
title,
isMobile,
onShowSideBar,
onCreateNewChat,
}) => {
return (
<div className="shrink-0 flex items-center justify-between h-12 px-3 bg-gray-100">
{isMobile
? (
<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></div>}
<div className='flex items-center space-x-2'>
<AppIcon size="small" />
<div className=" text-sm text-gray-800 font-bold">{title}</div>
</div>
{isMobile
? (
<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></div>}
</div>
)
}
export default React.memo(Header)
'use client'
import type { FC } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import produce from 'immer'
import { useBoolean, useGetState } from 'ahooks'
import useConversation from '@/hooks/use-conversation'
import Toast from '@/app/components/base/toast'
import Sidebar from '@/app/components/sidebar'
import ConfigSence from '@/app/components/config-scence'
import Header from '@/app/components/header'
import { fetchAppInfo, fetchAppParams, fetchChatList, fetchConversations, sendChatMessage, updateFeedback } from '@/service'
import type { ConversationItem, Feedbacktype, IChatItem, PromptConfig, SiteInfo } from '@/types/app'
import Chat from '@/app/components/chat'
import { setLocaleOnClient } from '@/i18n/client'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Loading from '@/app/components/base/loading'
import { replaceVarWithValues } from '@/utils/prompt'
import AppUnavailable from '@/app/components/app-unavailable'
import { APP_ID, API_KEY } from '@/config'
export type IMainProps = {
params: {
locale: string
appId: string
conversationId: string
token: string
}
}
const Main: FC<IMainProps> = () => {
const { t } = useTranslation()
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
const hasSetAppConfig = APP_ID && API_KEY
/*
* app info
*/
const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
const [isUnknwonReason, setIsUnknwonReason] = useState<boolean>(false)
const [appId, setAppId] = useState<string>('')
const [isPublicVersion, setIsPublicVersion] = useState<boolean>(true)
const [siteInfo, setSiteInfo] = useState<SiteInfo | null>()
const [promptConfig, setPromptConfig] = useState<PromptConfig | null>(null)
const [inited, setInited] = useState<boolean>(false)
const [plan, setPlan] = useState<string>('basic') // basic/plus/pro
// in mobile, show sidebar by click button
const [isShowSidebar, { setTrue: showSidebar, setFalse: hideSidebar }] = useBoolean(false)
useEffect(() => {
if (siteInfo?.title) {
if (plan !== 'basic')
document.title = `${siteInfo.title}`
else
document.title = `${siteInfo.title} - Powered by LangGenius`
}
}, [siteInfo?.title, plan])
/*
* conversation info
*/
const {
conversationList,
setConversationList,
currConversationId,
setCurrConversationId,
getConversationIdFromStorage,
isNewConversation,
currConversationInfo,
currInputs,
newConversationInputs,
resetNewConversationInputs,
setCurrInputs,
setNewConversationInfo,
setExistConversationInfo,
} = useConversation()
const [conversationIdChangeBecauseOfNew, setConversationIdChangeBecauseOfNew, getConversationIdChangeBecauseOfNew] = useGetState(false)
const [isChatStarted, { setTrue: setChatStarted, setFalse: setChatNotStarted }] = useBoolean(false)
const handleStartChat = (inputs: Record<string, any>) => {
setCurrInputs(inputs)
setChatStarted()
// parse variables in introduction
setChatList(generateNewChatListWithOpenstatement('', inputs))
}
const hasSetInputs = (() => {
if (!isNewConversation)
return true
return isChatStarted
})()
const conversationName = currConversationInfo?.name || t('app.chat.newChatDefaultName') as string
const conversationIntroduction = currConversationInfo?.introduction || ''
const handleConversationSwitch = () => {
if (!inited)
return
if (!appId) {
// wait for appId
setTimeout(handleConversationSwitch, 100)
return
}
// update inputs of current conversation
let notSyncToStateIntroduction = ''
let notSyncToStateInputs: Record<string, any> | undefined | null = {}
if (!isNewConversation) {
const item = conversationList.find(item => item.id === currConversationId)
notSyncToStateInputs = item?.inputs || {}
setCurrInputs(notSyncToStateInputs as any)
notSyncToStateIntroduction = item?.introduction || ''
setExistConversationInfo({
name: item?.name || '',
introduction: notSyncToStateIntroduction,
})
}
else {
notSyncToStateInputs = newConversationInputs
setCurrInputs(notSyncToStateInputs)
}
// update chat list of current conversation
if (!isNewConversation && !conversationIdChangeBecauseOfNew && !isResponsing) {
fetchChatList(currConversationId).then((res: any) => {
const { data } = res
const newChatList: IChatItem[] = generateNewChatListWithOpenstatement(notSyncToStateIntroduction, notSyncToStateInputs)
data.forEach((item: any) => {
newChatList.push({
id: `question-${item.id}`,
content: item.query,
isAnswer: false,
})
newChatList.push({
id: item.id,
content: item.answer,
feedback: item.feedback,
isAnswer: true,
})
})
setChatList(newChatList)
})
}
if (isNewConversation && isChatStarted)
setChatList(generateNewChatListWithOpenstatement())
setControlFocus(Date.now())
}
useEffect(handleConversationSwitch, [currConversationId, inited])
const handleConversationIdChange = (id: string) => {
if (id === '-1') {
createNewChat()
setConversationIdChangeBecauseOfNew(true)
}
else {
setConversationIdChangeBecauseOfNew(false)
}
// trigger handleConversationSwitch
setCurrConversationId(id, appId)
hideSidebar()
}
/*
* chat info. chat is under conversation.
*/
const [chatList, setChatList, getChatList] = useGetState<IChatItem[]>([])
const chatListDomRef = useRef<HTMLDivElement>(null)
useEffect(() => {
// scroll to bottom
if (chatListDomRef.current)
chatListDomRef.current.scrollTop = chatListDomRef.current.scrollHeight
}, [chatList, currConversationId])
// user can not edit inputs if user had send message
const canEditInpus = !chatList.some(item => item.isAnswer === false) && isNewConversation
const createNewChat = () => {
// if new chat is already exist, do not create new chat
if (conversationList.some(item => item.id === '-1'))
return
setConversationList(produce(conversationList, (draft) => {
draft.unshift({
id: '-1',
name: t('app.chat.newChatDefaultName'),
inputs: newConversationInputs,
introduction: conversationIntroduction,
})
}))
}
// sometime introduction is not applied to state
const generateNewChatListWithOpenstatement = (introduction?: string, inputs?: Record<string, any> | null) => {
let caculatedIntroduction = introduction || conversationIntroduction || ''
const caculatedPromptVariables = inputs || currInputs || null
if (caculatedIntroduction && caculatedPromptVariables)
caculatedIntroduction = replaceVarWithValues(caculatedIntroduction, promptConfig?.prompt_variables || [], caculatedPromptVariables)
const openstatement = {
id: `${Date.now()}`,
content: caculatedIntroduction,
isAnswer: true,
feedbackDisabled: true,
isOpeningStatement: isPublicVersion,
}
if (caculatedIntroduction)
return [openstatement]
return []
}
// init
useEffect(() => {
if (!hasSetAppConfig) {
setAppUnavailable(true)
return
}
(async () => {
try {
const [appData, conversationData, appParams] = await Promise.all([fetchAppInfo(), fetchConversations(), fetchAppParams()])
const { app_id: appId, site: siteInfo, prompt_config, plan }: any = appData
setAppId(appId)
setPlan(plan)
const tempIsPublicVersion = !!prompt_config
setIsPublicVersion(tempIsPublicVersion)
const prompt_template = tempIsPublicVersion ? prompt_config.prompt_template : ''
// handle current conversation id
const { data: conversations } = conversationData as { data: ConversationItem[] }
const _conversationId = getConversationIdFromStorage(appId)
const isNotNewConversation = conversations.some(item => item.id === _conversationId)
// fetch new conversation info
const { variables: prompt_variables, introduction }: any = appParams
setLocaleOnClient(siteInfo.default_language, true)
setNewConversationInfo({
name: t('app.chat.newChatDefaultName'),
introduction,
})
setSiteInfo(siteInfo as SiteInfo)
setPromptConfig({
prompt_template,
prompt_variables,
} as PromptConfig)
setConversationList(conversations as ConversationItem[])
if (isNotNewConversation)
setCurrConversationId(_conversationId, appId, false)
setInited(true)
}
catch (e: any) {
if (e.status === 404) {
setAppUnavailable(true)
}
else {
setIsUnknwonReason(true)
setAppUnavailable(true)
}
}
})()
}, [])
const [isResponsing, { setTrue: setResponsingTrue, setFalse: setResponsingFalse }] = useBoolean(false)
const { notify } = Toast
const logError = (message: string) => {
notify({ type: 'error', message })
}
const checkCanSend = () => {
if (!currInputs || !promptConfig?.prompt_variables)
return true
const inputLens = Object.values(currInputs).length
const promptVariablesLens = promptConfig.prompt_variables.length
const emytyInput = inputLens < promptVariablesLens || Object.values(currInputs).find(v => !v)
if (emytyInput) {
logError(t('app.errorMessage.valueOfVarRequired'))
return false
}
return true
}
const [controlFocus, setControlFocus] = useState(0)
const handleSend = async (message: string) => {
if (isResponsing) {
notify({ type: 'info', message: t('app.errorMessage.waitForResponse') })
return
}
const data = {
inputs: currInputs,
query: message,
conversation_id: isNewConversation ? null : currConversationId,
}
// qustion
const questionId = `question-${Date.now()}`
const questionItem = {
id: questionId,
content: message,
isAnswer: false,
}
const placeholderAnswerId = `answer-placeholder-${Date.now()}`
const placeholderAnswerItem = {
id: placeholderAnswerId,
content: '...',
isAnswer: true,
}
const newList = [...getChatList(), questionItem, placeholderAnswerItem]
setChatList(newList)
// answer
const responseItem = {
id: `${Date.now()}`,
content: '',
isAnswer: true,
}
let tempNewConversationId = ''
setResponsingTrue()
sendChatMessage(data, {
onData: (message: string, isFirstMessage: boolean, { conversationId: newConversationId, messageId }: any) => {
responseItem.content = responseItem.content + message
responseItem.id = messageId
if (isFirstMessage && newConversationId)
tempNewConversationId = newConversationId
// closesure new list is outdated.
const newListWithAnswer = produce(
getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
(draft) => {
if (!draft.find(item => item.id === questionId))
draft.push({ ...questionItem })
draft.push({ ...responseItem })
})
setChatList(newListWithAnswer)
},
async onCompleted() {
setResponsingFalse()
if (!tempNewConversationId) {
return
}
if (getConversationIdChangeBecauseOfNew()) {
const { data: conversations }: any = await fetchConversations()
setConversationList(conversations as ConversationItem[])
}
setConversationIdChangeBecauseOfNew(false)
resetNewConversationInputs()
setChatNotStarted()
setCurrConversationId(tempNewConversationId, appId, true)
},
onError() {
setResponsingFalse()
// role back placeholder answer
setChatList(produce(getChatList(), (draft) => {
draft.splice(draft.findIndex(item => item.id === placeholderAnswerId), 1)
}))
},
})
}
const handleFeedback = async (messageId: string, feedback: Feedbacktype) => {
await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } })
const newChatList = chatList.map((item) => {
if (item.id === messageId) {
return {
...item,
feedback,
}
}
return item
})
setChatList(newChatList)
notify({ type: 'success', message: t('common.api.success') })
}
const renderSidebar = () => {
if (!appId || !siteInfo || !promptConfig)
return null
return (
<Sidebar
list={conversationList}
onCurrentIdChange={handleConversationIdChange}
currentId={currConversationId}
copyRight={siteInfo.copyright || siteInfo.title}
/>
)
}
if (appUnavailable)
return <AppUnavailable isUnknwonReason={isUnknwonReason} errMessage={!hasSetAppConfig && 'Please set APP_ID and API_KEY in config/index.tsx'} />
if (!appId || !siteInfo || !promptConfig)
return <Loading type='app' />
return (
<div className='bg-gray-100'>
<Header
title={siteInfo.title}
isMobile={isMobile}
onShowSideBar={showSidebar}
onCreateNewChat={() => handleConversationIdChange('-1')}
/>
<div className="flex rounded-t-2xl bg-white overflow-hidden">
{/* sidebar */}
{!isMobile && renderSidebar()}
{isMobile && isShowSidebar && (
<div className='fixed inset-0 z-50'
style={{ backgroundColor: 'rgba(35, 56, 118, 0.2)' }}
onClick={hideSidebar}
>
<div className='inline-block' onClick={e => e.stopPropagation()}>
{renderSidebar()}
</div>
</div>
)}
{/* main */}
<div className='flex-grow flex flex-col h-[calc(100vh_-_3rem)] overflow-y-auto'>
<ConfigSence
conversationName={conversationName}
hasSetInputs={hasSetInputs}
isPublicVersion={isPublicVersion}
siteInfo={siteInfo}
promptConfig={promptConfig}
onStartChat={handleStartChat}
canEidtInpus={canEditInpus}
savedInputs={currInputs as Record<string, any>}
onInputsChange={setCurrInputs}
plan={plan}
></ConfigSence>
{
hasSetInputs && (
<div className='relative grow h-[200px] pc:w-[794px] max-w-full mobile:w-full pb-[66px] mx-auto mb-3.5 overflow-hidden'>
<div className='h-full overflow-y-auto' ref={chatListDomRef}>
<Chat
chatList={chatList}
onSend={handleSend}
onFeedback={handleFeedback}
isResponsing={isResponsing}
checkCanSend={checkCanSend}
controlFocus={controlFocus}
/>
</div>
</div>)
}
</div>
</div>
</div>
)
}
export default React.memo(Main)
.card:hover {
background: linear-gradient(0deg, rgba(235, 245, 255, 0.4), rgba(235, 245, 255, 0.4)), #FFFFFF;
}
\ No newline at end of file
import React from 'react'
import { useTranslation } from 'react-i18next'
import s from './card.module.css'
type PropType = {
children: React.ReactNode
text?: string
}
function Card({ children, text }: PropType) {
const { t } = useTranslation()
return (
<div className={`${s.card} box-border w-full flex flex-col items-start px-4 py-3 rounded-lg border-solid border border-gray-200 cursor-pointer hover:border-primary-300`}>
<div className='text-gray-400 font-medium text-xs mb-2'>{text ?? t('app.chat.powerBy')}</div>
{children}
</div>
)
}
export default Card
import React from 'react'
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
import {
ChatBubbleOvalLeftEllipsisIcon,
PencilSquareIcon,
} from '@heroicons/react/24/outline'
import { ChatBubbleOvalLeftEllipsisIcon as ChatBubbleOvalLeftEllipsisSolidIcon } from '@heroicons/react/24/solid'
import Button from '@/app/components/base/button'
// import Card from './card'
import type { ConversationItem } from '@/types/app'
function classNames(...classes: any[]) {
return classes.filter(Boolean).join(' ')
}
const MAX_CONVERSATION_LENTH = 20
export type ISidebarProps = {
copyRight: string
currentId: string
onCurrentIdChange: (id: string) => void
list: ConversationItem[]
}
const Sidebar: FC<ISidebarProps> = ({
copyRight,
currentId,
onCurrentIdChange,
list,
}) => {
const { t } = useTranslation()
return (
<div
className="shrink-0 flex flex-col overflow-y-auto bg-white pc:w-[244px] tablet:w-[192px] mobile:w-[240px] border-r border-gray-200 tablet:h-[calc(100vh_-_3rem)] mobile:h-screen"
>
{list.length < MAX_CONVERSATION_LENTH && (
<div className="flex flex-shrink-0 p-4 !pb-0">
<Button
onClick={() => { onCurrentIdChange('-1') }}
className="group block w-full flex-shrink-0 !justify-start !h-9 text-primary-600 items-center text-sm">
<PencilSquareIcon className="mr-2 h-4 w-4" /> {t('app.chat.newChat')}
</Button>
</div>
)}
<nav className="mt-4 flex-1 space-y-1 bg-white p-4 !pt-0">
{list.map((item) => {
const isCurrent = item.id === currentId
const ItemIcon
= isCurrent ? ChatBubbleOvalLeftEllipsisSolidIcon : ChatBubbleOvalLeftEllipsisIcon
return (
<div
onClick={() => onCurrentIdChange(item.id)}
key={item.id}
className={classNames(
isCurrent
? 'bg-primary-50 text-primary-600'
: 'text-gray-700 hover:bg-gray-100 hover:text-gray-700',
'group flex items-center rounded-md px-2 py-2 text-sm font-medium cursor-pointer',
)}
>
<ItemIcon
className={classNames(
isCurrent
? 'text-primary-600'
: 'text-gray-400 group-hover:text-gray-500',
'mr-3 h-5 w-5 flex-shrink-0',
)}
aria-hidden="true"
/>
{item.name}
</div>
)
})}
</nav>
{/* <a className="flex flex-shrink-0 p-4" href="https://langgenius.ai/" target="_blank">
<Card><div className="flex flex-row items-center"><ChatBubbleOvalLeftEllipsisSolidIcon className="text-primary-600 h-6 w-6 mr-2" /><span>LangGenius</span></div></Card>
</a> */}
<div className="flex flex-shrink-0 pr-4 pb-4 pl-4">
<div className="text-gray-400 font-normal text-xs">© {copyRight} {(new Date()).getFullYear()}</div>
</div>
</div>
)
}
export default React.memo(Sidebar)
'use client'
import type { FC, ReactNode } from 'react'
import React from 'react'
import cn from 'classnames'
import { useTranslation } from 'react-i18next'
import s from './style.module.css'
import { StarIcon } from '@/app/components//welcome/massive-component'
import Button from '@/app/components/base/button'
export type ITemplateVarPanelProps = {
className?: string
header: ReactNode
children?: ReactNode | null
isFold: boolean
}
const TemplateVarPanel: FC<ITemplateVarPanelProps> = ({
className,
header,
children,
isFold,
}) => {
return (
<div className={cn(isFold ? 'border border-indigo-100' : s.boxShodow, className, 'rounded-xl ')}>
{/* header */}
<div
className={cn(isFold && 'rounded-b-xl', 'rounded-t-xl px-6 py-4 bg-indigo-25 text-xs')}
>
{header}
</div>
{/* body */}
{!isFold && children && (
<div className='rounded-b-xl p-6'>
{children}
</div>
)}
</div>
)
}
export const PanelTitle: FC<{ title: string; className?: string }> = ({
title,
className,
}) => {
return (
<div className={cn(className, 'flex items-center space-x-1 text-indigo-600')}>
<StarIcon />
<span className='text-xs'>{title}</span>
</div>
)
}
export const VarOpBtnGroup: FC<{ className?: string; onConfirm: () => void; onCancel: () => void }> = ({
className,
onConfirm,
onCancel,
}) => {
const { t } = useTranslation()
return (
<div className={cn(className, 'flex mt-3 space-x-2 mobile:ml-0 tablet:ml-[128px] text-sm')}>
<Button
className='text-sm'
type='primary'
onClick={onConfirm}
>
{t('common.operation.save')}
</Button>
<Button
className='text-sm'
onClick={onCancel}
>
{t('common.operation.cancel')}
</Button>
</div >
)
}
export default React.memo(TemplateVarPanel)
.boxShodow {
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
}
\ No newline at end of file
'use client'
import type { FC } from 'react'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import TemplateVarPanel, { PanelTitle, VarOpBtnGroup } from '../value-panel'
import s from './style.module.css'
import { AppInfo, ChatBtn, EditBtn, FootLogo, PromptTemplate } from './massive-component'
import type { PromptConfig, SiteInfo } from '@/types/app'
import Toast from '@/app/components/base/toast'
import Select from '@/app/components/base/select'
import { DEFAULT_VALUE_MAX_LEN } from '@/config'
// regex to match the {{}} and replace it with a span
const regex = /\{\{([^}]+)\}\}/g
export type IWelcomeProps = {
conversationName: string
hasSetInputs: boolean
isPublicVersion: boolean
siteInfo: SiteInfo
promptConfig: PromptConfig
onStartChat: (inputs: Record<string, any>) => void
canEidtInpus: boolean
savedInputs: Record<string, any>
onInputsChange: (inputs: Record<string, any>) => void
plan: string
}
const Welcome: FC<IWelcomeProps> = ({
conversationName,
hasSetInputs,
isPublicVersion,
siteInfo,
plan,
promptConfig,
onStartChat,
canEidtInpus,
savedInputs,
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)
return savedInputs
const res: Record<string, any> = {}
if (promptConfig) {
promptConfig.prompt_variables.forEach((item) => {
res[item.key] = ''
})
}
return res
})())
useEffect(() => {
if (!savedInputs) {
const res: Record<string, any> = {}
if (promptConfig) {
promptConfig.prompt_variables.forEach((item) => {
res[item.key] = ''
})
}
setInputs(res)
}
else {
setInputs(savedInputs)
}
}, [savedInputs])
const highLightPromoptTemplate = (() => {
if (!promptConfig)
return ''
const res = promptConfig.prompt_template.replace(regex, (match, p1) => {
return `<span class='text-gray-800 font-bold'>${inputs?.[p1] ? inputs?.[p1] : match}</span>`
})
return res
})()
const { notify } = Toast
const logError = (message: string) => {
notify({ type: 'error', message, duration: 3000 })
}
const renderHeader = () => {
return (
<div className='absolute top-0 left-0 right-0 flex items-center justify-between border-b border-gray-100 mobile:h-12 tablet:h-16 px-8 bg-white'>
<div className='text-gray-900'>{conversationName}</div>
</div>
)
}
const renderInputs = () => {
return (
<div className='space-y-3'>
{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}
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>
)
}
const canChat = () => {
const inputLens = Object.values(inputs).length
const promptVariablesLens = promptConfig.prompt_variables.length
const emytyInput = inputLens < promptVariablesLens || Object.values(inputs).filter(v => v === '').length > 0
if (emytyInput) {
logError(t('app.errorMessage.valueOfVarRequired'))
return false
}
return true
}
const handleChat = () => {
if (!canChat())
return
onStartChat(inputs)
}
const renderNoVarPanel = () => {
if (isPublicVersion) {
return (
<div>
<AppInfo siteInfo={siteInfo} />
<TemplateVarPanel
isFold={false}
header={
<>
<PanelTitle
title={t('app.chat.publicPromptConfigTitle')}
className='mb-1'
/>
<PromptTemplate html={highLightPromoptTemplate} />
</>
}
>
<ChatBtn onClick={handleChat} />
</TemplateVarPanel>
</div>
)
}
// private version
return (
<TemplateVarPanel
isFold={false}
header={
<AppInfo siteInfo={siteInfo} />
}
>
<ChatBtn onClick={handleChat} />
</TemplateVarPanel>
)
}
const renderVarPanel = () => {
return (
<TemplateVarPanel
isFold={false}
header={
<AppInfo siteInfo={siteInfo} />
}
>
{renderInputs()}
<ChatBtn
className='mt-3 mobile:ml-0 tablet:ml-[128px]'
onClick={handleChat}
/>
</TemplateVarPanel>
)
}
const renderVarOpBtnGroup = () => {
return (
<VarOpBtnGroup
onConfirm={() => {
if (!canChat())
return
onInputsChange(inputs)
setIsFold(true)
}}
onCancel={() => {
setInputs(savedInputs)
setIsFold(true)
}}
/>
)
}
const renderHasSetInputsPublic = () => {
if (!canEidtInpus) {
return (
<TemplateVarPanel
isFold={false}
header={
<>
<PanelTitle
title={t('app.chat.publicPromptConfigTitle')}
className='mb-1'
/>
<PromptTemplate html={highLightPromoptTemplate} />
</>
}
/>
)
}
return (
<TemplateVarPanel
isFold={isFold}
header={
<>
<PanelTitle
title={t('app.chat.publicPromptConfigTitle')}
className='mb-1'
/>
<PromptTemplate html={highLightPromoptTemplate} />
{isFold && (
<div className='flex items-center justify-between mt-3 border-t border-indigo-100 pt-4 text-xs text-indigo-600'>
<span className='text-gray-700'>{t('app.chat.configStatusDes')}</span>
<EditBtn onClick={() => setIsFold(false)} />
</div>
)}
</>
}
>
{renderInputs()}
{renderVarOpBtnGroup()}
</TemplateVarPanel>
)
}
const renderHasSetInputsPrivate = () => {
if (!canEidtInpus || !hasVar)
return null
return (
<TemplateVarPanel
isFold={isFold}
header={
<div className='flex items-center justify-between text-indigo-600'>
<PanelTitle
title={!isFold ? t('app.chat.privatePromptConfigTitle') : t('app.chat.configStatusDes')}
/>
{isFold && (
<EditBtn onClick={() => setIsFold(false)} />
)}
</div>
}
>
{renderInputs()}
{renderVarOpBtnGroup()}
</TemplateVarPanel>
)
}
const renderHasSetInputs = () => {
if (!isPublicVersion && !canEidtInpus || !hasVar)
return null
return (
<div
className='pt-[88px] mb-5'
>
{isPublicVersion ? renderHasSetInputsPublic() : renderHasSetInputsPrivate()}
</div>)
}
return (
<div className='relative mobile:min-h-[48px] tablet:min-h-[64px]'>
{hasSetInputs && renderHeader()}
<div className='mx-auto pc:w-[794px] max-w-full mobile:w-full px-3.5'>
{/* Has't set inputs */}
{
!hasSetInputs && (
<div className='mobile:pt-[72px] tablet:pt-[128px] pc:pt-[200px]'>
{hasVar
? (
renderVarPanel()
)
: (
renderNoVarPanel()
)}
</div>
)
}
{/* Has set inputs */}
{hasSetInputs && renderHasSetInputs()}
{/* foot */}
{!hasSetInputs && (
<div className='mt-4 flex justify-between items-center h-8 text-xs text-gray-400'>
{siteInfo.privacy_policy
? <div>{t('app.chat.privacyPolicyLeft')}
<a
className='text-gray-500'
href={siteInfo.privacy_policy}
target='_blank'>{t('app.chat.privacyPolicyMiddle')}</a>
{t('app.chat.privacyPolicyRight')}
</div>
: <div>
</div>}
{plan === 'basic' && <a className='flex items-center pr-3 space-x-3' href="https://langgenius.ai/" target="_blank">
<span className='uppercase'>{t('app.chat.powerBy')}</span>
<FootLogo />
</a>}
</div>
)}
</div>
</div >
)
}
export default React.memo(Welcome)
'use client'
import type { FC } from 'react'
import React from 'react'
import cn from 'classnames'
import { useTranslation } from 'react-i18next'
import {
PencilIcon,
} from '@heroicons/react/24/solid'
import s from './style.module.css'
import type { SiteInfo } from '@/types/app'
import Button from '@/app/components/base/button'
export const AppInfo: FC<{ siteInfo: SiteInfo }> = ({ siteInfo }) => {
const { t } = useTranslation()
return (
<div>
<div className='flex items-center py-2 text-xl font-medium text-gray-700 rounded-md'>👏 {t('app.common.welcome')} {siteInfo.title}</div>
<p className='text-sm text-gray-500'>{siteInfo.description}</p>
</div>
)
}
export const PromptTemplate: FC<{ html: string }> = ({ html }) => {
return (
<div
className={' box-border text-sm text-gray-700'}
dangerouslySetInnerHTML={{ __html: html }}
></div>
)
}
export const StarIcon = () => (
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.75 1C2.75 0.723858 2.52614 0.5 2.25 0.5C1.97386 0.5 1.75 0.723858 1.75 1V1.75H1C0.723858 1.75 0.5 1.97386 0.5 2.25C0.5 2.52614 0.723858 2.75 1 2.75H1.75V3.5C1.75 3.77614 1.97386 4 2.25 4C2.52614 4 2.75 3.77614 2.75 3.5V2.75H3.5C3.77614 2.75 4 2.52614 4 2.25C4 1.97386 3.77614 1.75 3.5 1.75H2.75V1Z" fill="#444CE7" />
<path d="M2.75 8.5C2.75 8.22386 2.52614 8 2.25 8C1.97386 8 1.75 8.22386 1.75 8.5V9.25H1C0.723858 9.25 0.5 9.47386 0.5 9.75C0.5 10.0261 0.723858 10.25 1 10.25H1.75V11C1.75 11.2761 1.97386 11.5 2.25 11.5C2.52614 11.5 2.75 11.2761 2.75 11V10.25H3.5C3.77614 10.25 4 10.0261 4 9.75C4 9.47386 3.77614 9.25 3.5 9.25H2.75V8.5Z" fill="#444CE7" />
<path d="M6.96667 1.32051C6.8924 1.12741 6.70689 1 6.5 1C6.29311 1 6.10759 1.12741 6.03333 1.32051L5.16624 3.57494C5.01604 3.96546 4.96884 4.078 4.90428 4.1688C4.8395 4.2599 4.7599 4.3395 4.6688 4.40428C4.578 4.46884 4.46546 4.51604 4.07494 4.66624L1.82051 5.53333C1.62741 5.60759 1.5 5.79311 1.5 6C1.5 6.20689 1.62741 6.39241 1.82051 6.46667L4.07494 7.33376C4.46546 7.48396 4.578 7.53116 4.6688 7.59572C4.7599 7.6605 4.8395 7.7401 4.90428 7.8312C4.96884 7.922 5.01604 8.03454 5.16624 8.42506L6.03333 10.6795C6.1076 10.8726 6.29311 11 6.5 11C6.70689 11 6.89241 10.8726 6.96667 10.6795L7.83376 8.42506C7.98396 8.03454 8.03116 7.922 8.09572 7.8312C8.1605 7.7401 8.2401 7.6605 8.3312 7.59572C8.422 7.53116 8.53454 7.48396 8.92506 7.33376L11.1795 6.46667C11.3726 6.39241 11.5 6.20689 11.5 6C11.5 5.79311 11.3726 5.60759 11.1795 5.53333L8.92506 4.66624C8.53454 4.51604 8.422 4.46884 8.3312 4.40428C8.2401 4.3395 8.1605 4.2599 8.09572 4.1688C8.03116 4.078 7.98396 3.96546 7.83376 3.57494L6.96667 1.32051Z" fill="#444CE7" />
</svg>
)
export const ChatBtn: FC<{ onClick: () => void; className?: string }> = ({
className,
onClick,
}) => {
const { t } = useTranslation()
return (
<Button
type='primary'
className={cn(className, `space-x-2 flex items-center ${s.customBtn}`)}
onClick={onClick}>
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M18 10.5C18 14.366 14.418 17.5 10 17.5C8.58005 17.506 7.17955 17.1698 5.917 16.52L2 17.5L3.338 14.377C2.493 13.267 2 11.934 2 10.5C2 6.634 5.582 3.5 10 3.5C14.418 3.5 18 6.634 18 10.5ZM7 9.5H5V11.5H7V9.5ZM15 9.5H13V11.5H15V9.5ZM9 9.5H11V11.5H9V9.5Z" fill="white" />
</svg>
{t('app.chat.startChat')}
</Button>
)
}
export const EditBtn = ({ className, onClick }: { className?: string; onClick: () => void }) => {
const { t } = useTranslation()
return (
<div
className={cn('px-2 flex space-x-1 items-center rounded-md cursor-pointer', className)}
onClick={onClick}
>
<PencilIcon className='w-3 h-3' />
<span>{t('common.operation.edit')}</span>
</div>
)
}
export const FootLogo = () => (
<svg width="68" height="17" viewBox="0 0 68 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.0001 8.49961C18.0001 13.1388 14.105 16.8996 9.3001 16.8996C8.14286 16.8996 7.03839 16.6815 6.0284 16.2854C5.8351 16.2096 5.73845 16.1717 5.66033 16.1548C5.5839 16.1383 5.52735 16.1322 5.44903 16.1322C5.36896 16.1322 5.28175 16.1462 5.10733 16.1743L1.66803 16.7278C1.30787 16.7857 1.12779 16.8147 0.997576 16.7608C0.883604 16.7136 0.792783 16.6259 0.743899 16.5158C0.688047 16.3901 0.71806 16.2162 0.778087 15.8685L1.3513 12.5478C1.38037 12.3794 1.39491 12.2952 1.3949 12.2179C1.39489 12.1423 1.38862 12.0877 1.37149 12.0139C1.35398 11.9384 1.31473 11.8451 1.23622 11.6585C0.826043 10.6833 0.600098 9.61694 0.600098 8.49961C0.600098 3.86042 4.49522 0.0996094 9.3001 0.0996094C14.105 0.0996094 18.0001 3.86042 18.0001 8.49961Z" fill="#155EEF" />
<path opacity="0.72" fillRule="evenodd" clipRule="evenodd" d="M15.5011 14.3919L8.75536 14.3919C8.40761 14.3919 8.08484 14.2113 7.90299 13.9148L4.86874 8.96915C4.82047 8.89048 4.79492 8.79998 4.79492 8.70768V7.1416L8.50747 13.1368C8.6442 13.3576 8.88542 13.4919 9.14511 13.4919H16.298C16.0543 13.8099 15.7879 14.1106 15.5011 14.3919Z" fill="white" />
<path d="M17.3614 11.6657C17.7734 10.6887 18.0003 9.61998 18.0003 8.50008C18.0003 8.32551 17.9948 8.15219 17.984 7.98026H11.9909C11.8167 7.98026 11.6551 7.88964 11.5643 7.74105L9.1534 3.79735C9.12615 3.75277 9.07767 3.72559 9.02542 3.72559H5.24077C5.04552 3.72559 4.92563 3.93939 5.02747 4.10598L9.35639 11.1872C9.53806 11.4844 9.86128 11.6657 10.2096 11.6657L17.3614 11.6657Z" fill="white" />
<rect x="22.8003" y="13.4912" width="12.5919" height="0.9" fill="#155EEF" />
<rect x="41.4927" y="13.4912" width="25.6982" height="0.9" fill="#155EEF" />
<path d="M24.3123 10.9124H25.9647V12.1868H22.8003V5.19922H24.3123V10.9124Z" fill="#155EEF" />
<path d="M29.3059 6.91642H30.5263V12.1868H29.3059V11.798C29.0827 12.1292 28.7119 12.2948 28.1935 12.2948H27.9667C27.0739 12.2948 26.6275 11.8196 26.6275 10.8692V8.23402C26.6275 7.28362 27.0739 6.80842 27.9667 6.80842H28.1935C28.7119 6.80842 29.0827 6.97402 29.3059 7.30522V6.91642ZM29.0791 11.042V8.06122C29.0791 8.00362 29.0575 7.95322 29.0143 7.91002C28.9783 7.86682 28.9315 7.84522 28.8739 7.84522H28.2799C28.2223 7.84522 28.1719 7.86682 28.1287 7.91002C28.0927 7.95322 28.0747 8.00362 28.0747 8.06122V11.042C28.0747 11.0996 28.0927 11.15 28.1287 11.1932C28.1719 11.2364 28.2223 11.258 28.2799 11.258H28.8739C28.9315 11.258 28.9783 11.2364 29.0143 11.1932C29.0575 11.15 29.0791 11.0996 29.0791 11.042Z" fill="#155EEF" />
<path d="M32.9579 12.1868H31.5107V6.91642H32.8283V7.18642C33.0515 6.93442 33.3899 6.80842 33.8435 6.80842H34.0703C34.9631 6.80842 35.4095 7.28362 35.4095 8.23402V12.1868H33.9623V8.06122C33.9623 8.00362 33.9407 7.95322 33.8975 7.91002C33.8615 7.86682 33.8147 7.84522 33.7571 7.84522H33.1631C33.1055 7.84522 33.0551 7.86682 33.0119 7.91002C32.9759 7.95322 32.9579 8.00362 32.9579 8.06122V12.1868Z" fill="#155EEF" />
<path d="M40.2927 6.91642V12.5864C40.2927 13.5368 39.8463 14.012 38.9535 14.012H37.8735C36.9807 14.012 36.5343 13.5368 36.5343 12.5864V12.4136H37.8411V12.7592C37.8411 12.8168 37.8591 12.8672 37.8951 12.9104C37.9383 12.9536 37.9887 12.9752 38.0463 12.9752H38.6403C38.6979 12.9752 38.7447 12.9536 38.7807 12.9104C38.8239 12.8672 38.8455 12.8168 38.8455 12.7592V11.6792C38.6223 11.852 38.3271 11.9384 37.9599 11.9384H37.7331C36.8403 11.9384 36.3939 11.4632 36.3939 10.5128V8.23402C36.3939 7.28362 36.8403 6.80842 37.7331 6.80842H37.9599C38.4783 6.80842 38.8491 6.97402 39.0723 7.30522V6.91642H40.2927ZM38.8455 10.6856V8.06122C38.8455 8.00362 38.8239 7.95322 38.7807 7.91002C38.7447 7.86682 38.6979 7.84522 38.6403 7.84522H38.0463C37.9887 7.84522 37.9383 7.86682 37.8951 7.91002C37.8591 7.95322 37.8411 8.00362 37.8411 8.06122V10.6856C37.8411 10.7432 37.8591 10.7936 37.8951 10.8368C37.9383 10.88 37.9887 10.9016 38.0463 10.9016H38.6403C38.6979 10.9016 38.7447 10.88 38.7807 10.8368C38.8239 10.7936 38.8455 10.7432 38.8455 10.6856Z" fill="#155EEF" />
<path d="M43.5339 9.71362V8.60122H45.5319V11.1716C45.5319 12.122 45.0855 12.5972 44.1927 12.5972H42.8319C41.9391 12.5972 41.4927 12.122 41.4927 11.1716V6.81922C41.4927 5.86882 41.9391 5.39362 42.8319 5.39362H44.1927C45.0855 5.39362 45.5319 5.86882 45.5319 6.81922V7.47802H44.0955V6.63562C44.0955 6.57802 44.0739 6.52762 44.0307 6.48442C43.9947 6.44122 43.9479 6.41962 43.8903 6.41962H43.2099C43.1523 6.41962 43.1019 6.44122 43.0587 6.48442C43.0227 6.52762 43.0047 6.57802 43.0047 6.63562V11.3444C43.0047 11.402 43.0227 11.4524 43.0587 11.4956C43.1019 11.5388 43.1523 11.5604 43.2099 11.5604H43.8903C43.9479 11.5604 43.9947 11.5388 44.0307 11.4956C44.0739 11.4524 44.0955 11.402 44.0955 11.3444V9.71362H43.5339Z" fill="#101828" />
<path d="M48.8709 7.11082C49.7637 7.11082 50.2101 7.58602 50.2101 8.53642V10.2428H47.7585V11.3444C47.7585 11.402 47.7765 11.4524 47.8125 11.4956C47.8557 11.5388 47.9061 11.5604 47.9637 11.5604H48.5577C48.6153 11.5604 48.6621 11.5388 48.6981 11.4956C48.7413 11.4524 48.7629 11.402 48.7629 11.3444V10.7612H50.2101V11.1716C50.2101 12.122 49.7637 12.5972 48.8709 12.5972H47.6505C46.7577 12.5972 46.3113 12.122 46.3113 11.1716V8.53642C46.3113 7.58602 46.7577 7.11082 47.6505 7.11082H48.8709ZM47.7585 9.29242H48.7629V8.32042C48.7629 8.26282 48.7413 8.21242 48.6981 8.16922C48.6621 8.12602 48.6153 8.10442 48.5577 8.10442H47.9637C47.9061 8.10442 47.8557 8.12602 47.8125 8.16922C47.7765 8.21242 47.7585 8.26282 47.7585 8.32042V9.29242Z" fill="#101828" />
<path d="M52.4519 12.4892H51.0047V7.21882H52.3223V7.48882C52.5455 7.23682 52.8839 7.11082 53.3375 7.11082H53.5643C54.4571 7.11082 54.9035 7.58602 54.9035 8.53642V12.4892H53.4563V8.36362C53.4563 8.30602 53.4347 8.25562 53.3915 8.21242C53.3555 8.16922 53.3087 8.14762 53.2511 8.14762H52.6571C52.5995 8.14762 52.5491 8.16922 52.5059 8.21242C52.4699 8.25562 52.4519 8.30602 52.4519 8.36362V12.4892Z" fill="#101828" />
<path d="M57.3567 6.56002C57.2055 6.71122 57.0219 6.78682 56.8059 6.78682C56.5899 6.78682 56.4027 6.71122 56.2443 6.56002C56.0859 6.40162 56.0067 6.21442 56.0067 5.99842C56.0067 5.77522 56.0859 5.58802 56.2443 5.43682C56.4027 5.27842 56.5899 5.19922 56.8059 5.19922C57.0219 5.19922 57.2055 5.27842 57.3567 5.43682C57.5151 5.58802 57.5943 5.77522 57.5943 5.99842C57.5943 6.22162 57.5151 6.40882 57.3567 6.56002ZM56.0823 12.4892V7.21882H57.5295V12.4892H56.0823Z" fill="#101828" />
<path d="M60.9973 7.21882H62.4445V12.4892H61.1269V12.2192C60.9037 12.4712 60.5653 12.5972 60.1117 12.5972H59.8849C58.9921 12.5972 58.5457 12.122 58.5457 11.1716V7.21882H59.9929V11.3444C59.9929 11.402 60.0109 11.4524 60.0469 11.4956C60.0901 11.5388 60.1405 11.5604 60.1981 11.5604H60.7921C60.8497 11.5604 60.8965 11.5388 60.9325 11.4956C60.9757 11.4524 60.9973 11.402 60.9973 11.3444V7.21882Z" fill="#101828" />
<path d="M65.9129 9.18442C66.8057 9.18442 67.2521 9.65962 67.2521 10.61V11.1716C67.2521 12.122 66.8057 12.5972 65.9129 12.5972H64.7141C63.8213 12.5972 63.3749 12.122 63.3749 11.1716V10.8368H64.7141V11.312C64.7141 11.3696 64.7321 11.42 64.7681 11.4632C64.8113 11.5064 64.8617 11.528 64.9193 11.528H65.8049C65.8625 11.528 65.9093 11.5064 65.9453 11.4632C65.9885 11.42 66.0101 11.3696 66.0101 11.312V10.664C66.0101 10.6064 65.9885 10.556 65.9453 10.5128C65.9093 10.4696 65.8625 10.448 65.8049 10.448H64.6925C63.7997 10.448 63.3533 9.97282 63.3533 9.02242V8.53642C63.3533 7.58602 63.7997 7.11082 64.6925 7.11082H65.8265C66.7193 7.11082 67.1657 7.58602 67.1657 8.53642V8.73082H65.8265V8.39602C65.8265 8.33842 65.8049 8.28802 65.7617 8.24482C65.7257 8.20162 65.6789 8.18002 65.6213 8.18002H64.8005C64.7429 8.18002 64.6925 8.20162 64.6493 8.24482C64.6133 8.28802 64.5953 8.33842 64.5953 8.39602V8.96842C64.5953 9.02602 64.6133 9.07642 64.6493 9.11962C64.6925 9.16282 64.7429 9.18442 64.8005 9.18442H65.9129Z" fill="#101828" />
</svg>
)
.boxShodow {
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
}
.bgGrayColor {
background-color: #F9FAFB;
}
.headerBg {
height: 3.5rem;
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.formLabel {
width: 120px;
margin-right: 8px;
}
.customBtn {
width: 136px;
}
\ No newline at end of file
import { getLocaleOnServer } from '@/i18n/server'
import './styles/globals.css'
import './styles/markdown.scss'
const LocaleLayout = ({
children,
}: {
children: React.ReactNode
}) => {
const locale = getLocaleOnServer()
return (
<html lang={locale ?? 'en'} className="h-full">
<body className="h-full">
<div className="overflow-x-auto">
<div className="w-screen h-screen min-w-[300px]">
{children}
</div>
</div>
</body>
</html>
)
}
export default LocaleLayout
import type { FC } from 'react'
import React from 'react'
import type { IMainProps } from '@/app/components'
import Main from '@/app/components'
const App: FC<IMainProps> = ({
params,
}: any) => {
return (
<Main params={params} />
)
}
export default React.memo(App)
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--max-width: 1100px;
--border-radius: 12px;
--font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono",
"Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro",
"Fira Mono", "Droid Sans Mono", "Courier New", monospace;
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
--primary-glow: conic-gradient(from 180deg at 50% 50%,
#16abff33 0deg,
#0885ff33 55deg,
#54d6ff33 120deg,
#0071ff33 160deg,
transparent 360deg);
--secondary-glow: radial-gradient(rgba(255, 255, 255, 1),
rgba(255, 255, 255, 0));
--tile-start-rgb: 239, 245, 249;
--tile-end-rgb: 228, 232, 233;
--tile-border: conic-gradient(#00000080,
#00000040,
#00000030,
#00000020,
#00000010,
#00000010,
#00000080);
--callout-rgb: 238, 240, 241;
--callout-border-rgb: 172, 175, 176;
--card-rgb: 180, 185, 188;
--card-border-rgb: 131, 134, 135;
}
/* @media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
--primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
--secondary-glow: linear-gradient(to bottom right,
rgba(1, 65, 255, 0),
rgba(1, 65, 255, 0),
rgba(1, 65, 255, 0.3));
--tile-start-rgb: 2, 13, 46;
--tile-end-rgb: 2, 5, 19;
--tile-border: conic-gradient(#ffffff80,
#ffffff40,
#ffffff30,
#ffffff20,
#ffffff10,
#ffffff10,
#ffffff80);
--callout-rgb: 20, 20, 20;
--callout-border-rgb: 108, 108, 108;
--card-rgb: 100, 100, 100;
--card-border-rgb: 200, 200, 200;
}
} */
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
}
body {
color: rgb(var(--foreground-rgb));
/* background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb)); */
}
a {
color: inherit;
text-decoration: none;
}
/* @media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
} */
/* CSS Utils */
.h1 {
padding-bottom: 1.5rem;
line-height: 1.5;
font-size: 1.125rem;
color: #111928;
}
.h2 {
font-size: 14px;
font-weight: 500;
color: #111928;
line-height: 1.5;
}
.link {
@apply text-blue-600 cursor-pointer hover:opacity-80 transition-opacity duration-200 ease-in-out;
}
.text-gradient {
background: linear-gradient(91.58deg, #2250F2 -29.55%, #0EBCF3 75.22%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
@mixin light {
color-scheme: light;
--color-prettylights-syntax-comment: #6e7781;
--color-prettylights-syntax-constant: #0550ae;
--color-prettylights-syntax-entity: #8250df;
--color-prettylights-syntax-storage-modifier-import: #24292f;
--color-prettylights-syntax-entity-tag: #116329;
--color-prettylights-syntax-keyword: #cf222e;
--color-prettylights-syntax-string: #0a3069;
--color-prettylights-syntax-variable: #953800;
--color-prettylights-syntax-brackethighlighter-unmatched: #82071e;
--color-prettylights-syntax-invalid-illegal-text: #f6f8fa;
--color-prettylights-syntax-invalid-illegal-bg: #82071e;
--color-prettylights-syntax-carriage-return-text: #f6f8fa;
--color-prettylights-syntax-carriage-return-bg: #cf222e;
--color-prettylights-syntax-string-regexp: #116329;
--color-prettylights-syntax-markup-list: #3b2300;
--color-prettylights-syntax-markup-heading: #0550ae;
--color-prettylights-syntax-markup-italic: #24292f;
--color-prettylights-syntax-markup-bold: #24292f;
--color-prettylights-syntax-markup-deleted-text: #82071e;
--color-prettylights-syntax-markup-deleted-bg: #ffebe9;
--color-prettylights-syntax-markup-inserted-text: #116329;
--color-prettylights-syntax-markup-inserted-bg: #dafbe1;
--color-prettylights-syntax-markup-changed-text: #953800;
--color-prettylights-syntax-markup-changed-bg: #ffd8b5;
--color-prettylights-syntax-markup-ignored-text: #eaeef2;
--color-prettylights-syntax-markup-ignored-bg: #0550ae;
--color-prettylights-syntax-meta-diff-range: #8250df;
--color-prettylights-syntax-brackethighlighter-angle: #57606a;
--color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;
--color-prettylights-syntax-constant-other-reference-link: #0a3069;
--color-fg-default: #24292f;
--color-fg-muted: #57606a;
--color-fg-subtle: #6e7781;
--color-canvas-default: transparent;
--color-canvas-subtle: #f6f8fa;
--color-border-default: #d0d7de;
--color-border-muted: hsla(210, 18%, 87%, 1);
--color-neutral-muted: rgba(175, 184, 193, 0.2);
--color-accent-fg: #0969da;
--color-accent-emphasis: #0969da;
--color-attention-subtle: #fff8c5;
--color-danger-fg: #cf222e;
}
.markdown-body {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
margin: 0;
color: #101828;
background-color: var(--color-canvas-default);
font-size: 14px;
font-weight: 400;
line-height: 1.5;
word-wrap: break-word;
}
.light {
@include light;
}
:root {
@include light;
}
@media (prefers-color-scheme: light) {
:root {
@include light;
}
}
.markdown-body .octicon {
display: inline-block;
fill: currentColor;
vertical-align: text-bottom;
}
.markdown-body h1:hover .anchor .octicon-link:before,
.markdown-body h2:hover .anchor .octicon-link:before,
.markdown-body h3:hover .anchor .octicon-link:before,
.markdown-body h4:hover .anchor .octicon-link:before,
.markdown-body h5:hover .anchor .octicon-link:before,
.markdown-body h6:hover .anchor .octicon-link:before {
width: 16px;
height: 16px;
content: " ";
display: inline-block;
background-color: currentColor;
-webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
}
.markdown-body details,
.markdown-body figcaption,
.markdown-body figure {
display: block;
}
.markdown-body summary {
display: list-item;
}
.markdown-body [hidden] {
display: none !important;
}
.markdown-body a {
background-color: transparent;
color: var(--color-accent-fg);
text-decoration: none;
}
.markdown-body abbr[title] {
border-bottom: none;
text-decoration: underline dotted;
}
.markdown-body b,
.markdown-body strong {
font-weight: var(--base-text-weight-semibold, 600);
}
.markdown-body dfn {
font-style: italic;
}
.markdown-body mark {
background-color: var(--color-attention-subtle);
color: var(--color-fg-default);
}
.markdown-body small {
font-size: 90%;
}
.markdown-body sub,
.markdown-body sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
.markdown-body sub {
bottom: -0.25em;
}
.markdown-body sup {
top: -0.5em;
}
.markdown-body img {
border-style: none;
max-width: 100%;
box-sizing: content-box;
background-color: var(--color-canvas-default);
}
.markdown-body code,
.markdown-body kbd,
.markdown-body pre,
.markdown-body samp {
font-family: monospace;
font-size: 1em;
}
.markdown-body figure {
margin: 1em 40px;
}
.markdown-body hr {
box-sizing: content-box;
overflow: hidden;
background: transparent;
border-bottom: 1px solid var(--color-border-muted);
height: 0.25em;
padding: 0;
margin: 24px 0;
background-color: var(--color-border-default);
border: 0;
}
.markdown-body input {
font: inherit;
margin: 0;
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
.markdown-body [type="button"],
.markdown-body [type="reset"],
.markdown-body [type="submit"] {
-webkit-appearance: button;
}
.markdown-body [type="checkbox"],
.markdown-body [type="radio"] {
box-sizing: border-box;
padding: 0;
}
.markdown-body [type="number"]::-webkit-inner-spin-button,
.markdown-body [type="number"]::-webkit-outer-spin-button {
height: auto;
}
.markdown-body [type="search"]::-webkit-search-cancel-button,
.markdown-body [type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
.markdown-body ::-webkit-input-placeholder {
color: inherit;
opacity: 0.54;
}
.markdown-body ::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
.markdown-body a:hover {
text-decoration: underline;
}
.markdown-body ::placeholder {
color: var(--color-fg-subtle);
opacity: 1;
}
.markdown-body hr::before {
display: table;
content: "";
}
.markdown-body hr::after {
display: table;
clear: both;
content: "";
}
.markdown-body table {
border-spacing: 0;
border-collapse: collapse;
display: block;
width: max-content;
max-width: 100%;
overflow: auto;
}
.markdown-body td,
.markdown-body th {
padding: 0;
}
.markdown-body details summary {
cursor: pointer;
}
.markdown-body details:not([open])>*:not(summary) {
display: none !important;
}
.markdown-body a:focus,
.markdown-body [role="button"]:focus,
.markdown-body input[type="radio"]:focus,
.markdown-body input[type="checkbox"]:focus {
outline: 2px solid var(--color-accent-fg);
outline-offset: -2px;
box-shadow: none;
}
.markdown-body a:focus:not(:focus-visible),
.markdown-body [role="button"]:focus:not(:focus-visible),
.markdown-body input[type="radio"]:focus:not(:focus-visible),
.markdown-body input[type="checkbox"]:focus:not(:focus-visible) {
outline: solid 1px transparent;
}
.markdown-body a:focus-visible,
.markdown-body [role="button"]:focus-visible,
.markdown-body input[type="radio"]:focus-visible,
.markdown-body input[type="checkbox"]:focus-visible {
outline: 2px solid var(--color-accent-fg);
outline-offset: -2px;
box-shadow: none;
}
.markdown-body a:not([class]):focus,
.markdown-body a:not([class]):focus-visible,
.markdown-body input[type="radio"]:focus,
.markdown-body input[type="radio"]:focus-visible,
.markdown-body input[type="checkbox"]:focus,
.markdown-body input[type="checkbox"]:focus-visible {
outline-offset: 0;
}
.markdown-body kbd {
display: inline-block;
padding: 3px 5px;
font: 11px ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas,
Liberation Mono, monospace;
line-height: 10px;
color: var(--color-fg-default);
vertical-align: middle;
background-color: var(--color-canvas-subtle);
border: solid 1px var(--color-neutral-muted);
border-bottom-color: var(--color-neutral-muted);
border-radius: 6px;
box-shadow: inset 0 -1px 0 var(--color-neutral-muted);
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
margin-top: 24px;
margin-bottom: 16px;
font-weight: var(--base-text-weight-semibold, 600);
line-height: 1.25;
}
.markdown-body p {
margin-top: 0;
margin-bottom: 10px;
}
.markdown-body blockquote {
margin: 0;
padding: 0 8px;
border-left: 2px solid #2970FF;
}
.markdown-body ul,
.markdown-body ol {
margin-top: 0;
margin-bottom: 0;
padding-left: 2em;
}
.markdown-body ol {
list-style: decimal;
}
.markdown-body ul {
list-style: disc;
}
.markdown-body ol ol,
.markdown-body ul ol {
list-style-type: lower-roman;
}
.markdown-body ul ul ol,
.markdown-body ul ol ol,
.markdown-body ol ul ol,
.markdown-body ol ol ol {
list-style-type: lower-alpha;
}
.markdown-body dd {
margin-left: 0;
}
.markdown-body tt,
.markdown-body code,
.markdown-body samp {
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas,
Liberation Mono, monospace;
font-size: 12px;
}
.markdown-body pre {
margin-top: 0;
margin-bottom: 0;
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas,
Liberation Mono, monospace;
font-size: 12px;
word-wrap: normal;
}
.markdown-body .octicon {
display: inline-block;
overflow: visible !important;
vertical-align: text-bottom;
fill: currentColor;
}
.markdown-body input::-webkit-outer-spin-button,
.markdown-body input::-webkit-inner-spin-button {
margin: 0;
-webkit-appearance: none;
appearance: none;
}
.markdown-body::before {
display: table;
content: "";
}
.markdown-body::after {
display: table;
clear: both;
content: "";
}
.markdown-body>*:first-child {
margin-top: 0 !important;
}
.markdown-body>*:last-child {
margin-bottom: 0 !important;
}
.markdown-body a:not([href]) {
color: inherit;
text-decoration: none;
}
.markdown-body .absent {
color: var(--color-danger-fg);
}
.markdown-body .anchor {
float: left;
padding-right: 4px;
margin-left: -20px;
line-height: 1;
}
.markdown-body .anchor:focus {
outline: none;
}
.markdown-body p,
.markdown-body blockquote,
.markdown-body ul,
.markdown-body ol,
.markdown-body dl,
.markdown-body table,
.markdown-body pre,
.markdown-body details {
margin-top: 0;
margin-bottom: 16px;
}
.markdown-body blockquote> :first-child {
margin-top: 0;
}
.markdown-body blockquote> :last-child {
margin-bottom: 0;
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
color: var(--color-fg-default);
vertical-align: middle;
visibility: hidden;
}
.markdown-body h1:hover .anchor,
.markdown-body h2:hover .anchor,
.markdown-body h3:hover .anchor,
.markdown-body h4:hover .anchor,
.markdown-body h5:hover .anchor,
.markdown-body h6:hover .anchor {
text-decoration: none;
}
.markdown-body h1:hover .anchor .octicon-link,
.markdown-body h2:hover .anchor .octicon-link,
.markdown-body h3:hover .anchor .octicon-link,
.markdown-body h4:hover .anchor .octicon-link,
.markdown-body h5:hover .anchor .octicon-link,
.markdown-body h6:hover .anchor .octicon-link {
visibility: visible;
}
.markdown-body h1 tt,
.markdown-body h1 code,
.markdown-body h2 tt,
.markdown-body h2 code,
.markdown-body h3 tt,
.markdown-body h3 code,
.markdown-body h4 tt,
.markdown-body h4 code,
.markdown-body h5 tt,
.markdown-body h5 code,
.markdown-body h6 tt,
.markdown-body h6 code {
padding: 0 0.2em;
font-size: inherit;
}
.markdown-body summary h1,
.markdown-body summary h2,
.markdown-body summary h3,
.markdown-body summary h4,
.markdown-body summary h5,
.markdown-body summary h6 {
display: inline-block;
}
.markdown-body summary h1 .anchor,
.markdown-body summary h2 .anchor,
.markdown-body summary h3 .anchor,
.markdown-body summary h4 .anchor,
.markdown-body summary h5 .anchor,
.markdown-body summary h6 .anchor {
margin-left: -40px;
}
.markdown-body summary h1,
.markdown-body summary h2 {
padding-bottom: 0;
border-bottom: 0;
}
.markdown-body ul.no-list,
.markdown-body ol.no-list {
padding: 0;
list-style-type: none;
}
.markdown-body ol[type="a"] {
list-style-type: lower-alpha;
}
.markdown-body ol[type="A"] {
list-style-type: upper-alpha;
}
.markdown-body ol[type="i"] {
list-style-type: lower-roman;
}
.markdown-body ol[type="I"] {
list-style-type: upper-roman;
}
.markdown-body ol[type="1"] {
list-style-type: decimal;
}
.markdown-body div>ol:not([type]) {
list-style-type: decimal;
}
.markdown-body ul ul,
.markdown-body ul ol,
.markdown-body ol ol,
.markdown-body ol ul {
margin-top: 0;
margin-bottom: 0;
}
.markdown-body li>p {
margin-top: 16px;
}
.markdown-body li+li {
margin-top: 0.25em;
}
.markdown-body dl {
padding: 0;
}
.markdown-body dl dt {
padding: 0;
margin-top: 16px;
font-size: 1em;
font-style: italic;
font-weight: var(--base-text-weight-semibold, 600);
}
.markdown-body dl dd {
padding: 0 16px;
margin-bottom: 16px;
}
.markdown-body table th {
font-weight: var(--base-text-weight-semibold, 600);
}
.markdown-body table th,
.markdown-body table td {
padding: 6px 13px;
border: 1px solid var(--color-border-default);
}
.markdown-body table tr {
background-color: var(--color-canvas-default);
border-top: 1px solid var(--color-border-muted);
}
.markdown-body table tr:nth-child(2n) {
background-color: var(--color-canvas-subtle);
}
.markdown-body table img {
background-color: transparent;
}
.markdown-body img[align="right"] {
padding-left: 20px;
}
.markdown-body img[align="left"] {
padding-right: 20px;
}
.markdown-body .emoji {
max-width: none;
vertical-align: text-top;
background-color: transparent;
}
.markdown-body span.frame {
display: block;
overflow: hidden;
}
.markdown-body span.frame>span {
display: block;
float: left;
width: auto;
padding: 7px;
margin: 13px 0 0;
overflow: hidden;
border: 1px solid var(--color-border-default);
}
.markdown-body span.frame span img {
display: block;
float: left;
}
.markdown-body span.frame span span {
display: block;
padding: 5px 0 0;
clear: both;
color: var(--color-fg-default);
}
.markdown-body span.align-center {
display: block;
overflow: hidden;
clear: both;
}
.markdown-body span.align-center>span {
display: block;
margin: 13px auto 0;
overflow: hidden;
text-align: center;
}
.markdown-body span.align-center span img {
margin: 0 auto;
text-align: center;
}
.markdown-body span.align-right {
display: block;
overflow: hidden;
clear: both;
}
.markdown-body span.align-right>span {
display: block;
margin: 13px 0 0;
overflow: hidden;
text-align: right;
}
.markdown-body span.align-right span img {
margin: 0;
text-align: right;
}
.markdown-body span.float-left {
display: block;
float: left;
margin-right: 13px;
overflow: hidden;
}
.markdown-body span.float-left span {
margin: 13px 0 0;
}
.markdown-body span.float-right {
display: block;
float: right;
margin-left: 13px;
overflow: hidden;
}
.markdown-body span.float-right>span {
display: block;
margin: 13px auto 0;
overflow: hidden;
text-align: right;
}
.markdown-body code,
.markdown-body tt {
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
white-space: break-spaces;
background-color: var(--color-neutral-muted);
border-radius: 6px;
}
.markdown-body code br,
.markdown-body tt br {
display: none;
}
.markdown-body del code {
text-decoration: inherit;
}
.markdown-body samp {
font-size: 85%;
}
.markdown-body pre code {
font-size: 100%;
}
.markdown-body pre>code {
padding: 0;
margin: 0;
word-break: normal;
white-space: pre;
background: transparent;
border: 0;
}
.markdown-body .highlight {
margin-bottom: 16px;
}
.markdown-body .highlight pre {
margin-bottom: 0;
word-break: normal;
}
.markdown-body .highlight pre,
.markdown-body pre {
padding: 16px;
background: #fff;
overflow: auto;
font-size: 85%;
line-height: 1.45;
border-radius: 6px;
}
.markdown-body pre code,
.markdown-body pre tt {
display: inline-block;
max-width: 100%;
padding: 0;
margin: 0;
overflow-x: scroll;
line-height: inherit;
word-wrap: normal;
background-color: transparent;
border: 0;
}
.markdown-body .csv-data td,
.markdown-body .csv-data th {
padding: 5px;
overflow: hidden;
font-size: 12px;
line-height: 1;
text-align: left;
white-space: nowrap;
}
.markdown-body .csv-data .blob-num {
padding: 10px 8px 9px;
text-align: right;
background: var(--color-canvas-default);
border: 0;
}
.markdown-body .csv-data tr {
border-top: 0;
}
.markdown-body .csv-data th {
font-weight: var(--base-text-weight-semibold, 600);
background: var(--color-canvas-subtle);
border-top: 0;
}
.markdown-body [data-footnote-ref]::before {
content: "[";
}
.markdown-body [data-footnote-ref]::after {
content: "]";
}
.markdown-body .footnotes {
font-size: 12px;
color: var(--color-fg-muted);
border-top: 1px solid var(--color-border-default);
}
.markdown-body .footnotes ol {
padding-left: 16px;
}
.markdown-body .footnotes ol ul {
display: inline-block;
padding-left: 16px;
margin-top: 16px;
}
.markdown-body .footnotes li {
position: relative;
}
.markdown-body .footnotes li:target::before {
position: absolute;
top: -8px;
right: -8px;
bottom: -8px;
left: -24px;
pointer-events: none;
content: "";
border: 2px solid var(--color-accent-emphasis);
border-radius: 6px;
}
.markdown-body .footnotes li:target {
color: var(--color-fg-default);
}
.markdown-body .footnotes .data-footnote-backref g-emoji {
font-family: monospace;
}
.markdown-body .pl-c {
color: var(--color-prettylights-syntax-comment);
}
.markdown-body .pl-c1,
.markdown-body .pl-s .pl-v {
color: var(--color-prettylights-syntax-constant);
}
.markdown-body .pl-e,
.markdown-body .pl-en {
color: var(--color-prettylights-syntax-entity);
}
.markdown-body .pl-smi,
.markdown-body .pl-s .pl-s1 {
color: var(--color-prettylights-syntax-storage-modifier-import);
}
.markdown-body .pl-ent {
color: var(--color-prettylights-syntax-entity-tag);
}
.markdown-body .pl-k {
color: var(--color-prettylights-syntax-keyword);
}
.markdown-body .pl-s,
.markdown-body .pl-pds,
.markdown-body .pl-s .pl-pse .pl-s1,
.markdown-body .pl-sr,
.markdown-body .pl-sr .pl-cce,
.markdown-body .pl-sr .pl-sre,
.markdown-body .pl-sr .pl-sra {
color: var(--color-prettylights-syntax-string);
}
.markdown-body .pl-v,
.markdown-body .pl-smw {
color: var(--color-prettylights-syntax-variable);
}
.markdown-body .pl-bu {
color: var(--color-prettylights-syntax-brackethighlighter-unmatched);
}
.markdown-body .pl-ii {
color: var(--color-prettylights-syntax-invalid-illegal-text);
background-color: var(--color-prettylights-syntax-invalid-illegal-bg);
}
.markdown-body .pl-c2 {
color: var(--color-prettylights-syntax-carriage-return-text);
background-color: var(--color-prettylights-syntax-carriage-return-bg);
}
.markdown-body .pl-sr .pl-cce {
font-weight: bold;
color: var(--color-prettylights-syntax-string-regexp);
}
.markdown-body .pl-ml {
color: var(--color-prettylights-syntax-markup-list);
}
.markdown-body .pl-mh,
.markdown-body .pl-mh .pl-en,
.markdown-body .pl-ms {
font-weight: bold;
color: var(--color-prettylights-syntax-markup-heading);
}
.markdown-body .pl-mi {
font-style: italic;
color: var(--color-prettylights-syntax-markup-italic);
}
.markdown-body .pl-mb {
font-weight: bold;
color: var(--color-prettylights-syntax-markup-bold);
}
.markdown-body .pl-md {
color: var(--color-prettylights-syntax-markup-deleted-text);
background-color: var(--color-prettylights-syntax-markup-deleted-bg);
}
.markdown-body .pl-mi1 {
color: var(--color-prettylights-syntax-markup-inserted-text);
background-color: var(--color-prettylights-syntax-markup-inserted-bg);
}
.markdown-body .pl-mc {
color: var(--color-prettylights-syntax-markup-changed-text);
background-color: var(--color-prettylights-syntax-markup-changed-bg);
}
.markdown-body .pl-mi2 {
color: var(--color-prettylights-syntax-markup-ignored-text);
background-color: var(--color-prettylights-syntax-markup-ignored-bg);
}
.markdown-body .pl-mdr {
font-weight: bold;
color: var(--color-prettylights-syntax-meta-diff-range);
}
.markdown-body .pl-ba {
color: var(--color-prettylights-syntax-brackethighlighter-angle);
}
.markdown-body .pl-sg {
color: var(--color-prettylights-syntax-sublimelinter-gutter-mark);
}
.markdown-body .pl-corl {
text-decoration: underline;
color: var(--color-prettylights-syntax-constant-other-reference-link);
}
.markdown-body g-emoji {
display: inline-block;
min-width: 1ch;
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 1em;
font-style: normal !important;
font-weight: var(--base-text-weight-normal, 400);
line-height: 1;
vertical-align: -0.075em;
}
.markdown-body g-emoji img {
width: 1em;
height: 1em;
}
.markdown-body .task-list-item {
list-style-type: none;
}
.markdown-body .task-list-item label {
font-weight: var(--base-text-weight-normal, 400);
}
.markdown-body .task-list-item.enabled label {
cursor: pointer;
}
.markdown-body .task-list-item+.task-list-item {
margin-top: 4px;
}
.markdown-body .task-list-item .handle {
display: none;
}
.markdown-body .task-list-item-checkbox {
margin: 0 0.2em 0.25em -1.4em;
vertical-align: middle;
}
.markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox {
margin: 0 -1.6em 0.25em 0.2em;
}
.markdown-body .contains-task-list {
position: relative;
}
.markdown-body .contains-task-list:hover .task-list-item-convert-container,
.markdown-body .contains-task-list:focus-within .task-list-item-convert-container {
display: block;
width: auto;
height: 24px;
overflow: visible;
clip: auto;
}
.markdown-body ::-webkit-calendar-picker-indicator {
filter: invert(50%);
}
\ No newline at end of file
export const APP_ID = ''
export const API_KEY = ''
export const APP_INFO = {
"app_id": APP_ID,
"site": {
"title": "Chat APP",
"description": null,
"copyright": null,
"privacy_policy": null,
"default_language": "zh-Hans",
"prompt_public": true
},
"prompt_config": {
"introduction": "Chat APP",
"prompt_template": "{{a}}", "prompt_variables": [{ "key": "a", "name": "a", "type": "string", "max_length": 48 }], "completion_params": { "max_token": 256, "temperature": 1, "top_p": 1, "presence_penalty": 0, "frequency_penalty": 0 }
}
}
export const API_PREFIX = '/api';
export const LOCALE_COOKIE_NAME = 'locale'
export const DEFAULT_VALUE_MAX_LEN = 48
'use client'
import React from 'react'
export enum MediaType {
mobile = 'mobile',
tablet = 'tablet',
pc = 'pc',
}
const useBreakpoints = () => {
const [width, setWidth] = React.useState(globalThis.innerWidth);
const media = (() => {
if (width <= 640) return MediaType.mobile;
if (width <= 768) return MediaType.tablet;
return MediaType.pc;
})();
React.useEffect(() => {
const handleWindowResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleWindowResize);
return () => window.removeEventListener("resize", handleWindowResize);
}, []);
return media;
}
export default useBreakpoints
\ No newline at end of file
import { useState } from 'react'
import type { ConversationItem } from '@/types/app'
import produce from 'immer'
const storageConversationIdKey = 'conversationIdInfo'
type ConversationInfoType = Omit<ConversationItem, 'inputs' | 'id'>
function useConversation() {
const [conversationList, setConversationList] = useState<ConversationItem[]>([])
const [currConversationId, doSetCurrConversationId] = useState<string>('-1')
// when set conversation id, we do not have set appId
const setCurrConversationId = (id: string, appId: string, isSetToLocalStroge = true, newConversationName = '') => {
doSetCurrConversationId(id)
if (isSetToLocalStroge && id !== '-1') {
// conversationIdInfo: {[appId1]: conversationId1, [appId2]: conversationId2}
const conversationIdInfo = globalThis.localStorage?.getItem(storageConversationIdKey) ? JSON.parse(globalThis.localStorage?.getItem(storageConversationIdKey) || '') : {}
conversationIdInfo[appId] = id
globalThis.localStorage?.setItem(storageConversationIdKey, JSON.stringify(conversationIdInfo))
}
}
const getConversationIdFromStorage = (appId: string) => {
const conversationIdInfo = globalThis.localStorage?.getItem(storageConversationIdKey) ? JSON.parse(globalThis.localStorage?.getItem(storageConversationIdKey) || '') : {}
const id = conversationIdInfo[appId]
return id
}
const isNewConversation = currConversationId === '-1'
// input can be updated by user
const [newConversationInputs, setNewConversationInputs] = useState<Record<string, any> | null>(null)
const resetNewConversationInputs = () => {
if (!newConversationInputs) return
setNewConversationInputs(produce(newConversationInputs, draft => {
Object.keys(draft).forEach(key => {
draft[key] = ''
})
}))
}
const [existConversationInputs, setExistConversationInputs] = useState<Record<string, any> | null>(null)
const currInputs = isNewConversation ? newConversationInputs : existConversationInputs
const setCurrInputs = isNewConversation ? setNewConversationInputs : setExistConversationInputs
// info is muted
const [newConversationInfo, setNewConversationInfo] = useState<ConversationInfoType | null>(null)
const [existConversationInfo, setExistConversationInfo] = useState<ConversationInfoType | null>(null)
const currConversationInfo = isNewConversation ? newConversationInfo : existConversationInfo
return {
conversationList,
setConversationList,
currConversationId,
setCurrConversationId,
getConversationIdFromStorage,
isNewConversation,
currInputs,
newConversationInputs,
existConversationInputs,
resetNewConversationInputs,
setCurrInputs,
currConversationInfo,
setNewConversationInfo,
setExistConversationInfo
}
}
export default useConversation;
\ No newline at end of file
import Cookies from 'js-cookie'
import type { Locale } from '.'
import { i18n } from '.'
import { LOCALE_COOKIE_NAME } from '@/config'
import { changeLanguage } from '@/i18n/i18next-config'
// same logic as server
export const getLocaleOnClient = (): Locale => {
return Cookies.get(LOCALE_COOKIE_NAME) as Locale || i18n.defaultLocale
}
export const setLocaleOnClient = (locale: Locale, notReload?: boolean) => {
Cookies.set(LOCALE_COOKIE_NAME, locale)
changeLanguage(locale)
if (!notReload) {
location.reload()
}
}
'use client'
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import commonEn from './lang/common.en'
import commonZh from './lang/common.zh'
import appEn from './lang/app.en'
import appZh from './lang/app.zh'
import { Locale } from '.'
const resources = {
'en': {
translation: {
common: commonEn,
app: appEn,
},
},
'zh-Hans': {
translation: {
common: commonZh,
app: appZh,
},
},
}
i18n.use(initReactI18next)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
lng: 'en',
fallbackLng: 'en',
// debug: true,
resources,
})
export const changeLanguage = (lan: Locale) => {
i18n.changeLanguage(lan)
}
export default i18n
import { createInstance } from 'i18next'
import resourcesToBackend from 'i18next-resources-to-backend'
import { initReactI18next } from 'react-i18next/initReactI18next'
import { Locale } from '.'
// https://locize.com/blog/next-13-app-dir-i18n/
const initI18next = async (lng: Locale, ns: string) => {
const i18nInstance = createInstance()
await i18nInstance
.use(initReactI18next)
.use(resourcesToBackend((language: string, namespace: string) => import(`./lang/${namespace}.${language}.ts`)))
.init({
lng: lng === 'zh-Hans' ? 'zh' : lng,
ns,
fallbackLng: 'en',
})
return i18nInstance
}
export async function useTranslation(lng: Locale, ns = '', options: Record<string, any> = {}) {
const i18nextInstance = await initI18next(lng, ns)
return {
t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix),
i18n: i18nextInstance
}
}
\ No newline at end of file
export const i18n = {
defaultLocale: 'en',
locales: ['en', 'zh-Hans'],
} as const
export type Locale = typeof i18n['locales'][number]
const translation = {
common: {
welcome: "Welcome to use",
appUnavailable: "App is unavailable",
appUnkonwError: "App is unavailable"
},
chat: {
newChat: "New chat",
newChatDefaultName: "New conversation",
openingStatementTitle: "Opening statement",
powerBy: "Powered by",
prompt: "Prompt",
privatePromptConfigTitle: "Conversation settings",
publicPromptConfigTitle: "Initial Prompt",
configStatusDes: "Before start, you can modify conversation settings",
configDisabled:
"Previous session settings have been used for this session.",
startChat: "Start Chat",
privacyPolicyLeft:
"Please read the ",
privacyPolicyMiddle:
"privacy policy",
privacyPolicyRight:
" provided by the app developer.",
},
errorMessage: {
valueOfVarRequired: "Variables value can not be empty",
waitForResponse:
"Please wait for the response to the previous message to complete.",
},
};
export default translation;
const translation = {
common: {
welcome: "欢迎使用",
appUnavailable: "应用不可用",
appUnkonwError: "应用不可用",
},
chat: {
newChat: "新对话",
newChatDefaultName: "新的对话",
openingStatementTitle: "对话开场白",
powerBy: "Powered by",
prompt: "提示词",
privatePromptConfigTitle: "对话设置",
publicPromptConfigTitle: "对话前提示词",
configStatusDes: "开始前,您可以修改对话设置",
configDisabled: "此次会话已使用上次会话表单",
startChat: "开始对话",
privacyPolicyLeft: "请阅读由该应用开发者提供的",
privacyPolicyMiddle: "隐私政策",
privacyPolicyRight: "。",
},
errorMessage: {
valueOfVarRequired: "变量值必填",
waitForResponse: "请等待上条信息响应完成",
},
};
export default translation;
const translation = {
api: {
success: 'Success',
saved: 'Saved',
create: 'Created',
},
operation: {
confirm: 'Confirm',
cancel: 'Cancel',
clear: 'Clear',
save: 'Save',
edit: 'Edit',
refresh: 'Restart',
search: 'Search',
send: 'Send',
lineBreak: 'Line break',
like: 'like',
dislike: 'dislike',
}
}
export default translation
const translation = {
api: {
success: '成功',
saved: '已保存',
create: '已创建',
},
operation: {
confirm: '确认',
cancel: '取消',
clear: '清空',
save: '保存',
edit: '编辑',
refresh: '重新开始',
search: '搜索',
send: '发送',
lineBreak: '换行',
like: '赞同',
dislike: '反对',
}
}
export default translation
import 'server-only'
import { cookies, headers } from 'next/headers'
import Negotiator from 'negotiator'
import { match } from '@formatjs/intl-localematcher'
import type { Locale } from '.'
import { i18n } from '.'
export const getLocaleOnServer = (): Locale => {
// @ts-expect-error locales are readonly
const locales: string[] = i18n.locales
let languages: string[] | undefined
// get locale from cookie
const localeCookie = cookies().get('locale')
languages = localeCookie?.value ? [localeCookie.value] : []
if (!languages.length) {
// Negotiator expects plain object so we need to transform headers
const negotiatorHeaders: Record<string, string> = {}
headers().forEach((value, key) => (negotiatorHeaders[key] = value))
// Use negotiator and intl-localematcher to get best locale
languages = new Negotiator({ headers: negotiatorHeaders }).languages()
}
// match locale
const matchedLocale = match(languages, locales, i18n.defaultLocale) as Locale
return matchedLocale
}
/** @type {import('next').NextConfig} */
const nextConfig = {
productionBrowserSourceMaps: false, // enable browser source map generation during the production build
// Configure pageExtensions to include md and mdx
pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
experimental: {
appDir: true,
},
// fix all before production. Now it slow the develop speed.
eslint: {
// Warning: This allows production builds to successfully complete even if
// your project has ESLint errors.
ignoreDuringBuilds: true,
},
typescript: {
// https://nextjs.org/docs/api-reference/next.config.js/ignoring-typescript-errors
ignoreBuildErrors: true,
}
}
module.exports = nextConfig
{
"name": "langgenius-gateway-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"fix": "next lint --fix"
},
"dependencies": {
"@formatjs/intl-localematcher": "^0.2.32",
"@headlessui/react": "^1.7.13",
"@heroicons/react": "^2.0.16",
"@mdx-js/loader": "^2.3.0",
"@mdx-js/react": "^2.3.0",
"@tailwindcss/line-clamp": "^0.4.2",
"@types/node": "18.15.0",
"@types/react": "18.0.28",
"@types/react-dom": "18.0.11",
"@types/react-syntax-highlighter": "^15.5.6",
"ahooks": "^3.7.5",
"axios": "^1.3.5",
"classnames": "^2.3.2",
"copy-to-clipboard": "^3.3.3",
"eslint": "8.36.0",
"eslint-config-next": "13.2.4",
"eventsource-parser": "^1.0.0",
"i18next": "^22.4.13",
"i18next-resources-to-backend": "^1.1.3",
"immer": "^9.0.19",
"js-cookie": "^3.0.1",
"negotiator": "^0.6.3",
"next": "13.2.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-error-boundary": "^4.0.2",
"react-headless-pagination": "^1.1.4",
"react-i18next": "^12.2.0",
"react-markdown": "^8.0.6",
"react-syntax-highlighter": "^15.5.0",
"react-tooltip": "5.8.3",
"rehype-katex": "^6.0.2",
"remark-breaks": "^3.0.2",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
"sass": "^1.61.0",
"scheduler": "^0.23.0",
"server-only": "^0.0.1",
"swr": "^2.1.0",
"typescript": "4.9.5",
"use-context-selector": "^1.4.1",
"uuid": "^9.0.0"
},
"devDependencies": {
"@antfu/eslint-config": "^0.36.0",
"@faker-js/faker": "^7.6.0",
"@tailwindcss/typography": "^0.5.9",
"@types/js-cookie": "^3.0.3",
"@types/negotiator": "^0.6.1",
"autoprefixer": "^10.4.14",
"eslint-plugin-react-hooks": "^4.6.0",
"miragejs": "^0.1.47",
"postcss": "^8.4.21",
"tailwindcss": "^3.2.7"
}
}
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
import { API_PREFIX } from '@/config'
import Toast from '@/app/components/base/toast'
const TIME_OUT = 100000
const ContentType = {
json: 'application/json',
stream: 'text/event-stream',
form: 'application/x-www-form-urlencoded; charset=UTF-8',
download: 'application/octet-stream', // for download
}
const baseOptions = {
method: 'GET',
mode: 'cors',
credentials: 'include', // always send cookies、HTTP Basic authentication.
headers: new Headers({
'Content-Type': ContentType.json,
}),
redirect: 'follow',
}
export type IOnDataMoreInfo = {
conversationId: string | undefined
messageId: string
errorMessage?: string
}
export type IOnData = (message: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => void
export type IOnCompleted = () => void
export type IOnError = (msg: string) => void
type IOtherOptions = {
needAllResponseContent?: boolean
onData?: IOnData // for stream
onError?: IOnError
onCompleted?: IOnCompleted // for stream
}
function unicodeToChar(text: string) {
return text.replace(/\\u[0-9a-f]{4}/g, (_match, p1) => {
return String.fromCharCode(parseInt(p1, 16))
})
}
const handleStream = (response: any, onData: IOnData, onCompleted?: IOnCompleted) => {
if (!response.ok)
throw new Error('Network response was not ok')
const reader = response.body.getReader()
const decoder = new TextDecoder('utf-8')
let buffer = ''
let bufferObj: any
let isFirstMessage = true
function read() {
reader.read().then((result: any) => {
if (result.done) {
onCompleted && onCompleted()
return
}
buffer += decoder.decode(result.value, { stream: true })
const lines = buffer.split('\n')
try {
lines.forEach((message) => {
if (!message) return
bufferObj = JSON.parse(message) // remove data: and parse as json
onData(unicodeToChar(bufferObj.answer), isFirstMessage, {
conversationId: bufferObj.conversation_id,
messageId: bufferObj.id,
})
isFirstMessage = false
})
buffer = lines[lines.length - 1]
} catch (e) {
onData('', false, {
conversationId: undefined,
messageId: '',
errorMessage: e + ''
})
return
}
read()
})
}
read()
}
const baseFetch = (url: string, fetchOptions: any, { needAllResponseContent }: IOtherOptions) => {
const options = Object.assign({}, baseOptions, fetchOptions)
let urlPrefix = API_PREFIX
let urlWithPrefix = `${urlPrefix}${url.startsWith('/') ? url : `/${url}`}`
const { method, params, body } = options
// handle query
if (method === 'GET' && params) {
const paramsArray: string[] = []
Object.keys(params).forEach(key =>
paramsArray.push(`${key}=${encodeURIComponent(params[key])}`),
)
if (urlWithPrefix.search(/\?/) === -1)
urlWithPrefix += `?${paramsArray.join('&')}`
else
urlWithPrefix += `&${paramsArray.join('&')}`
delete options.params
}
if (body)
options.body = JSON.stringify(body)
// Handle timeout
return Promise.race([
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('request timeout'))
}, TIME_OUT)
}),
new Promise((resolve, reject) => {
globalThis.fetch(urlWithPrefix, options)
.then((res: any) => {
const resClone = res.clone()
// Error handler
if (!/^(2|3)\d{2}$/.test(res.status)) {
const bodyJson = res.json()
switch (res.status) {
case 401: {
Toast.notify({ type: 'error', message: 'Invalid token' })
return
}
default:
// eslint-disable-next-line no-new
new Promise(() => {
bodyJson.then((data: any) => {
Toast.notify({ type: 'error', message: data.message })
})
})
}
return Promise.reject(resClone)
}
// handle delete api. Delete api not return content.
if (res.status === 204) {
resolve({ result: "success" })
return
}
// return data
const data = options.headers.get('Content-type') === ContentType.download ? res.blob() : res.json()
resolve(needAllResponseContent ? resClone : data)
})
.catch((err) => {
Toast.notify({ type: 'error', message: err })
reject(err)
})
}),
])
}
export const ssePost = (url: string, fetchOptions: any, {
onData, onCompleted, onError }: IOtherOptions) => {
const options = Object.assign({}, baseOptions, {
method: 'POST',
}, fetchOptions)
const urlPrefix = API_PREFIX
const urlWithPrefix = `${urlPrefix}${url.startsWith('/') ? url : `/${url}`}`
const { body } = options
if (body)
options.body = JSON.stringify(body)
globalThis.fetch(urlWithPrefix, options)
.then((res: any) => {
if (!/^(2|3)\d{2}$/.test(res.status)) {
// eslint-disable-next-line no-new
new Promise(() => {
res.json().then((data: any) => {
Toast.notify({ type: 'error', message: data.message || 'Server Error' })
})
})
onError?.('Server Error')
return
}
return handleStream(res, (str: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => {
if (moreInfo.errorMessage) {
Toast.notify({ type: 'error', message: moreInfo.errorMessage })
return
}
onData?.(str, isFirstMessage, moreInfo)
}, () => {
onCompleted?.()
})
}).catch((e) => {
Toast.notify({ type: 'error', message: e })
onError?.(e)
})
}
export const request = (url: string, options = {}, otherOptions?: IOtherOptions) => {
return baseFetch(url, options, otherOptions || {})
}
export const get = (url: string, options = {}, otherOptions?: IOtherOptions) => {
return request(url, Object.assign({}, options, { method: 'GET' }), otherOptions)
}
export const post = (url: string, options = {}, otherOptions?: IOtherOptions) => {
return request(url, Object.assign({}, options, { method: 'POST' }), otherOptions)
}
export const put = (url: string, options = {}, otherOptions?: IOtherOptions) => {
return request(url, Object.assign({}, options, { method: 'PUT' }), otherOptions)
}
export const del = (url: string, options = {}, otherOptions?: IOtherOptions) => {
return request(url, Object.assign({}, options, { method: 'DELETE' }), otherOptions)
}
import type { IOnCompleted, IOnData, IOnError } from './base'
import { get, post, ssePost } from './base'
import type { Feedbacktype } from '@/types/app'
export const sendChatMessage = async (body: Record<string, any>, { onData, onCompleted, onError }: {
onData: IOnData
onCompleted: IOnCompleted
onError: IOnError
}) => {
return ssePost('chat-messages', {
body: {
...body,
response_mode: 'streaming',
},
}, { onData, onCompleted, onError })
}
export const fetchAppInfo = async () => {
return get('site')
}
export const fetchConversations = async () => {
return get('conversations', { params: { limit: 20, first_id: '' } })
}
export const fetchChatList = async (conversationId: string) => {
return get('messages', { params: { conversation_id: conversationId, limit: 20, last_id: '' } })
}
// init value. wait for server update
export const fetchAppParams = async () => {
return get('parameters')
}
export const updateFeedback = async ({ url, body }: { url: string; body: Feedbacktype }) => {
return post(url, { body })
}
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./app/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
theme: {
typography: require('./typography'),
extend: {
colors: {
gray: {
50: '#F9FAFB',
100: '#F3F4F6',
200: '#E5E7EB',
300: '#D1D5DB',
400: '#9CA3AF',
500: '#6B7280',
700: '#374151',
800: '#1F2A37',
900: '#111928',
},
primary: {
50: '#EBF5FF',
100: '#E1EFFE',
200: '#C3DDFD',
300: '#A4CAFE',
600: '#1C64F2',
700: '#1A56DB',
},
blue: {
500: '#E1EFFE',
},
green: {
50: '#F3FAF7',
100: '#DEF7EC',
800: '#03543F',
},
yellow: {
100: '#FDF6B2',
800: '#723B13',
},
purple: {
50: '#F6F5FF',
},
indigo: {
25: '#F5F8FF',
100: '#E0EAFF',
600: '#444CE7'
}
},
screens: {
'mobile': '100px',
// => @media (min-width: 100px) { ... }
'tablet': '640px', // 391
// => @media (min-width: 600px) { ... }
'pc': '769px',
// => @media (min-width: 769px) { ... }
},
},
},
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/line-clamp'),
],
}
{
"compilerOptions": {
"target": "es2015",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": [
"./*"
]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"app/components/develop/Prose.jsx"
],
"exclude": [
"node_modules"
]
}
import { Locale } from '@/i18n'
export type PromptVariable = {
key: string,
name: string,
type: "string" | "number" | "select",
default?: string | number,
options?: string[]
max_length: number
}
export type PromptConfig = {
prompt_template: string,
prompt_variables: PromptVariable[],
}
export const MessageRatings = ['like', 'dislike', null] as const
export type MessageRating = typeof MessageRatings[number]
export type Feedbacktype = {
rating: MessageRating
content?: string | null
}
export type MessageMore = {
time: string
tokens: number
latency: number | string
}
export type IChatItem = {
id: string
content: string
/**
* Specific message type
*/
isAnswer: boolean
/**
* The user feedback result of this message
*/
feedback?: Feedbacktype
/**
* The admin feedback result of this message
*/
adminFeedback?: Feedbacktype
/**
* Whether to hide the feedback area
*/
feedbackDisabled?: boolean
/**
* More information about this message
*/
more?: MessageMore
isIntroduction?: boolean
useCurrentUserAvatar?: boolean
isOpeningStatement?: boolean
}
export type ResponseHolder = {}
export type ConversationItem = {
id: string
name: string
inputs: Record<string, any> | null
introduction: string,
}
export type SiteInfo = {
title: string
description: string
default_language: Locale
copyright?: string
privacy_policy?: string
}
\ No newline at end of file
module.exports = ({ theme }) => ({
DEFAULT: {
css: {
'--tw-prose-body': theme('colors.zinc.700'),
'--tw-prose-headings': theme('colors.zinc.900'),
'--tw-prose-links': theme('colors.emerald.500'),
'--tw-prose-links-hover': theme('colors.emerald.600'),
'--tw-prose-links-underline': theme('colors.emerald.500 / 0.3'),
'--tw-prose-bold': theme('colors.zinc.900'),
'--tw-prose-counters': theme('colors.zinc.500'),
'--tw-prose-bullets': theme('colors.zinc.300'),
'--tw-prose-hr': theme('colors.zinc.900 / 0.05'),
'--tw-prose-quotes': theme('colors.zinc.900'),
'--tw-prose-quote-borders': theme('colors.zinc.200'),
'--tw-prose-captions': theme('colors.zinc.500'),
'--tw-prose-code': theme('colors.zinc.900'),
'--tw-prose-code-bg': theme('colors.zinc.100'),
'--tw-prose-code-ring': theme('colors.zinc.300'),
'--tw-prose-th-borders': theme('colors.zinc.300'),
'--tw-prose-td-borders': theme('colors.zinc.200'),
'--tw-prose-invert-body': theme('colors.zinc.400'),
'--tw-prose-invert-headings': theme('colors.white'),
'--tw-prose-invert-links': theme('colors.emerald.400'),
'--tw-prose-invert-links-hover': theme('colors.emerald.500'),
'--tw-prose-invert-links-underline': theme('colors.emerald.500 / 0.3'),
'--tw-prose-invert-bold': theme('colors.white'),
'--tw-prose-invert-counters': theme('colors.zinc.400'),
'--tw-prose-invert-bullets': theme('colors.zinc.600'),
'--tw-prose-invert-hr': theme('colors.white / 0.05'),
'--tw-prose-invert-quotes': theme('colors.zinc.100'),
'--tw-prose-invert-quote-borders': theme('colors.zinc.700'),
'--tw-prose-invert-captions': theme('colors.zinc.400'),
'--tw-prose-invert-code': theme('colors.white'),
'--tw-prose-invert-code-bg': theme('colors.zinc.700 / 0.15'),
'--tw-prose-invert-code-ring': theme('colors.white / 0.1'),
'--tw-prose-invert-th-borders': theme('colors.zinc.600'),
'--tw-prose-invert-td-borders': theme('colors.zinc.700'),
// Base
color: 'var(--tw-prose-body)',
fontSize: theme('fontSize.sm')[0],
lineHeight: theme('lineHeight.7'),
// Layout
'> *': {
maxWidth: theme('maxWidth.2xl'),
marginLeft: 'auto',
marginRight: 'auto',
'@screen lg': {
maxWidth: theme('maxWidth.3xl'),
marginLeft: `calc(50% - min(50%, ${theme('maxWidth.lg')}))`,
marginRight: `calc(50% - min(50%, ${theme('maxWidth.lg')}))`,
},
},
// Text
p: {
marginTop: theme('spacing.6'),
marginBottom: theme('spacing.6'),
},
'[class~="lead"]': {
fontSize: theme('fontSize.base')[0],
...theme('fontSize.base')[1],
},
// Lists
ol: {
listStyleType: 'decimal',
marginTop: theme('spacing.5'),
marginBottom: theme('spacing.5'),
paddingLeft: '1.625rem',
},
'ol[type="A"]': {
listStyleType: 'upper-alpha',
},
'ol[type="a"]': {
listStyleType: 'lower-alpha',
},
'ol[type="A" s]': {
listStyleType: 'upper-alpha',
},
'ol[type="a" s]': {
listStyleType: 'lower-alpha',
},
'ol[type="I"]': {
listStyleType: 'upper-roman',
},
'ol[type="i"]': {
listStyleType: 'lower-roman',
},
'ol[type="I" s]': {
listStyleType: 'upper-roman',
},
'ol[type="i" s]': {
listStyleType: 'lower-roman',
},
'ol[type="1"]': {
listStyleType: 'decimal',
},
ul: {
listStyleType: 'disc',
marginTop: theme('spacing.5'),
marginBottom: theme('spacing.5'),
paddingLeft: '1.625rem',
},
li: {
marginTop: theme('spacing.2'),
marginBottom: theme('spacing.2'),
},
':is(ol, ul) > li': {
paddingLeft: theme('spacing[1.5]'),
},
'ol > li::marker': {
fontWeight: '400',
color: 'var(--tw-prose-counters)',
},
'ul > li::marker': {
color: 'var(--tw-prose-bullets)',
},
'> ul > li p': {
marginTop: theme('spacing.3'),
marginBottom: theme('spacing.3'),
},
'> ul > li > *:first-child': {
marginTop: theme('spacing.5'),
},
'> ul > li > *:last-child': {
marginBottom: theme('spacing.5'),
},
'> ol > li > *:first-child': {
marginTop: theme('spacing.5'),
},
'> ol > li > *:last-child': {
marginBottom: theme('spacing.5'),
},
'ul ul, ul ol, ol ul, ol ol': {
marginTop: theme('spacing.3'),
marginBottom: theme('spacing.3'),
},
// Horizontal rules
hr: {
borderColor: 'var(--tw-prose-hr)',
borderTopWidth: 1,
marginTop: theme('spacing.16'),
marginBottom: theme('spacing.16'),
maxWidth: 'none',
marginLeft: `calc(-1 * ${theme('spacing.4')})`,
marginRight: `calc(-1 * ${theme('spacing.4')})`,
'@screen sm': {
marginLeft: `calc(-1 * ${theme('spacing.6')})`,
marginRight: `calc(-1 * ${theme('spacing.6')})`,
},
'@screen lg': {
marginLeft: `calc(-1 * ${theme('spacing.8')})`,
marginRight: `calc(-1 * ${theme('spacing.8')})`,
},
},
// Quotes
blockquote: {
fontWeight: '500',
fontStyle: 'italic',
color: 'var(--tw-prose-quotes)',
borderLeftWidth: '0.25rem',
borderLeftColor: 'var(--tw-prose-quote-borders)',
quotes: '"\\201C""\\201D""\\2018""\\2019"',
marginTop: theme('spacing.8'),
marginBottom: theme('spacing.8'),
paddingLeft: theme('spacing.5'),
},
'blockquote p:first-of-type::before': {
content: 'open-quote',
},
'blockquote p:last-of-type::after': {
content: 'close-quote',
},
// Headings
h1: {
color: 'var(--tw-prose-headings)',
fontWeight: '700',
fontSize: theme('fontSize.2xl')[0],
...theme('fontSize.2xl')[1],
marginBottom: theme('spacing.2'),
},
h2: {
color: 'var(--tw-prose-headings)',
fontWeight: '600',
fontSize: theme('fontSize.lg')[0],
...theme('fontSize.lg')[1],
marginTop: theme('spacing.16'),
marginBottom: theme('spacing.2'),
},
h3: {
color: 'var(--tw-prose-headings)',
fontSize: theme('fontSize.base')[0],
...theme('fontSize.base')[1],
fontWeight: '600',
marginTop: theme('spacing.10'),
marginBottom: theme('spacing.2'),
},
// Media
'img, video, figure': {
marginTop: theme('spacing.8'),
marginBottom: theme('spacing.8'),
},
'figure > *': {
marginTop: '0',
marginBottom: '0',
},
figcaption: {
color: 'var(--tw-prose-captions)',
fontSize: theme('fontSize.xs')[0],
...theme('fontSize.xs')[1],
marginTop: theme('spacing.2'),
},
// Tables
table: {
width: '100%',
tableLayout: 'auto',
textAlign: 'left',
marginTop: theme('spacing.8'),
marginBottom: theme('spacing.8'),
lineHeight: theme('lineHeight.6'),
},
thead: {
borderBottomWidth: '1px',
borderBottomColor: 'var(--tw-prose-th-borders)',
},
'thead th': {
color: 'var(--tw-prose-headings)',
fontWeight: '600',
verticalAlign: 'bottom',
paddingRight: theme('spacing.2'),
paddingBottom: theme('spacing.2'),
paddingLeft: theme('spacing.2'),
},
'thead th:first-child': {
paddingLeft: '0',
},
'thead th:last-child': {
paddingRight: '0',
},
'tbody tr': {
borderBottomWidth: '1px',
borderBottomColor: 'var(--tw-prose-td-borders)',
},
'tbody tr:last-child': {
borderBottomWidth: '0',
},
'tbody td': {
verticalAlign: 'baseline',
},
tfoot: {
borderTopWidth: '1px',
borderTopColor: 'var(--tw-prose-th-borders)',
},
'tfoot td': {
verticalAlign: 'top',
},
':is(tbody, tfoot) td': {
paddingTop: theme('spacing.2'),
paddingRight: theme('spacing.2'),
paddingBottom: theme('spacing.2'),
paddingLeft: theme('spacing.2'),
},
':is(tbody, tfoot) td:first-child': {
paddingLeft: '0',
},
':is(tbody, tfoot) td:last-child': {
paddingRight: '0',
},
// Inline elements
a: {
color: 'var(--tw-prose-links)',
textDecoration: 'underline transparent',
fontWeight: '500',
transitionProperty: 'color, text-decoration-color',
transitionDuration: theme('transitionDuration.DEFAULT'),
transitionTimingFunction: theme('transitionTimingFunction.DEFAULT'),
'&:hover': {
color: 'var(--tw-prose-links-hover)',
textDecorationColor: 'var(--tw-prose-links-underline)',
},
},
':is(h1, h2, h3) a': {
fontWeight: 'inherit',
},
strong: {
color: 'var(--tw-prose-bold)',
fontWeight: '600',
},
':is(a, blockquote, thead th) strong': {
color: 'inherit',
},
code: {
color: 'var(--tw-prose-code)',
borderRadius: theme('borderRadius.lg'),
paddingTop: theme('padding.1'),
paddingRight: theme('padding[1.5]'),
paddingBottom: theme('padding.1'),
paddingLeft: theme('padding[1.5]'),
boxShadow: 'inset 0 0 0 1px var(--tw-prose-code-ring)',
backgroundColor: 'var(--tw-prose-code-bg)',
fontSize: theme('fontSize.2xs'),
},
':is(a, h1, h2, h3, blockquote, thead th) code': {
color: 'inherit',
},
'h2 code': {
fontSize: theme('fontSize.base')[0],
fontWeight: 'inherit',
},
'h3 code': {
fontSize: theme('fontSize.sm')[0],
fontWeight: 'inherit',
},
// Overrides
':is(h1, h2, h3) + *': {
marginTop: '0',
},
'> :first-child': {
marginTop: '0 !important',
},
'> :last-child': {
marginBottom: '0 !important',
},
},
},
invert: {
css: {
'--tw-prose-body': 'var(--tw-prose-invert-body)',
'--tw-prose-headings': 'var(--tw-prose-invert-headings)',
'--tw-prose-links': 'var(--tw-prose-invert-links)',
'--tw-prose-links-hover': 'var(--tw-prose-invert-links-hover)',
'--tw-prose-links-underline': 'var(--tw-prose-invert-links-underline)',
'--tw-prose-bold': 'var(--tw-prose-invert-bold)',
'--tw-prose-counters': 'var(--tw-prose-invert-counters)',
'--tw-prose-bullets': 'var(--tw-prose-invert-bullets)',
'--tw-prose-hr': 'var(--tw-prose-invert-hr)',
'--tw-prose-quotes': 'var(--tw-prose-invert-quotes)',
'--tw-prose-quote-borders': 'var(--tw-prose-invert-quote-borders)',
'--tw-prose-captions': 'var(--tw-prose-invert-captions)',
'--tw-prose-code': 'var(--tw-prose-invert-code)',
'--tw-prose-code-bg': 'var(--tw-prose-invert-code-bg)',
'--tw-prose-code-ring': 'var(--tw-prose-invert-code-ring)',
'--tw-prose-th-borders': 'var(--tw-prose-invert-th-borders)',
'--tw-prose-td-borders': 'var(--tw-prose-invert-td-borders)',
},
},
})
import { PromptVariable } from '@/types/app'
export function replaceVarWithValues(str: string, promptVariables: PromptVariable[], inputs: Record<string, any>) {
return str.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
const name = inputs[key]
if (name)
return name
const valueObj: PromptVariable | undefined = promptVariables.find(v => v.key === key)
return valueObj ? `{{${valueObj.key}}}` : match
})
}
\ No newline at end of file
const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'
export function randomString(length: number) {
let result = ''
for (let i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)]
return result
}
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