Commit cfd0c953 authored by Joel's avatar Joel

feat: lint code

parent 2e46f795
...@@ -25,4 +25,4 @@ ...@@ -25,4 +25,4 @@
], ],
"react-hooks/exhaustive-deps": "warn" "react-hooks/exhaustive-deps": "warn"
} }
} }
\ No newline at end of file
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
# misc # misc
.DS_Store .DS_Store
.vscode
*.pem *.pem
# debug # debug
......
...@@ -25,4 +25,4 @@ ...@@ -25,4 +25,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -29,4 +29,4 @@ ...@@ -29,4 +29,4 @@
"i18n/lang", "i18n/lang",
"app/api/messages" "app/api/messages"
] ]
} }
\ No newline at end of file
import { type NextRequest } from 'next/server' import { type NextRequest } from 'next/server'
import { getInfo, client } from '@/app/api/utils/common' import { client, getInfo } from '@/app/api/utils/common'
import { OpenAIStream } from '@/app/api/utils/stream' import { OpenAIStream } from '@/app/api/utils/stream'
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {
...@@ -8,10 +8,10 @@ export async function POST(request: NextRequest) { ...@@ -8,10 +8,10 @@ export async function POST(request: NextRequest) {
inputs, inputs,
query, query,
conversation_id: conversationId, conversation_id: conversationId,
response_mode: responseMode response_mode: responseMode,
} = body } = body
const { user } = getInfo(request); const { user } = getInfo(request)
const res = await client.createChatMessage(inputs, query, user, responseMode, conversationId) const res = await client.createChatMessage(inputs, query, user, responseMode, conversationId)
const stream = await OpenAIStream(res as any) const stream = await OpenAIStream(res as any)
return new Response(stream as any) return new Response(stream as any)
} }
\ No newline at end of file
import { type NextRequest } from 'next/server' import { type NextRequest } from 'next/server'
import { NextResponse } from 'next/server' import { NextResponse } from 'next/server'
import { getInfo, setSession, client } from '@/app/api/utils/common' import { client, getInfo, setSession } from '@/app/api/utils/common'
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
const { sessionId, user } = getInfo(request); const { sessionId, user } = getInfo(request)
const { data }: any = await client.getConversations(user); const { data }: any = await client.getConversations(user)
return NextResponse.json(data, { return NextResponse.json(data, {
headers: setSession(sessionId) headers: setSession(sessionId),
}) })
} }
\ No newline at end of file
import { type NextRequest } from 'next/server' import { type NextRequest } from 'next/server'
import { NextResponse } from 'next/server' import { NextResponse } from 'next/server'
import { getInfo, client } from '@/app/api/utils/common' import { client, getInfo } from '@/app/api/utils/common'
export async function POST(request: NextRequest, { params }: { export async function POST(request: NextRequest, { params }: {
params: { messageId: string } params: { messageId: string }
}) { }) {
const body = await request.json() const body = await request.json()
const { const {
rating rating,
} = body } = body
const { messageId } = params const { messageId } = params
const { user } = getInfo(request); const { user } = getInfo(request)
const { data } = await client.messageFeedback(messageId, rating, user) const { data } = await client.messageFeedback(messageId, rating, user)
return NextResponse.json(data) return NextResponse.json(data)
} }
import { type NextRequest } from 'next/server' import { type NextRequest } from 'next/server'
import { NextResponse } from 'next/server' import { NextResponse } from 'next/server'
import { getInfo, setSession, client } from '@/app/api/utils/common' import { client, getInfo, setSession } from '@/app/api/utils/common'
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
const { sessionId, user } = getInfo(request); const { sessionId, user } = getInfo(request)
const { searchParams } = new URL(request.url); const { searchParams } = new URL(request.url)
const conversationId = searchParams.get('conversation_id') const conversationId = searchParams.get('conversation_id')
const { data }: any = await client.getConversationMessages(user, conversationId as string); const { data }: any = await client.getConversationMessages(user, conversationId as string)
return NextResponse.json(data, { return NextResponse.json(data, {
headers: setSession(sessionId) headers: setSession(sessionId),
}) })
} }
\ No newline at end of file
import { type NextRequest } from 'next/server' import { type NextRequest } from 'next/server'
import { NextResponse } from 'next/server' import { NextResponse } from 'next/server'
import { getInfo, setSession, client } from '@/app/api/utils/common' import { client, getInfo, setSession } from '@/app/api/utils/common'
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
const { sessionId, user } = getInfo(request); const { sessionId, user } = getInfo(request)
const { data } = await client.getApplicationParameters(user); const { data } = await client.getApplicationParameters(user)
return NextResponse.json(data as object, { return NextResponse.json(data as object, {
headers: setSession(sessionId) headers: setSession(sessionId),
}) })
} }
\ No newline at end of file
import { type NextRequest } from 'next/server' import { type NextRequest } from 'next/server'
import { APP_ID, API_KEY, API_URL } from '@/config'
import { ChatClient } from 'dify-client' import { ChatClient } from 'dify-client'
import { v4 } from 'uuid' import { v4 } from 'uuid'
import { API_KEY, API_URL, APP_ID } from '@/config'
const userPrefix = `user_${APP_ID}:`; const userPrefix = `user_${APP_ID}:`
export const getInfo = (request: NextRequest) => { export const getInfo = (request: NextRequest) => {
const sessionId = request.cookies.get('session_id')?.value || v4(); const sessionId = request.cookies.get('session_id')?.value || v4()
const user = userPrefix + sessionId; const user = userPrefix + sessionId
return { return {
sessionId, sessionId,
user user,
} }
} }
...@@ -18,4 +18,4 @@ export const setSession = (sessionId: string) => { ...@@ -18,4 +18,4 @@ export const setSession = (sessionId: string) => {
return { 'Set-Cookie': `session_id=${sessionId}` } return { 'Set-Cookie': `session_id=${sessionId}` }
} }
export const client = new ChatClient(API_KEY, API_URL ? API_URL : undefined) export const client = new ChatClient(API_KEY, API_URL || undefined)
export async function OpenAIStream(res: { body: any }) { export async function OpenAIStream(res: { body: any }) {
const reader = res.body.getReader(); const reader = res.body.getReader()
const stream = new ReadableStream({ const stream = new ReadableStream({
// https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams // 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 // https://github.com/whichlight/chatgpt-api-streaming/blob/master/pages/api/OpenAIStream.ts
start(controller) { start(controller) {
return pump(); return pump()
function pump() { function pump() {
return reader.read().then(({ done, value }: any) => { return reader.read().then(({ done, value }: any) => {
// When no more data needs to be consumed, close the stream // When no more data needs to be consumed, close the stream
if (done) { if (done) {
controller.close(); controller.close()
return; return
} }
// Enqueue the next data chunk into our target stream // Enqueue the next data chunk into our target stream
controller.enqueue(value); controller.enqueue(value)
return pump(); return pump()
}); })
} }
}, },
}); })
return stream; return stream
} }
\ No newline at end of file
...@@ -14,9 +14,8 @@ const AppUnavailable: FC<IAppUnavailableProps> = ({ ...@@ -14,9 +14,8 @@ const AppUnavailable: FC<IAppUnavailableProps> = ({
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
let message = errMessage let message = errMessage
if (!errMessage) { if (!errMessage)
message = (isUnknwonReason ? t('app.common.appUnkonwError') : t('app.common.appUnavailable')) as string message = (isUnknwonReason ? t('app.common.appUnkonwError') : t('app.common.appUnavailable')) as string
}
return ( return (
<div className='flex items-center justify-center w-screen h-screen'> <div className='flex items-center justify-center w-screen h-screen'>
......
...@@ -19,6 +19,7 @@ const AutoHeightTextarea = forwardRef( ...@@ -19,6 +19,7 @@ const AutoHeightTextarea = forwardRef(
{ value, onChange, placeholder, className, minHeight = 36, maxHeight = 96, autoFocus, controlFocus, onKeyDown, onKeyUp }: IProps, { value, onChange, placeholder, className, minHeight = 36, maxHeight = 96, autoFocus, controlFocus, onKeyDown, onKeyUp }: IProps,
outerRef: any, outerRef: any,
) => { ) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const ref = outerRef || useRef<HTMLTextAreaElement>(null) const ref = outerRef || useRef<HTMLTextAreaElement>(null)
const doFocus = () => { const doFocus = () => {
......
...@@ -18,7 +18,7 @@ export function Markdown(props: { content: string }) { ...@@ -18,7 +18,7 @@ export function Markdown(props: { content: string }) {
components={{ components={{
code({ node, inline, className, children, ...props }) { code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || '') const match = /language-(\w+)/.exec(className || '')
return !inline && match return (!inline && match)
? ( ? (
<SyntaxHighlighter <SyntaxHighlighter
{...props} {...props}
......
'use client' 'use client'
import type { FC, } from 'react' import type { FC } from 'react'
import React, { useEffect, useRef } from 'react' import React, { useEffect, useRef } from 'react'
import cn from 'classnames' import cn from 'classnames'
import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline' import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import s from './style.module.css' import s from './style.module.css'
import LoadingAnim from './loading-anim'
import { randomString } from '@/utils/string' import { randomString } from '@/utils/string'
import type { Feedbacktype, MessageRating } from '@/types/app' import type { Feedbacktype, MessageRating } from '@/types/app'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import Toast from '@/app/components/base/toast' import Toast from '@/app/components/base/toast'
import AutoHeightTextarea from '@/app/components/base/auto-height-textarea' import AutoHeightTextarea from '@/app/components/base/auto-height-textarea'
import { Markdown } from '@/app/components/base/markdown' import { Markdown } from '@/app/components/base/markdown'
import LoadingAnim from './loading-anim'
export type FeedbackFunc = (messageId: string, feedback: Feedbacktype) => Promise<any> export type FeedbackFunc = (messageId: string, feedback: Feedbacktype) => Promise<any>
...@@ -168,8 +168,8 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, onFeedback, ...@@ -168,8 +168,8 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, onFeedback,
<div key={id}> <div key={id}>
<div className='flex items-start'> <div className='flex items-start'>
<div className={`${s.answerIcon} w-10 h-10 shrink-0`}> <div className={`${s.answerIcon} w-10 h-10 shrink-0`}>
{isResponsing && {isResponsing
<div className={s.typeingIcon}> && <div className={s.typeingIcon}>
<LoadingAnim type='avatar' /> <LoadingAnim type='avatar' />
</div> </div>
} }
...@@ -183,13 +183,15 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, onFeedback, ...@@ -183,13 +183,15 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, onFeedback,
<div className='text-xs text-gray-500'>{t('app.chat.openingStatementTitle')}</div> <div className='text-xs text-gray-500'>{t('app.chat.openingStatementTitle')}</div>
</div> </div>
)} )}
{(isResponsing && !content) ? ( {(isResponsing && !content)
<div className='flex items-center justify-center w-6 h-5'> ? (
<LoadingAnim type='text' /> <div className='flex items-center justify-center w-6 h-5'>
</div> <LoadingAnim type='text' />
) : ( </div>
<Markdown content={content} /> )
)} : (
<Markdown content={content} />
)}
</div> </div>
<div className='absolute top-[-14px] right-[-14px] flex flex-row justify-end gap-1'> <div className='absolute top-[-14px] right-[-14px] flex flex-row justify-end gap-1'>
{!feedbackDisabled && !item.feedbackDisabled && renderItemOperation()} {!feedbackDisabled && !item.feedbackDisabled && renderItemOperation()}
...@@ -282,9 +284,8 @@ const Chat: FC<IChatProps> = ({ ...@@ -282,9 +284,8 @@ const Chat: FC<IChatProps> = ({
if (e.code === 'Enter') { if (e.code === 'Enter') {
e.preventDefault() e.preventDefault()
// prevent send message when using input method enter // prevent send message when using input method enter
if (!e.shiftKey && !isUseInputMethod.current) { if (!e.shiftKey && !isUseInputMethod.current)
handleSend() handleSend()
}
} }
} }
......
'use client' 'use client'
import React, { FC } from 'react' import type { FC } from 'react'
import React from 'react'
import s from './style.module.css' import s from './style.module.css'
export interface ILoaidingAnimProps { export type ILoaidingAnimProps = {
type: 'text' | 'avatar' type: 'text' | 'avatar'
} }
const LoaidingAnim: FC<ILoaidingAnimProps> = ({ const LoaidingAnim: FC<ILoaidingAnimProps> = ({
type type,
}) => { }) => {
return ( return (
<div className={`${s['dot-flashing']} ${s[type]}`}></div> <div className={`${s['dot-flashing']} ${s[type]}`}></div>
......
/* eslint-disable @typescript-eslint/no-use-before-define */
'use client' 'use client'
import type { FC } from 'react' import type { FC } from 'react'
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
...@@ -10,15 +11,14 @@ import Sidebar from '@/app/components/sidebar' ...@@ -10,15 +11,14 @@ import Sidebar from '@/app/components/sidebar'
import ConfigSence from '@/app/components/config-scence' import ConfigSence from '@/app/components/config-scence'
import Header from '@/app/components/header' import Header from '@/app/components/header'
import { fetchAppParams, fetchChatList, fetchConversations, sendChatMessage, updateFeedback } from '@/service' import { fetchAppParams, fetchChatList, fetchConversations, sendChatMessage, updateFeedback } from '@/service'
import type { ConversationItem, Feedbacktype, IChatItem, PromptConfig, AppInfo } from '@/types/app' import type { ConversationItem, Feedbacktype, IChatItem, PromptConfig } from '@/types/app'
import Chat from '@/app/components/chat' import Chat from '@/app/components/chat'
import { setLocaleOnClient } from '@/i18n/client' import { setLocaleOnClient } from '@/i18n/client'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Loading from '@/app/components/base/loading' import Loading from '@/app/components/base/loading'
import { replaceVarWithValues } from '@/utils/prompt' import { replaceVarWithValues, userInputsFormToPromptVariables } from '@/utils/prompt'
import AppUnavailable from '@/app/components/app-unavailable' import AppUnavailable from '@/app/components/app-unavailable'
import { APP_ID, API_KEY, APP_INFO, isShowPrompt, promptTemplate } from '@/config' import { API_KEY, APP_ID, APP_INFO, isShowPrompt, promptTemplate } from '@/config'
import { userInputsFormToPromptVariables } from '@/utils/prompt'
const Main: FC = () => { const Main: FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
...@@ -37,9 +37,8 @@ const Main: FC = () => { ...@@ -37,9 +37,8 @@ const Main: FC = () => {
const [isShowSidebar, { setTrue: showSidebar, setFalse: hideSidebar }] = useBoolean(false) const [isShowSidebar, { setTrue: showSidebar, setFalse: hideSidebar }] = useBoolean(false)
useEffect(() => { useEffect(() => {
if (APP_INFO?.title) { if (APP_INFO?.title)
document.title = `${APP_INFO.title} - Powered by Dify` document.title = `${APP_INFO.title} - Powered by Dify`
}
}, [APP_INFO?.title]) }, [APP_INFO?.title])
/* /*
...@@ -318,9 +317,9 @@ const Main: FC = () => { ...@@ -318,9 +317,9 @@ const Main: FC = () => {
}, },
async onCompleted() { async onCompleted() {
setResponsingFalse() setResponsingFalse()
if (!tempNewConversationId) { if (!tempNewConversationId)
return return
}
if (getConversationIdChangeBecauseOfNew()) { if (getConversationIdChangeBecauseOfNew()) {
const { data: conversations }: any = await fetchConversations() const { data: conversations }: any = await fetchConversations()
setConversationList(conversations as ConversationItem[]) setConversationList(conversations as ConversationItem[])
......
...@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next' ...@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'
import TemplateVarPanel, { PanelTitle, VarOpBtnGroup } from '../value-panel' import TemplateVarPanel, { PanelTitle, VarOpBtnGroup } from '../value-panel'
import s from './style.module.css' import s from './style.module.css'
import { AppInfoComp, ChatBtn, EditBtn, FootLogo, PromptTemplate } from './massive-component' import { AppInfoComp, ChatBtn, EditBtn, FootLogo, PromptTemplate } from './massive-component'
import type { PromptConfig, AppInfo } from '@/types/app' import type { AppInfo, PromptConfig } from '@/types/app'
import Toast from '@/app/components/base/toast' import Toast from '@/app/components/base/toast'
import Select from '@/app/components/base/select' import Select from '@/app/components/base/select'
import { DEFAULT_VALUE_MAX_LEN } from '@/config' import { DEFAULT_VALUE_MAX_LEN } from '@/config'
...@@ -276,7 +276,7 @@ const Welcome: FC<IWelcomeProps> = ({ ...@@ -276,7 +276,7 @@ const Welcome: FC<IWelcomeProps> = ({
} }
const renderHasSetInputs = () => { const renderHasSetInputs = () => {
if (!isPublicVersion && !canEidtInpus || !hasVar) if ((!isPublicVersion && !canEidtInpus) || !hasVar)
return null return null
return ( return (
......
import { AppInfo } from "@/types/app" import type { AppInfo } from '@/types/app'
export const APP_ID = '' export const APP_ID = ''
export const API_KEY = '' export const API_KEY = ''
export const API_URL = '' export const API_URL = ''
export const APP_INFO: AppInfo = { export const APP_INFO: AppInfo = {
"title": 'Chat APP', title: 'Chat APP',
"description": '', description: '',
"copyright": '', copyright: '',
"privacy_policy": '', privacy_policy: '',
"default_language": 'zh-Hans' default_language: 'zh-Hans',
} }
export const isShowPrompt = false export const isShowPrompt = false
export const promptTemplate = 'I want you to act as a javascript console.' export const promptTemplate = 'I want you to act as a javascript console.'
export const API_PREFIX = '/api'; export const API_PREFIX = '/api'
export const LOCALE_COOKIE_NAME = 'locale' export const LOCALE_COOKIE_NAME = 'locale'
......
...@@ -8,20 +8,22 @@ export enum MediaType { ...@@ -8,20 +8,22 @@ export enum MediaType {
} }
const useBreakpoints = () => { const useBreakpoints = () => {
const [width, setWidth] = React.useState(globalThis.innerWidth); const [width, setWidth] = React.useState(globalThis.innerWidth)
const media = (() => { const media = (() => {
if (width <= 640) return MediaType.mobile; if (width <= 640)
if (width <= 768) return MediaType.tablet; return MediaType.mobile
return MediaType.pc; if (width <= 768)
})(); return MediaType.tablet
return MediaType.pc
})()
React.useEffect(() => { React.useEffect(() => {
const handleWindowResize = () => setWidth(window.innerWidth); const handleWindowResize = () => setWidth(window.innerWidth)
window.addEventListener("resize", handleWindowResize); window.addEventListener('resize', handleWindowResize)
return () => window.removeEventListener("resize", handleWindowResize); return () => window.removeEventListener('resize', handleWindowResize)
}, []); }, [])
return media; return media
} }
export default useBreakpoints export default useBreakpoints
\ No newline at end of file
import { useState } from 'react' import { useState } from 'react'
import type { ConversationItem } from '@/types/app'
import produce from 'immer' import produce from 'immer'
import type { ConversationItem } from '@/types/app'
const storageConversationIdKey = 'conversationIdInfo' const storageConversationIdKey = 'conversationIdInfo'
...@@ -29,9 +29,10 @@ function useConversation() { ...@@ -29,9 +29,10 @@ function useConversation() {
// input can be updated by user // input can be updated by user
const [newConversationInputs, setNewConversationInputs] = useState<Record<string, any> | null>(null) const [newConversationInputs, setNewConversationInputs] = useState<Record<string, any> | null>(null)
const resetNewConversationInputs = () => { const resetNewConversationInputs = () => {
if (!newConversationInputs) return if (!newConversationInputs)
setNewConversationInputs(produce(newConversationInputs, draft => { return
Object.keys(draft).forEach(key => { setNewConversationInputs(produce(newConversationInputs, (draft) => {
Object.keys(draft).forEach((key) => {
draft[key] = '' draft[key] = ''
}) })
})) }))
...@@ -59,8 +60,8 @@ function useConversation() { ...@@ -59,8 +60,8 @@ function useConversation() {
setCurrInputs, setCurrInputs,
currConversationInfo, currConversationInfo,
setNewConversationInfo, setNewConversationInfo,
setExistConversationInfo setExistConversationInfo,
} }
} }
export default useConversation; export default useConversation
\ No newline at end of file
...@@ -12,7 +12,6 @@ export const getLocaleOnClient = (): Locale => { ...@@ -12,7 +12,6 @@ export const getLocaleOnClient = (): Locale => {
export const setLocaleOnClient = (locale: Locale, notReload?: boolean) => { export const setLocaleOnClient = (locale: Locale, notReload?: boolean) => {
Cookies.set(LOCALE_COOKIE_NAME, locale) Cookies.set(LOCALE_COOKIE_NAME, locale)
changeLanguage(locale) changeLanguage(locale)
if (!notReload) { if (!notReload)
location.reload() location.reload()
}
} }
...@@ -5,7 +5,7 @@ import commonEn from './lang/common.en' ...@@ -5,7 +5,7 @@ import commonEn from './lang/common.en'
import commonZh from './lang/common.zh' import commonZh from './lang/common.zh'
import appEn from './lang/app.en' import appEn from './lang/app.en'
import appZh from './lang/app.zh' import appZh from './lang/app.zh'
import { Locale } from '.' import type { Locale } from '.'
const resources = { const resources = {
'en': { 'en': {
......
import { createInstance } from 'i18next' import { createInstance } from 'i18next'
import resourcesToBackend from 'i18next-resources-to-backend' import resourcesToBackend from 'i18next-resources-to-backend'
import { initReactI18next } from 'react-i18next/initReactI18next' import { initReactI18next } from 'react-i18next/initReactI18next'
import { Locale } from '.' import type { Locale } from '.'
// https://locize.com/blog/next-13-app-dir-i18n/ // https://locize.com/blog/next-13-app-dir-i18n/
const initI18next = async (lng: Locale, ns: string) => { const initI18next = async (lng: Locale, ns: string) => {
...@@ -21,6 +21,6 @@ export async function useTranslation(lng: Locale, ns = '', options: Record<strin ...@@ -21,6 +21,6 @@ export async function useTranslation(lng: Locale, ns = '', options: Record<strin
const i18nextInstance = await initI18next(lng, ns) const i18nextInstance = await initI18next(lng, ns)
return { return {
t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix), t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix),
i18n: i18nextInstance i18n: i18nextInstance,
} }
} }
\ No newline at end of file
const translation = { const translation = {
common: { common: {
welcome: "Welcome to use", welcome: 'Welcome to use',
appUnavailable: "App is unavailable", appUnavailable: 'App is unavailable',
appUnkonwError: "App is unavailable" appUnkonwError: 'App is unavailable',
}, },
chat: { chat: {
newChat: "New chat", newChat: 'New chat',
newChatDefaultName: "New conversation", newChatDefaultName: 'New conversation',
openingStatementTitle: "Opening statement", openingStatementTitle: 'Opening statement',
powerBy: "Powered by", powerBy: 'Powered by',
prompt: "Prompt", prompt: 'Prompt',
privatePromptConfigTitle: "Conversation settings", privatePromptConfigTitle: 'Conversation settings',
publicPromptConfigTitle: "Initial Prompt", publicPromptConfigTitle: 'Initial Prompt',
configStatusDes: "Before start, you can modify conversation settings", configStatusDes: 'Before start, you can modify conversation settings',
configDisabled: configDisabled:
"Previous session settings have been used for this session.", 'Previous session settings have been used for this session.',
startChat: "Start Chat", startChat: 'Start Chat',
privacyPolicyLeft: privacyPolicyLeft:
"Please read the ", 'Please read the ',
privacyPolicyMiddle: privacyPolicyMiddle:
"privacy policy", 'privacy policy',
privacyPolicyRight: privacyPolicyRight:
" provided by the app developer.", ' provided by the app developer.',
}, },
errorMessage: { errorMessage: {
valueOfVarRequired: "Variables value can not be empty", valueOfVarRequired: 'Variables value can not be empty',
waitForResponse: waitForResponse:
"Please wait for the response to the previous message to complete.", 'Please wait for the response to the previous message to complete.',
}, },
}; }
export default translation; export default translation
const translation = { const translation = {
common: { common: {
welcome: "欢迎使用", welcome: '欢迎使用',
appUnavailable: "应用不可用", appUnavailable: '应用不可用',
appUnkonwError: "应用不可用", appUnkonwError: '应用不可用',
}, },
chat: { chat: {
newChat: "新对话", newChat: '新对话',
newChatDefaultName: "新的对话", newChatDefaultName: '新的对话',
openingStatementTitle: "对话开场白", openingStatementTitle: '对话开场白',
powerBy: "Powered by", powerBy: 'Powered by',
prompt: "提示词", prompt: '提示词',
privatePromptConfigTitle: "对话设置", privatePromptConfigTitle: '对话设置',
publicPromptConfigTitle: "对话前提示词", publicPromptConfigTitle: '对话前提示词',
configStatusDes: "开始前,您可以修改对话设置", configStatusDes: '开始前,您可以修改对话设置',
configDisabled: "此次会话已使用上次会话表单", configDisabled: '此次会话已使用上次会话表单',
startChat: "开始对话", startChat: '开始对话',
privacyPolicyLeft: "请阅读由该应用开发者提供的", privacyPolicyLeft: '请阅读由该应用开发者提供的',
privacyPolicyMiddle: "隐私政策", privacyPolicyMiddle: '隐私政策',
privacyPolicyRight: "。", privacyPolicyRight: '。',
}, },
errorMessage: { errorMessage: {
valueOfVarRequired: "变量值必填", valueOfVarRequired: '变量值必填',
waitForResponse: "请等待上条信息响应完成", waitForResponse: '请等待上条信息响应完成',
}, },
}; }
export default translation; export default translation
...@@ -16,7 +16,7 @@ const translation = { ...@@ -16,7 +16,7 @@ const translation = {
lineBreak: 'Line break', lineBreak: 'Line break',
like: 'like', like: 'like',
dislike: 'dislike', dislike: 'dislike',
} },
} }
export default translation export default translation
...@@ -16,7 +16,7 @@ const translation = { ...@@ -16,7 +16,7 @@ const translation = {
lineBreak: '换行', lineBreak: '换行',
like: '赞同', like: '赞同',
dislike: '反对', dislike: '反对',
} },
} }
export default translation export default translation
...@@ -15,7 +15,7 @@ const nextConfig = { ...@@ -15,7 +15,7 @@ const nextConfig = {
typescript: { typescript: {
// https://nextjs.org/docs/api-reference/next.config.js/ignoring-typescript-errors // https://nextjs.org/docs/api-reference/next.config.js/ignoring-typescript-errors
ignoreBuildErrors: true, ignoreBuildErrors: true,
} },
} }
module.exports = nextConfig module.exports = nextConfig
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",
"fix": "next lint --fix" "fix": "next lint --fix",
"eslint-fix": "eslint . --fix",
"prepare": "husky install ./.husky"
}, },
"dependencies": { "dependencies": {
"@formatjs/intl-localematcher": "^0.2.32", "@formatjs/intl-localematcher": "^0.2.32",
...@@ -28,6 +30,7 @@ ...@@ -28,6 +30,7 @@
"eslint": "8.36.0", "eslint": "8.36.0",
"eslint-config-next": "13.2.4", "eslint-config-next": "13.2.4",
"eventsource-parser": "^1.0.0", "eventsource-parser": "^1.0.0",
"husky": "^8.0.3",
"i18next": "^22.4.13", "i18next": "^22.4.13",
"i18next-resources-to-backend": "^1.1.3", "i18next-resources-to-backend": "^1.1.3",
"immer": "^9.0.19", "immer": "^9.0.19",
...@@ -56,15 +59,23 @@ ...@@ -56,15 +59,23 @@
"uuid": "^9.0.0" "uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^0.36.0", "@antfu/eslint-config": "0.36.0",
"@faker-js/faker": "^7.6.0", "@faker-js/faker": "^7.6.0",
"@tailwindcss/typography": "^0.5.9", "@tailwindcss/typography": "^0.5.9",
"@types/js-cookie": "^3.0.3", "@types/js-cookie": "^3.0.3",
"@types/negotiator": "^0.6.1", "@types/negotiator": "^0.6.1",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"miragejs": "^0.1.47", "lint-staged": "^13.2.2",
"postcss": "^8.4.21", "postcss": "^8.4.21",
"tailwindcss": "^3.2.7" "tailwindcss": "^3.2.7"
},
"lint-staged": {
"**/*.js?(x)": [
"eslint --fix"
],
"**/*.ts?(x)": [
"eslint --fix"
]
} }
} }
\ No newline at end of file
...@@ -14,4 +14,4 @@ ...@@ -14,4 +14,4 @@
] ]
} }
] ]
} }
\ No newline at end of file
...@@ -46,15 +46,15 @@ module.exports = { ...@@ -46,15 +46,15 @@ module.exports = {
indigo: { indigo: {
25: '#F5F8FF', 25: '#F5F8FF',
100: '#E0EAFF', 100: '#E0EAFF',
600: '#444CE7' 600: '#444CE7',
} },
}, },
screens: { screens: {
'mobile': '100px', mobile: '100px',
// => @media (min-width: 100px) { ... } // => @media (min-width: 100px) { ... }
'tablet': '640px', // 391 tablet: '640px', // 391
// => @media (min-width: 600px) { ... } // => @media (min-width: 600px) { ... }
'pc': '769px', pc: '769px',
// => @media (min-width: 769px) { ... } // => @media (min-width: 769px) { ... }
}, },
}, },
......
...@@ -40,4 +40,4 @@ ...@@ -40,4 +40,4 @@
"exclude": [ "exclude": [
"node_modules" "node_modules"
] ]
} }
\ No newline at end of file
import { Locale } from '@/i18n' import type { Locale } from '@/i18n'
export type PromptVariable = { export type PromptVariable = {
key: string, key: string
name: string, name: string
type: "string" | "number" | "select", type: 'string' | 'number' | 'select'
default?: string | number, default?: string | number
options?: string[] options?: string[]
max_length?: number max_length?: number
required: boolean required: boolean
} }
export type PromptConfig = { export type PromptConfig = {
prompt_template: string, prompt_template: string
prompt_variables: PromptVariable[], prompt_variables: PromptVariable[]
} }
export type TextTypeFormItem = { export type TextTypeFormItem = {
label: string, label: string
variable: string, variable: string
required: boolean required: boolean
max_length: number max_length: number
} }
export type SelectTypeFormItem = { export type SelectTypeFormItem = {
label: string, label: string
variable: string, variable: string
required: boolean, required: boolean
options: string[] options: string[]
} }
/** /**
...@@ -79,14 +79,13 @@ export type IChatItem = { ...@@ -79,14 +79,13 @@ export type IChatItem = {
isOpeningStatement?: boolean isOpeningStatement?: boolean
} }
export type ResponseHolder = {} export type ResponseHolder = {}
export type ConversationItem = { export type ConversationItem = {
id: string id: string
name: string name: string
inputs: Record<string, any> | null inputs: Record<string, any> | null
introduction: string, introduction: string
} }
export type AppInfo = { export type AppInfo = {
...@@ -95,4 +94,4 @@ export type AppInfo = { ...@@ -95,4 +94,4 @@ export type AppInfo = {
default_language: Locale default_language: Locale
copyright?: string copyright?: string
privacy_policy?: string privacy_policy?: string
} }
\ No newline at end of file
...@@ -38,15 +38,15 @@ module.exports = ({ theme }) => ({ ...@@ -38,15 +38,15 @@ module.exports = ({ theme }) => ({
'--tw-prose-invert-td-borders': theme('colors.zinc.700'), '--tw-prose-invert-td-borders': theme('colors.zinc.700'),
// Base // Base
color: 'var(--tw-prose-body)', 'color': 'var(--tw-prose-body)',
fontSize: theme('fontSize.sm')[0], 'fontSize': theme('fontSize.sm')[0],
lineHeight: theme('lineHeight.7'), 'lineHeight': theme('lineHeight.7'),
// Layout // Layout
'> *': { '> *': {
maxWidth: theme('maxWidth.2xl'), 'maxWidth': theme('maxWidth.2xl'),
marginLeft: 'auto', 'marginLeft': 'auto',
marginRight: 'auto', 'marginRight': 'auto',
'@screen lg': { '@screen lg': {
maxWidth: theme('maxWidth.3xl'), maxWidth: theme('maxWidth.3xl'),
marginLeft: `calc(50% - min(50%, ${theme('maxWidth.lg')}))`, marginLeft: `calc(50% - min(50%, ${theme('maxWidth.lg')}))`,
...@@ -55,7 +55,7 @@ module.exports = ({ theme }) => ({ ...@@ -55,7 +55,7 @@ module.exports = ({ theme }) => ({
}, },
// Text // Text
p: { 'p': {
marginTop: theme('spacing.6'), marginTop: theme('spacing.6'),
marginBottom: theme('spacing.6'), marginBottom: theme('spacing.6'),
}, },
...@@ -65,7 +65,7 @@ module.exports = ({ theme }) => ({ ...@@ -65,7 +65,7 @@ module.exports = ({ theme }) => ({
}, },
// Lists // Lists
ol: { 'ol': {
listStyleType: 'decimal', listStyleType: 'decimal',
marginTop: theme('spacing.5'), marginTop: theme('spacing.5'),
marginBottom: theme('spacing.5'), marginBottom: theme('spacing.5'),
...@@ -98,13 +98,13 @@ module.exports = ({ theme }) => ({ ...@@ -98,13 +98,13 @@ module.exports = ({ theme }) => ({
'ol[type="1"]': { 'ol[type="1"]': {
listStyleType: 'decimal', listStyleType: 'decimal',
}, },
ul: { 'ul': {
listStyleType: 'disc', listStyleType: 'disc',
marginTop: theme('spacing.5'), marginTop: theme('spacing.5'),
marginBottom: theme('spacing.5'), marginBottom: theme('spacing.5'),
paddingLeft: '1.625rem', paddingLeft: '1.625rem',
}, },
li: { 'li': {
marginTop: theme('spacing.2'), marginTop: theme('spacing.2'),
marginBottom: theme('spacing.2'), marginBottom: theme('spacing.2'),
}, },
...@@ -140,14 +140,14 @@ module.exports = ({ theme }) => ({ ...@@ -140,14 +140,14 @@ module.exports = ({ theme }) => ({
}, },
// Horizontal rules // Horizontal rules
hr: { 'hr': {
borderColor: 'var(--tw-prose-hr)', 'borderColor': 'var(--tw-prose-hr)',
borderTopWidth: 1, 'borderTopWidth': 1,
marginTop: theme('spacing.16'), 'marginTop': theme('spacing.16'),
marginBottom: theme('spacing.16'), 'marginBottom': theme('spacing.16'),
maxWidth: 'none', 'maxWidth': 'none',
marginLeft: `calc(-1 * ${theme('spacing.4')})`, 'marginLeft': `calc(-1 * ${theme('spacing.4')})`,
marginRight: `calc(-1 * ${theme('spacing.4')})`, 'marginRight': `calc(-1 * ${theme('spacing.4')})`,
'@screen sm': { '@screen sm': {
marginLeft: `calc(-1 * ${theme('spacing.6')})`, marginLeft: `calc(-1 * ${theme('spacing.6')})`,
marginRight: `calc(-1 * ${theme('spacing.6')})`, marginRight: `calc(-1 * ${theme('spacing.6')})`,
...@@ -159,7 +159,7 @@ module.exports = ({ theme }) => ({ ...@@ -159,7 +159,7 @@ module.exports = ({ theme }) => ({
}, },
// Quotes // Quotes
blockquote: { 'blockquote': {
fontWeight: '500', fontWeight: '500',
fontStyle: 'italic', fontStyle: 'italic',
color: 'var(--tw-prose-quotes)', color: 'var(--tw-prose-quotes)',
...@@ -178,14 +178,14 @@ module.exports = ({ theme }) => ({ ...@@ -178,14 +178,14 @@ module.exports = ({ theme }) => ({
}, },
// Headings // Headings
h1: { 'h1': {
color: 'var(--tw-prose-headings)', color: 'var(--tw-prose-headings)',
fontWeight: '700', fontWeight: '700',
fontSize: theme('fontSize.2xl')[0], fontSize: theme('fontSize.2xl')[0],
...theme('fontSize.2xl')[1], ...theme('fontSize.2xl')[1],
marginBottom: theme('spacing.2'), marginBottom: theme('spacing.2'),
}, },
h2: { 'h2': {
color: 'var(--tw-prose-headings)', color: 'var(--tw-prose-headings)',
fontWeight: '600', fontWeight: '600',
fontSize: theme('fontSize.lg')[0], fontSize: theme('fontSize.lg')[0],
...@@ -193,7 +193,7 @@ module.exports = ({ theme }) => ({ ...@@ -193,7 +193,7 @@ module.exports = ({ theme }) => ({
marginTop: theme('spacing.16'), marginTop: theme('spacing.16'),
marginBottom: theme('spacing.2'), marginBottom: theme('spacing.2'),
}, },
h3: { 'h3': {
color: 'var(--tw-prose-headings)', color: 'var(--tw-prose-headings)',
fontSize: theme('fontSize.base')[0], fontSize: theme('fontSize.base')[0],
...theme('fontSize.base')[1], ...theme('fontSize.base')[1],
...@@ -211,7 +211,7 @@ module.exports = ({ theme }) => ({ ...@@ -211,7 +211,7 @@ module.exports = ({ theme }) => ({
marginTop: '0', marginTop: '0',
marginBottom: '0', marginBottom: '0',
}, },
figcaption: { 'figcaption': {
color: 'var(--tw-prose-captions)', color: 'var(--tw-prose-captions)',
fontSize: theme('fontSize.xs')[0], fontSize: theme('fontSize.xs')[0],
...theme('fontSize.xs')[1], ...theme('fontSize.xs')[1],
...@@ -219,7 +219,7 @@ module.exports = ({ theme }) => ({ ...@@ -219,7 +219,7 @@ module.exports = ({ theme }) => ({
}, },
// Tables // Tables
table: { 'table': {
width: '100%', width: '100%',
tableLayout: 'auto', tableLayout: 'auto',
textAlign: 'left', textAlign: 'left',
...@@ -227,7 +227,7 @@ module.exports = ({ theme }) => ({ ...@@ -227,7 +227,7 @@ module.exports = ({ theme }) => ({
marginBottom: theme('spacing.8'), marginBottom: theme('spacing.8'),
lineHeight: theme('lineHeight.6'), lineHeight: theme('lineHeight.6'),
}, },
thead: { 'thead': {
borderBottomWidth: '1px', borderBottomWidth: '1px',
borderBottomColor: 'var(--tw-prose-th-borders)', borderBottomColor: 'var(--tw-prose-th-borders)',
}, },
...@@ -255,7 +255,7 @@ module.exports = ({ theme }) => ({ ...@@ -255,7 +255,7 @@ module.exports = ({ theme }) => ({
'tbody td': { 'tbody td': {
verticalAlign: 'baseline', verticalAlign: 'baseline',
}, },
tfoot: { 'tfoot': {
borderTopWidth: '1px', borderTopWidth: '1px',
borderTopColor: 'var(--tw-prose-th-borders)', borderTopColor: 'var(--tw-prose-th-borders)',
}, },
...@@ -276,13 +276,13 @@ module.exports = ({ theme }) => ({ ...@@ -276,13 +276,13 @@ module.exports = ({ theme }) => ({
}, },
// Inline elements // Inline elements
a: { 'a': {
color: 'var(--tw-prose-links)', 'color': 'var(--tw-prose-links)',
textDecoration: 'underline transparent', 'textDecoration': 'underline transparent',
fontWeight: '500', 'fontWeight': '500',
transitionProperty: 'color, text-decoration-color', 'transitionProperty': 'color, text-decoration-color',
transitionDuration: theme('transitionDuration.DEFAULT'), 'transitionDuration': theme('transitionDuration.DEFAULT'),
transitionTimingFunction: theme('transitionTimingFunction.DEFAULT'), 'transitionTimingFunction': theme('transitionTimingFunction.DEFAULT'),
'&:hover': { '&:hover': {
color: 'var(--tw-prose-links-hover)', color: 'var(--tw-prose-links-hover)',
textDecorationColor: 'var(--tw-prose-links-underline)', textDecorationColor: 'var(--tw-prose-links-underline)',
...@@ -291,14 +291,14 @@ module.exports = ({ theme }) => ({ ...@@ -291,14 +291,14 @@ module.exports = ({ theme }) => ({
':is(h1, h2, h3) a': { ':is(h1, h2, h3) a': {
fontWeight: 'inherit', fontWeight: 'inherit',
}, },
strong: { 'strong': {
color: 'var(--tw-prose-bold)', color: 'var(--tw-prose-bold)',
fontWeight: '600', fontWeight: '600',
}, },
':is(a, blockquote, thead th) strong': { ':is(a, blockquote, thead th) strong': {
color: 'inherit', color: 'inherit',
}, },
code: { 'code': {
color: 'var(--tw-prose-code)', color: 'var(--tw-prose-code)',
borderRadius: theme('borderRadius.lg'), borderRadius: theme('borderRadius.lg'),
paddingTop: theme('padding.1'), paddingTop: theme('padding.1'),
......
import { PromptVariable, UserInputFormItem } from '@/types/app' import type { PromptVariable, UserInputFormItem } from '@/types/app'
export function replaceVarWithValues(str: string, promptVariables: PromptVariable[], inputs: Record<string, any>) { export function replaceVarWithValues(str: string, promptVariables: PromptVariable[], inputs: Record<string, any>) {
return str.replace(/\{\{([^}]+)\}\}/g, (match, key) => { return str.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
...@@ -12,11 +12,12 @@ export function replaceVarWithValues(str: string, promptVariables: PromptVariabl ...@@ -12,11 +12,12 @@ export function replaceVarWithValues(str: string, promptVariables: PromptVariabl
} }
export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | null) => { export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | null) => {
if (!useInputs) return [] if (!useInputs)
return []
const promptVariables: PromptVariable[] = [] const promptVariables: PromptVariable[] = []
useInputs.forEach((item: any) => { useInputs.forEach((item: any) => {
const type = item['text-input'] ? 'string' : 'select' const type = item['text-input'] ? 'string' : 'select'
const content = type === 'string' ? item['text-input'] : item['select'] const content = type === 'string' ? item['text-input'] : item.select
if (type === 'string') { if (type === 'string') {
promptVariables.push({ promptVariables.push({
key: content.variable, key: content.variable,
...@@ -26,7 +27,8 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | ...@@ -26,7 +27,8 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] |
max_length: content.max_length, max_length: content.max_length,
options: [], options: [],
}) })
} else { }
else {
promptVariables.push({ promptVariables.push({
key: content.variable, key: content.variable,
name: content.label, name: content.label,
...@@ -37,4 +39,4 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | ...@@ -37,4 +39,4 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] |
} }
}) })
return promptVariables return promptVariables
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment