mirror of
https://github.com/langgenius/webapp-text-generator.git
synced 2026-07-01 20:05:07 -04:00
feat: lint
This commit is contained in:
+1
-1
@@ -25,4 +25,4 @@
|
||||
],
|
||||
"react-hooks/exhaustive-deps": "warn"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -25,4 +25,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -29,4 +29,4 @@
|
||||
"i18n/lang",
|
||||
"app/api/messages"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ Config app in `config/index.ts`.Please config:
|
||||
More config:
|
||||
```js
|
||||
export const APP_INFO: AppInfo = {
|
||||
"title": 'Chat APP',
|
||||
"description": '',
|
||||
"copyright": '',
|
||||
"privacy_policy": '',
|
||||
"default_language": 'zh-Hans'
|
||||
title: 'Chat APP',
|
||||
description: '',
|
||||
copyright: '',
|
||||
privacy_policy: '',
|
||||
default_language: 'zh-Hans'
|
||||
}
|
||||
|
||||
export const isShowPrompt = true
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import { type NextRequest } 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 }: {
|
||||
params: { messageId: string }
|
||||
}) {
|
||||
const body = await request.json()
|
||||
const {
|
||||
rating
|
||||
rating,
|
||||
} = body
|
||||
const { messageId } = params
|
||||
const { user } = getInfo(request);
|
||||
const { user } = getInfo(request)
|
||||
try {
|
||||
const { data } = await client.messageFeedback(messageId, rating, user)
|
||||
return NextResponse.json(data)
|
||||
} catch (e) {
|
||||
}
|
||||
catch (e) {
|
||||
return NextResponse.json(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { type NextRequest } 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) {
|
||||
const { sessionId, user } = getInfo(request);
|
||||
const { searchParams } = new URL(request.url);
|
||||
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);
|
||||
const { data }: any = await client.getConversationMessages(user, conversationId as string)
|
||||
return NextResponse.json(data, {
|
||||
headers: setSession(sessionId)
|
||||
headers: setSession(sessionId),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { type NextRequest } 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) {
|
||||
const { sessionId, user } = getInfo(request);
|
||||
const { data } = await client.getApplicationParameters(user);
|
||||
const { sessionId, user } = getInfo(request)
|
||||
const { data } = await client.getApplicationParameters(user)
|
||||
return NextResponse.json(data as object, {
|
||||
headers: setSession(sessionId)
|
||||
headers: setSession(sessionId),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { type NextRequest } from 'next/server'
|
||||
import { APP_ID, API_KEY, API_URL } from '@/config'
|
||||
import { CompletionClient } from 'dify-client'
|
||||
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) => {
|
||||
const sessionId = request.cookies.get('session_id')?.value || v4();
|
||||
const user = userPrefix + sessionId;
|
||||
const sessionId = request.cookies.get('session_id')?.value || v4()
|
||||
const user = userPrefix + sessionId
|
||||
return {
|
||||
sessionId,
|
||||
user
|
||||
user,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +18,4 @@ export const setSession = (sessionId: string) => {
|
||||
return { 'Set-Cookie': `session_id=${sessionId}` }
|
||||
}
|
||||
|
||||
export const client = new CompletionClient(API_KEY, API_URL ? API_URL : undefined)
|
||||
export const client = new CompletionClient(API_KEY, API_URL || undefined)
|
||||
|
||||
@@ -14,9 +14,8 @@ const AppUnavailable: FC<IAppUnavailableProps> = ({
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
let message = errMessage
|
||||
if (!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'>
|
||||
|
||||
@@ -18,7 +18,7 @@ export function Markdown(props: { content: string }) {
|
||||
components={{
|
||||
code({ node, inline, className, children, ...props }) {
|
||||
const match = /language-(\w+)/.exec(className || '')
|
||||
return !inline && match
|
||||
return (!inline && match)
|
||||
? (
|
||||
<SyntaxHighlighter
|
||||
{...props}
|
||||
|
||||
+37
-39
@@ -1,25 +1,24 @@
|
||||
'use client'
|
||||
import React, { useEffect, useState, useRef } from 'react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import cn from 'classnames'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import { useBoolean, useClickAway } from 'ahooks'
|
||||
import { XMarkIcon } from '@heroicons/react/24/outline'
|
||||
import ConfigScence from './config-scence'
|
||||
import NoData from './no-data'
|
||||
import TextGenerationRes from './result'
|
||||
import Button from './base/button'
|
||||
import s from './style.module.css'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import { fetchAppParams, sendCompletionMessage, updateFeedback } from '@/service'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { Feedbacktype, PromptConfig } from '@/types/app'
|
||||
import type { Feedbacktype, PromptConfig } from '@/types/app'
|
||||
import { changeLanguage } from '@/i18n/i18next-config'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import AppUnavailable from '@/app/components/app-unavailable'
|
||||
import { APP_ID, API_KEY, APP_INFO } from '@/config'
|
||||
import { XMarkIcon } from '@heroicons/react/24/outline'
|
||||
import Button from './base/button'
|
||||
import { API_KEY, APP_ID, APP_INFO } from '@/config'
|
||||
import { userInputsFormToPromptVariables } from '@/utils/prompt'
|
||||
|
||||
import s from './style.module.css'
|
||||
|
||||
const TextGeneration = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -44,7 +43,7 @@ const TextGeneration = () => {
|
||||
const isNoData = !completionRes
|
||||
const [messageId, setMessageId] = useState<string | null>(null)
|
||||
const [feedback, setFeedback] = useState<Feedbacktype>({
|
||||
rating: null
|
||||
rating: null,
|
||||
})
|
||||
|
||||
const handleFeedback = async (feedback: Feedbacktype) => {
|
||||
@@ -95,7 +94,6 @@ const TextGeneration = () => {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
const data = {
|
||||
inputs,
|
||||
query,
|
||||
@@ -103,7 +101,7 @@ const TextGeneration = () => {
|
||||
|
||||
setMessageId(null)
|
||||
setFeedback({
|
||||
rating: null
|
||||
rating: null,
|
||||
})
|
||||
setCompletionRes('')
|
||||
|
||||
@@ -123,7 +121,7 @@ const TextGeneration = () => {
|
||||
},
|
||||
onError() {
|
||||
setResponsingFalse()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -164,14 +162,14 @@ const TextGeneration = () => {
|
||||
const [isShowResSidebar, { setTrue: showResSidebar, setFalse: hideResSidebar }] = useBoolean(false)
|
||||
const resRef = useRef<HTMLDivElement>(null)
|
||||
useClickAway(() => {
|
||||
hideResSidebar();
|
||||
hideResSidebar()
|
||||
}, resRef)
|
||||
|
||||
const renderRes = (
|
||||
<div
|
||||
ref={resRef}
|
||||
className={
|
||||
cn("flex flex-col h-full shrink-0",
|
||||
cn('flex flex-col h-full shrink-0',
|
||||
isPC ? 'px-10 py-8' : 'bg-gray-50',
|
||||
isTablet && 'p-6', isMoble && 'p-4')}
|
||||
>
|
||||
@@ -192,27 +190,29 @@ const TextGeneration = () => {
|
||||
</div>
|
||||
|
||||
<div className='grow'>
|
||||
{(isResponsing && !completionRes) ? (
|
||||
<div className='flex h-full w-full justify-center items-center'>
|
||||
<Loading type='area' />
|
||||
</div>) : (
|
||||
<>
|
||||
{isNoData
|
||||
? <NoData />
|
||||
: (
|
||||
<TextGenerationRes
|
||||
className='mt-3'
|
||||
content={completionRes}
|
||||
messageId={messageId}
|
||||
isInWebApp
|
||||
onFeedback={handleFeedback}
|
||||
feedback={feedback}
|
||||
isMobile={isMoble}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)}
|
||||
{(isResponsing && !completionRes)
|
||||
? (
|
||||
<div className='flex h-full w-full justify-center items-center'>
|
||||
<Loading type='area' />
|
||||
</div>)
|
||||
: (
|
||||
<>
|
||||
{isNoData
|
||||
? <NoData />
|
||||
: (
|
||||
<TextGenerationRes
|
||||
className='mt-3'
|
||||
content={completionRes}
|
||||
messageId={messageId}
|
||||
isInWebApp
|
||||
onFeedback={handleFeedback}
|
||||
feedback={feedback}
|
||||
isMobile={isMoble}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
@@ -224,12 +224,11 @@ const TextGeneration = () => {
|
||||
if (!APP_INFO || !promptConfig)
|
||||
return <Loading type='app' />
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={cn(isPC && 'flex', 'h-screen bg-gray-50')}>
|
||||
{/* Left */}
|
||||
<div className={cn(isPC ? 'w-[600px] max-w-[50%] p-8' : 'p-4', "shrink-0 relative flex flex-col pb-10 h-full border-r border-gray-100 bg-white")}>
|
||||
<div className={cn(isPC ? 'w-[600px] max-w-[50%] p-8' : 'p-4', 'shrink-0 relative flex flex-col pb-10 h-full border-r border-gray-100 bg-white')}>
|
||||
<div className='mb-6'>
|
||||
<div className='flex justify-between items-center'>
|
||||
<div className='flex items-center space-x-3'>
|
||||
@@ -264,7 +263,6 @@ const TextGeneration = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* copyright */}
|
||||
<div className='fixed left-8 bottom-4 flex space-x-2 text-gray-400 font-normal text-xs'>
|
||||
<div className="">© {APP_INFO.copyright || APP_INFO.title} {(new Date()).getFullYear()}</div>
|
||||
@@ -294,7 +292,7 @@ const TextGeneration = () => {
|
||||
<div
|
||||
className={cn('fixed z-50 inset-0', isTablet ? 'pl-[128px]' : 'pl-6')}
|
||||
style={{
|
||||
background: 'rgba(35, 56, 118, 0.2)'
|
||||
background: 'rgba(35, 56, 118, 0.2)',
|
||||
}}
|
||||
>
|
||||
{renderRes}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
'use client'
|
||||
import React, { FC, useState } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import cn from 'classnames'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline'
|
||||
import { Markdown } from '@/app/components/base/markdown'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { Feedbacktype } from '@/types/app'
|
||||
import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline'
|
||||
import type { Feedbacktype } from '@/types/app'
|
||||
|
||||
export interface IGenerationItemProps {
|
||||
export type IGenerationItemProps = {
|
||||
className?: string
|
||||
content: string
|
||||
messageId?: string | null
|
||||
@@ -22,7 +23,7 @@ export interface IGenerationItemProps {
|
||||
|
||||
export const SimpleBtn = ({ className, onClick, children }: {
|
||||
className?: string
|
||||
onClick?: () => void,
|
||||
onClick?: () => void
|
||||
children: React.ReactNode
|
||||
}) => (
|
||||
<div
|
||||
@@ -53,93 +54,95 @@ const GenerationItem: FC<IGenerationItemProps> = ({
|
||||
isInWebApp = false,
|
||||
feedback,
|
||||
onFeedback,
|
||||
isMobile
|
||||
isMobile,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className={cn(className, 'rounded-xl border border-gray-200 bg-white')}
|
||||
style={{
|
||||
boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)'
|
||||
boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)',
|
||||
}}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div className='flex items-center h-10'><Loading type='area' /></div>
|
||||
) : (
|
||||
<div
|
||||
className={cn('p-4')}
|
||||
>
|
||||
<Markdown content={content} />
|
||||
{messageId && (
|
||||
<div className='flex items-center justify-between mt-3'>
|
||||
<div className='flex items-center'>
|
||||
<SimpleBtn
|
||||
className={cn(isMobile && '!px-1.5', 'space-x-1')}
|
||||
onClick={() => {
|
||||
copy(content)
|
||||
Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') })
|
||||
}}>
|
||||
{copyIcon}
|
||||
{!isMobile && <div>{t('common.operation.copy')}</div>}
|
||||
</SimpleBtn>
|
||||
{isInWebApp && (
|
||||
<>
|
||||
<div className="mx-3 w-[1px] h-[14px] bg-gray-200"></div>
|
||||
{!feedback?.rating && (
|
||||
<SimpleBtn className="!px-0">
|
||||
<>
|
||||
<div
|
||||
onClick={() => {
|
||||
onFeedback?.({
|
||||
rating: 'like'
|
||||
})
|
||||
}}
|
||||
className='flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-100'>
|
||||
<HandThumbUpIcon width={16} height={16} />
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
onFeedback?.({
|
||||
rating: 'dislike'
|
||||
})
|
||||
}}
|
||||
className='flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-100'>
|
||||
<HandThumbDownIcon width={16} height={16} />
|
||||
</div>
|
||||
</>
|
||||
</SimpleBtn>
|
||||
)}
|
||||
{feedback?.rating === 'like' && (
|
||||
<div
|
||||
onClick={() => {
|
||||
onFeedback?.({
|
||||
rating: null
|
||||
})
|
||||
}}
|
||||
className='flex w-7 h-7 items-center justify-center rounded-md cursor-pointer !text-primary-600 border border-primary-200 bg-primary-100 hover:border-primary-300 hover:bg-primary-200'>
|
||||
<HandThumbUpIcon width={16} height={16} />
|
||||
</div>
|
||||
)}
|
||||
{feedback?.rating === 'dislike' && (
|
||||
<div
|
||||
onClick={() => {
|
||||
onFeedback?.({
|
||||
rating: null
|
||||
})
|
||||
}}
|
||||
className='flex w-7 h-7 items-center justify-center rounded-md cursor-pointer !text-red-600 border border-red-200 bg-red-100 hover:border-red-300 hover:bg-red-200'>
|
||||
<HandThumbDownIcon width={16} height={16} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{isLoading
|
||||
? (
|
||||
<div className='flex items-center h-10'><Loading type='area' /></div>
|
||||
)
|
||||
: (
|
||||
<div
|
||||
className={cn('p-4')}
|
||||
>
|
||||
<Markdown content={content} />
|
||||
{messageId && (
|
||||
<div className='flex items-center justify-between mt-3'>
|
||||
<div className='flex items-center'>
|
||||
<SimpleBtn
|
||||
className={cn(isMobile && '!px-1.5', 'space-x-1')}
|
||||
onClick={() => {
|
||||
copy(content)
|
||||
Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') })
|
||||
}}>
|
||||
{copyIcon}
|
||||
{!isMobile && <div>{t('common.operation.copy')}</div>}
|
||||
</SimpleBtn>
|
||||
{isInWebApp && (
|
||||
<>
|
||||
<div className="mx-3 w-[1px] h-[14px] bg-gray-200"></div>
|
||||
{!feedback?.rating && (
|
||||
<SimpleBtn className="!px-0">
|
||||
<>
|
||||
<div
|
||||
onClick={() => {
|
||||
onFeedback?.({
|
||||
rating: 'like',
|
||||
})
|
||||
}}
|
||||
className='flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-100'>
|
||||
<HandThumbUpIcon width={16} height={16} />
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
onFeedback?.({
|
||||
rating: 'dislike',
|
||||
})
|
||||
}}
|
||||
className='flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-100'>
|
||||
<HandThumbDownIcon width={16} height={16} />
|
||||
</div>
|
||||
</>
|
||||
</SimpleBtn>
|
||||
)}
|
||||
{feedback?.rating === 'like' && (
|
||||
<div
|
||||
onClick={() => {
|
||||
onFeedback?.({
|
||||
rating: null,
|
||||
})
|
||||
}}
|
||||
className='flex w-7 h-7 items-center justify-center rounded-md cursor-pointer !text-primary-600 border border-primary-200 bg-primary-100 hover:border-primary-300 hover:bg-primary-200'>
|
||||
<HandThumbUpIcon width={16} height={16} />
|
||||
</div>
|
||||
)}
|
||||
{feedback?.rating === 'dislike' && (
|
||||
<div
|
||||
onClick={() => {
|
||||
onFeedback?.({
|
||||
rating: null,
|
||||
})
|
||||
}}
|
||||
className='flex w-7 h-7 items-center justify-center rounded-md cursor-pointer !text-red-600 border border-red-200 bg-red-100 hover:border-red-300 hover:bg-red-200'>
|
||||
<HandThumbDownIcon width={16} height={16} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className='text-xs text-gray-500'>{content?.length} {t('common.unit.char')}</div>
|
||||
</div>
|
||||
<div className='text-xs text-gray-500'>{content?.length} {t('common.unit.char')}</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
+7
-7
@@ -1,16 +1,16 @@
|
||||
import { AppInfo } from "@/types/app"
|
||||
import type { AppInfo } from '@/types/app'
|
||||
export const APP_ID = ''
|
||||
export const API_KEY = ''
|
||||
export const API_URL = ''
|
||||
export const APP_INFO: AppInfo = {
|
||||
"title": 'Text Generator APP',
|
||||
"description": 'App description',
|
||||
"copyright": '',
|
||||
"privacy_policy": '',
|
||||
"default_language": 'zh-Hans'
|
||||
title: 'Text Generator APP',
|
||||
description: 'App description',
|
||||
copyright: '',
|
||||
privacy_policy: '',
|
||||
default_language: 'zh-Hans',
|
||||
}
|
||||
|
||||
export const API_PREFIX = '/api';
|
||||
export const API_PREFIX = '/api'
|
||||
|
||||
export const LOCALE_COOKIE_NAME = 'locale'
|
||||
|
||||
|
||||
+13
-11
@@ -8,20 +8,22 @@ export enum MediaType {
|
||||
}
|
||||
|
||||
const useBreakpoints = () => {
|
||||
const [width, setWidth] = React.useState(globalThis.innerWidth);
|
||||
const [width, setWidth] = React.useState(globalThis.innerWidth)
|
||||
const media = (() => {
|
||||
if (width <= 640) return MediaType.mobile;
|
||||
if (width <= 768) return MediaType.tablet;
|
||||
return MediaType.pc;
|
||||
})();
|
||||
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);
|
||||
}, []);
|
||||
const handleWindowResize = () => setWidth(window.innerWidth)
|
||||
window.addEventListener('resize', handleWindowResize)
|
||||
return () => window.removeEventListener('resize', handleWindowResize)
|
||||
}, [])
|
||||
|
||||
return media;
|
||||
return media
|
||||
}
|
||||
|
||||
export default useBreakpoints
|
||||
export default useBreakpoints
|
||||
|
||||
+1
-2
@@ -12,7 +12,6 @@ export const getLocaleOnClient = (): Locale => {
|
||||
export const setLocaleOnClient = (locale: Locale, notReload?: boolean) => {
|
||||
Cookies.set(LOCALE_COOKIE_NAME, locale)
|
||||
changeLanguage(locale)
|
||||
if (!notReload) {
|
||||
if (!notReload)
|
||||
location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ 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 '.'
|
||||
import type { Locale } from '.'
|
||||
|
||||
const resources = {
|
||||
'en': {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createInstance } from 'i18next'
|
||||
import resourcesToBackend from 'i18next-resources-to-backend'
|
||||
import { initReactI18next } from 'react-i18next/initReactI18next'
|
||||
import { Locale } from '.'
|
||||
import type { Locale } from '.'
|
||||
|
||||
// https://locize.com/blog/next-13-app-dir-i18n/
|
||||
const initI18next = async (lng: Locale, ns: string) => {
|
||||
@@ -21,6 +21,6 @@ export async function useTranslation(lng: Locale, ns = '', options: Record<strin
|
||||
const i18nextInstance = await initI18next(lng, ns)
|
||||
return {
|
||||
t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix),
|
||||
i18n: i18nextInstance
|
||||
i18n: i18nextInstance,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-18
@@ -1,29 +1,29 @@
|
||||
const translation = {
|
||||
common: {
|
||||
welcome: "Welcome to use",
|
||||
appUnavailable: "App is unavailable",
|
||||
appUnkonwError: "App is unavailable",
|
||||
optional: "Optional"
|
||||
welcome: 'Welcome to use',
|
||||
appUnavailable: 'App is unavailable',
|
||||
appUnkonwError: 'App is unavailable',
|
||||
optional: 'Optional',
|
||||
},
|
||||
generation: {
|
||||
queryTitle: "Query content",
|
||||
queryPlaceholder: "Write your query content...",
|
||||
run: "RUN",
|
||||
copy: "Copy",
|
||||
title: "AI Completion",
|
||||
noData: "AI will give you what you want here.",
|
||||
queryTitle: 'Query content',
|
||||
queryPlaceholder: 'Write your query content...',
|
||||
run: 'RUN',
|
||||
copy: 'Copy',
|
||||
title: 'AI Completion',
|
||||
noData: 'AI will give you what you want here.',
|
||||
privacyPolicyLeft:
|
||||
"Please read the ",
|
||||
'Please read the ',
|
||||
privacyPolicyMiddle:
|
||||
"privacy policy",
|
||||
'privacy policy',
|
||||
privacyPolicyRight:
|
||||
" provided by the app developer.",
|
||||
' provided by the app developer.',
|
||||
},
|
||||
errorMessage: {
|
||||
valueOfVarRequired: "Variables value can not be empty",
|
||||
queryRequired: "Request text is required.",
|
||||
waitForResponse: "Please wait for the response to the previous message to complete.",
|
||||
valueOfVarRequired: 'Variables value can not be empty',
|
||||
queryRequired: 'Request text is required.',
|
||||
waitForResponse: 'Please wait for the response to the previous message to complete.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default translation;
|
||||
export default translation
|
||||
|
||||
+18
-18
@@ -1,26 +1,26 @@
|
||||
const translation = {
|
||||
common: {
|
||||
welcome: "欢迎使用",
|
||||
appUnavailable: "应用不可用",
|
||||
appUnkonwError: "应用不可用",
|
||||
optional: "可选"
|
||||
welcome: '欢迎使用',
|
||||
appUnavailable: '应用不可用',
|
||||
appUnkonwError: '应用不可用',
|
||||
optional: '可选',
|
||||
},
|
||||
generation: {
|
||||
queryTitle: "查询内容",
|
||||
queryPlaceholder: "请输入文本内容",
|
||||
run: "运行",
|
||||
copy: "拷贝",
|
||||
title: "AI 智能书写",
|
||||
noData: "AI 会在这里给你惊喜。",
|
||||
privacyPolicyLeft: "请阅读由该应用开发者提供的",
|
||||
privacyPolicyMiddle: "隐私政策",
|
||||
privacyPolicyRight: "。",
|
||||
queryTitle: '查询内容',
|
||||
queryPlaceholder: '请输入文本内容',
|
||||
run: '运行',
|
||||
copy: '拷贝',
|
||||
title: 'AI 智能书写',
|
||||
noData: 'AI 会在这里给你惊喜。',
|
||||
privacyPolicyLeft: '请阅读由该应用开发者提供的',
|
||||
privacyPolicyMiddle: '隐私政策',
|
||||
privacyPolicyRight: '。',
|
||||
},
|
||||
errorMessage: {
|
||||
valueOfVarRequired: "变量值必填",
|
||||
queryRequired: "主要文本必填",
|
||||
waitForResponse: "请等待上条信息响应完成",
|
||||
valueOfVarRequired: '变量值必填',
|
||||
queryRequired: '主要文本必填',
|
||||
waitForResponse: '请等待上条信息响应完成',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default translation;
|
||||
export default translation
|
||||
|
||||
@@ -23,7 +23,7 @@ const translation = {
|
||||
},
|
||||
actionMsg: {
|
||||
copySuccessfully: 'Copied successfully',
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
@@ -23,7 +23,7 @@ const translation = {
|
||||
},
|
||||
actionMsg: {
|
||||
copySuccessfully: '复制成功',
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default translation
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ const nextConfig = {
|
||||
typescript: {
|
||||
// https://nextjs.org/docs/api-reference/next.config.js/ignoring-typescript-errors
|
||||
ignoreBuildErrors: true,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
||||
+13
-3
@@ -7,7 +7,9 @@
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"fix": "next lint --fix"
|
||||
"fix": "next lint --fix",
|
||||
"eslint-fix": "eslint . --fix",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "^0.2.32",
|
||||
@@ -28,6 +30,7 @@
|
||||
"eslint": "8.36.0",
|
||||
"eslint-config-next": "13.2.4",
|
||||
"eventsource-parser": "^1.0.0",
|
||||
"husky": "^8.0.3",
|
||||
"i18next": "^22.4.13",
|
||||
"i18next-resources-to-backend": "^1.1.3",
|
||||
"immer": "^9.0.19",
|
||||
@@ -57,14 +60,21 @@
|
||||
},
|
||||
"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",
|
||||
"lint-staged": "^13.2.2",
|
||||
"postcss": "^8.4.21",
|
||||
"tailwindcss": "^3.2.7"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.js?(x)": [
|
||||
"eslint --fix"
|
||||
],
|
||||
"**/*.ts?(x)": [
|
||||
"eslint --fix"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -46,15 +46,15 @@ module.exports = {
|
||||
indigo: {
|
||||
25: '#F5F8FF',
|
||||
100: '#E0EAFF',
|
||||
600: '#444CE7'
|
||||
}
|
||||
600: '#444CE7',
|
||||
},
|
||||
},
|
||||
screens: {
|
||||
'mobile': '100px',
|
||||
mobile: '100px',
|
||||
// => @media (min-width: 100px) { ... }
|
||||
'tablet': '640px', // 391
|
||||
tablet: '640px', // 391
|
||||
// => @media (min-width: 600px) { ... }
|
||||
'pc': '769px',
|
||||
pc: '769px',
|
||||
// => @media (min-width: 769px) { ... }
|
||||
},
|
||||
},
|
||||
|
||||
+1
-1
@@ -40,4 +40,4 @@
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -1,4 +1,4 @@
|
||||
import { Locale } from '@/i18n'
|
||||
import type { Locale } from '@/i18n'
|
||||
|
||||
export type AppInfo = {
|
||||
title: string
|
||||
@@ -9,31 +9,31 @@ export type AppInfo = {
|
||||
}
|
||||
|
||||
export type PromptVariable = {
|
||||
key: string,
|
||||
name: string,
|
||||
type: "string" | "number" | "select",
|
||||
default?: string | number,
|
||||
key: string
|
||||
name: string
|
||||
type: 'string' | 'number' | 'select'
|
||||
default?: string | number
|
||||
required?: boolean
|
||||
options?: string[]
|
||||
max_length?: number
|
||||
}
|
||||
|
||||
export type PromptConfig = {
|
||||
prompt_template: string,
|
||||
prompt_variables: PromptVariable[],
|
||||
prompt_template: string
|
||||
prompt_variables: PromptVariable[]
|
||||
}
|
||||
|
||||
export type TextTypeFormItem = {
|
||||
label: string,
|
||||
variable: string,
|
||||
label: string
|
||||
variable: string
|
||||
required: boolean
|
||||
max_length: number
|
||||
}
|
||||
|
||||
export type SelectTypeFormItem = {
|
||||
label: string,
|
||||
variable: string,
|
||||
required: boolean,
|
||||
label: string
|
||||
variable: string
|
||||
required: boolean
|
||||
options: string[]
|
||||
}
|
||||
/**
|
||||
|
||||
+35
-35
@@ -38,15 +38,15 @@ module.exports = ({ theme }) => ({
|
||||
'--tw-prose-invert-td-borders': theme('colors.zinc.700'),
|
||||
|
||||
// Base
|
||||
color: 'var(--tw-prose-body)',
|
||||
fontSize: theme('fontSize.sm')[0],
|
||||
lineHeight: theme('lineHeight.7'),
|
||||
'color': 'var(--tw-prose-body)',
|
||||
'fontSize': theme('fontSize.sm')[0],
|
||||
'lineHeight': theme('lineHeight.7'),
|
||||
|
||||
// Layout
|
||||
'> *': {
|
||||
maxWidth: theme('maxWidth.2xl'),
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto',
|
||||
'maxWidth': theme('maxWidth.2xl'),
|
||||
'marginLeft': 'auto',
|
||||
'marginRight': 'auto',
|
||||
'@screen lg': {
|
||||
maxWidth: theme('maxWidth.3xl'),
|
||||
marginLeft: `calc(50% - min(50%, ${theme('maxWidth.lg')}))`,
|
||||
@@ -55,7 +55,7 @@ module.exports = ({ theme }) => ({
|
||||
},
|
||||
|
||||
// Text
|
||||
p: {
|
||||
'p': {
|
||||
marginTop: theme('spacing.6'),
|
||||
marginBottom: theme('spacing.6'),
|
||||
},
|
||||
@@ -65,7 +65,7 @@ module.exports = ({ theme }) => ({
|
||||
},
|
||||
|
||||
// Lists
|
||||
ol: {
|
||||
'ol': {
|
||||
listStyleType: 'decimal',
|
||||
marginTop: theme('spacing.5'),
|
||||
marginBottom: theme('spacing.5'),
|
||||
@@ -98,13 +98,13 @@ module.exports = ({ theme }) => ({
|
||||
'ol[type="1"]': {
|
||||
listStyleType: 'decimal',
|
||||
},
|
||||
ul: {
|
||||
'ul': {
|
||||
listStyleType: 'disc',
|
||||
marginTop: theme('spacing.5'),
|
||||
marginBottom: theme('spacing.5'),
|
||||
paddingLeft: '1.625rem',
|
||||
},
|
||||
li: {
|
||||
'li': {
|
||||
marginTop: theme('spacing.2'),
|
||||
marginBottom: theme('spacing.2'),
|
||||
},
|
||||
@@ -140,14 +140,14 @@ module.exports = ({ theme }) => ({
|
||||
},
|
||||
|
||||
// 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')})`,
|
||||
'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')})`,
|
||||
@@ -159,7 +159,7 @@ module.exports = ({ theme }) => ({
|
||||
},
|
||||
|
||||
// Quotes
|
||||
blockquote: {
|
||||
'blockquote': {
|
||||
fontWeight: '500',
|
||||
fontStyle: 'italic',
|
||||
color: 'var(--tw-prose-quotes)',
|
||||
@@ -178,14 +178,14 @@ module.exports = ({ theme }) => ({
|
||||
},
|
||||
|
||||
// Headings
|
||||
h1: {
|
||||
'h1': {
|
||||
color: 'var(--tw-prose-headings)',
|
||||
fontWeight: '700',
|
||||
fontSize: theme('fontSize.2xl')[0],
|
||||
...theme('fontSize.2xl')[1],
|
||||
marginBottom: theme('spacing.2'),
|
||||
},
|
||||
h2: {
|
||||
'h2': {
|
||||
color: 'var(--tw-prose-headings)',
|
||||
fontWeight: '600',
|
||||
fontSize: theme('fontSize.lg')[0],
|
||||
@@ -193,7 +193,7 @@ module.exports = ({ theme }) => ({
|
||||
marginTop: theme('spacing.16'),
|
||||
marginBottom: theme('spacing.2'),
|
||||
},
|
||||
h3: {
|
||||
'h3': {
|
||||
color: 'var(--tw-prose-headings)',
|
||||
fontSize: theme('fontSize.base')[0],
|
||||
...theme('fontSize.base')[1],
|
||||
@@ -211,7 +211,7 @@ module.exports = ({ theme }) => ({
|
||||
marginTop: '0',
|
||||
marginBottom: '0',
|
||||
},
|
||||
figcaption: {
|
||||
'figcaption': {
|
||||
color: 'var(--tw-prose-captions)',
|
||||
fontSize: theme('fontSize.xs')[0],
|
||||
...theme('fontSize.xs')[1],
|
||||
@@ -219,7 +219,7 @@ module.exports = ({ theme }) => ({
|
||||
},
|
||||
|
||||
// Tables
|
||||
table: {
|
||||
'table': {
|
||||
width: '100%',
|
||||
tableLayout: 'auto',
|
||||
textAlign: 'left',
|
||||
@@ -227,7 +227,7 @@ module.exports = ({ theme }) => ({
|
||||
marginBottom: theme('spacing.8'),
|
||||
lineHeight: theme('lineHeight.6'),
|
||||
},
|
||||
thead: {
|
||||
'thead': {
|
||||
borderBottomWidth: '1px',
|
||||
borderBottomColor: 'var(--tw-prose-th-borders)',
|
||||
},
|
||||
@@ -255,7 +255,7 @@ module.exports = ({ theme }) => ({
|
||||
'tbody td': {
|
||||
verticalAlign: 'baseline',
|
||||
},
|
||||
tfoot: {
|
||||
'tfoot': {
|
||||
borderTopWidth: '1px',
|
||||
borderTopColor: 'var(--tw-prose-th-borders)',
|
||||
},
|
||||
@@ -276,13 +276,13 @@ module.exports = ({ theme }) => ({
|
||||
},
|
||||
|
||||
// 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'),
|
||||
'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)',
|
||||
@@ -291,14 +291,14 @@ module.exports = ({ theme }) => ({
|
||||
':is(h1, h2, h3) a': {
|
||||
fontWeight: 'inherit',
|
||||
},
|
||||
strong: {
|
||||
'strong': {
|
||||
color: 'var(--tw-prose-bold)',
|
||||
fontWeight: '600',
|
||||
},
|
||||
':is(a, blockquote, thead th) strong': {
|
||||
color: 'inherit',
|
||||
},
|
||||
code: {
|
||||
'code': {
|
||||
color: 'var(--tw-prose-code)',
|
||||
borderRadius: theme('borderRadius.lg'),
|
||||
paddingTop: theme('padding.1'),
|
||||
|
||||
+7
-5
@@ -1,4 +1,4 @@
|
||||
import { PromptVariable, UserInputFormItem } from '@/types/app'
|
||||
import type { PromptVariable, UserInputFormItem } from '@/types/app'
|
||||
|
||||
export function replaceVarWithValues(str: string, promptVariables: PromptVariable[], inputs: Record<string, any>) {
|
||||
return str.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
|
||||
@@ -12,11 +12,12 @@ export function replaceVarWithValues(str: string, promptVariables: PromptVariabl
|
||||
}
|
||||
|
||||
export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | null) => {
|
||||
if (!useInputs) return []
|
||||
if (!useInputs)
|
||||
return []
|
||||
const promptVariables: PromptVariable[] = []
|
||||
useInputs.forEach((item: any) => {
|
||||
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') {
|
||||
promptVariables.push({
|
||||
key: content.variable,
|
||||
@@ -26,7 +27,8 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] |
|
||||
max_length: content.max_length,
|
||||
options: [],
|
||||
})
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
promptVariables.push({
|
||||
key: content.variable,
|
||||
name: content.label,
|
||||
@@ -37,4 +39,4 @@ export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] |
|
||||
}
|
||||
})
|
||||
return promptVariables
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user