mirror of
https://github.com/coaidev/coai.git
synced 2025-05-23 23:10:13 +09:00
fix adapter of select group in mobile
This commit is contained in:
parent
9e20d56ba2
commit
edae895fc3
@ -319,12 +319,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.input-options {
|
.input-options {
|
||||||
width: max-content;
|
|
||||||
margin: 16px auto 2px;
|
margin: 16px auto 2px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: nowrap;
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
height: min-content;
|
height: min-content;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,14 @@
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
&.mobile {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
& span {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.select-group-item {
|
.select-group-item {
|
||||||
padding: 0.35rem 0.5rem;
|
padding: 0.35rem 0.5rem;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "./ui/select";
|
||||||
|
import { mobile } from "../utils.ts";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
type SelectGroupProps = {
|
type SelectGroupProps = {
|
||||||
current: string;
|
current: string;
|
||||||
list: string[];
|
list: string[];
|
||||||
@ -5,7 +15,30 @@ type SelectGroupProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function SelectGroup(props: SelectGroupProps) {
|
function SelectGroup(props: SelectGroupProps) {
|
||||||
return (
|
const [state, setState] = useState(mobile);
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("resize", () => {
|
||||||
|
setState(mobile);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return state ? (
|
||||||
|
<Select
|
||||||
|
value={props.current}
|
||||||
|
onValueChange={(value: string) => props.onChange?.(value)}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="select-group mobile">
|
||||||
|
<SelectValue placeholder={props.current} />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{props.list.map((select: string, idx: number) => (
|
||||||
|
<SelectItem key={idx} value={select}>
|
||||||
|
{select}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
) : (
|
||||||
<div className={`select-group`}>
|
<div className={`select-group`}>
|
||||||
{props.list.map((select: string, idx: number) => (
|
{props.list.map((select: string, idx: number) => (
|
||||||
<div
|
<div
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
export const version: string = "2.7.0";
|
export const version: string = "2.8.0";
|
||||||
export const deploy: boolean = true;
|
export const deploy: boolean = false;
|
||||||
export let rest_api: string = "http://localhost:8094";
|
export let rest_api: string = "http://localhost:8094";
|
||||||
export let ws_api: string = "ws://localhost:8094";
|
export let ws_api: string = "ws://localhost:8094";
|
||||||
|
|
||||||
@ -11,6 +11,14 @@ if (deploy) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const tokenField = deploy ? "token" : "token-dev";
|
export const tokenField = deploy ? "token" : "token-dev";
|
||||||
|
export const supportModels: string[] = [
|
||||||
|
"GPT-3.5",
|
||||||
|
"GPT-3.5-16k",
|
||||||
|
"GPT-4",
|
||||||
|
"GPT-4-32k",
|
||||||
|
"Claude-2",
|
||||||
|
"Claude-2-100k",
|
||||||
|
];
|
||||||
|
|
||||||
export function login() {
|
export function login() {
|
||||||
location.href = "https://deeptrain.lightxi.com/login?app=chatnio";
|
location.href = "https://deeptrain.lightxi.com/login?app=chatnio";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {ws_api} from "../conf.ts";
|
import { ws_api } from "../conf.ts";
|
||||||
|
|
||||||
export const endpoint = `${ws_api}/generation/create`;
|
export const endpoint = `${ws_api}/generation/create`;
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ export type GenerationForm = {
|
|||||||
token: string;
|
token: string;
|
||||||
prompt: string;
|
prompt: string;
|
||||||
model: string;
|
model: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type GenerationSegmentResponse = {
|
export type GenerationSegmentResponse = {
|
||||||
message: string;
|
message: string;
|
||||||
@ -14,12 +14,12 @@ export type GenerationSegmentResponse = {
|
|||||||
end: boolean;
|
end: boolean;
|
||||||
error: string;
|
error: string;
|
||||||
hash: string;
|
hash: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type MessageEvent = {
|
export type MessageEvent = {
|
||||||
message: string;
|
message: string;
|
||||||
quota: number;
|
quota: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class GenerationManager {
|
export class GenerationManager {
|
||||||
protected processing: boolean;
|
protected processing: boolean;
|
||||||
@ -31,88 +31,92 @@ export class GenerationManager {
|
|||||||
protected onFinished?: (hash: string) => void;
|
protected onFinished?: (hash: string) => void;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.processing = false;
|
this.processing = false;
|
||||||
|
this.connection = null;
|
||||||
|
this.message = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public setProcessingChangeHandler(
|
||||||
|
handler: (processing: boolean) => void,
|
||||||
|
): void {
|
||||||
|
this.onProcessingChange = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setMessageHandler(handler: (message: MessageEvent) => void): void {
|
||||||
|
this.onMessage = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setErrorHandler(handler: (error: string) => void): void {
|
||||||
|
this.onError = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setFinishedHandler(handler: (hash: string) => void): void {
|
||||||
|
this.onFinished = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isProcessing(): boolean {
|
||||||
|
return this.processing;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setProcessing(processing: boolean): boolean {
|
||||||
|
this.processing = processing;
|
||||||
|
if (!processing) {
|
||||||
this.connection = null;
|
this.connection = null;
|
||||||
this.message = "";
|
this.message = "";
|
||||||
}
|
}
|
||||||
|
this.onProcessingChange?.(processing);
|
||||||
|
return processing;
|
||||||
|
}
|
||||||
|
|
||||||
public setProcessingChangeHandler(handler: (processing: boolean) => void): void {
|
public getConnection(): WebSocket | null {
|
||||||
this.onProcessingChange = handler;
|
return this.connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected handleMessage(message: GenerationSegmentResponse): void {
|
||||||
|
if (message.error && message.end) {
|
||||||
|
this.onError?.(message.error);
|
||||||
|
this.setProcessing(false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setMessageHandler(handler: (message: MessageEvent) => void): void {
|
this.message += message.message;
|
||||||
this.onMessage = handler;
|
this.onMessage?.({
|
||||||
}
|
message: this.message,
|
||||||
|
quota: message.quota,
|
||||||
|
});
|
||||||
|
|
||||||
public setErrorHandler(handler: (error: string) => void): void {
|
if (message.end) {
|
||||||
this.onError = handler;
|
this.onFinished?.(message.hash);
|
||||||
|
this.setProcessing(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public setFinishedHandler(handler: (hash: string) => void): void {
|
public generate(prompt: string, model: string) {
|
||||||
this.onFinished = handler;
|
this.setProcessing(true);
|
||||||
}
|
const token = localStorage.getItem("token") || "";
|
||||||
|
if (token) {
|
||||||
public isProcessing(): boolean {
|
this.connection = new WebSocket(endpoint);
|
||||||
return this.processing;
|
this.connection.onopen = () => {
|
||||||
}
|
this.connection?.send(
|
||||||
|
JSON.stringify({ token, prompt, model } as GenerationForm),
|
||||||
protected setProcessing(processing: boolean): boolean {
|
);
|
||||||
this.processing = processing;
|
};
|
||||||
if (!processing) {
|
this.connection.onmessage = (event) => {
|
||||||
this.connection = null;
|
this.handleMessage(JSON.parse(event.data) as GenerationSegmentResponse);
|
||||||
this.message = "";
|
};
|
||||||
}
|
this.connection.onclose = () => {
|
||||||
this.onProcessingChange?.(processing);
|
|
||||||
return processing;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getConnection(): WebSocket | null {
|
|
||||||
return this.connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected handleMessage(message: GenerationSegmentResponse): void {
|
|
||||||
if (message.error && message.end) {
|
|
||||||
this.onError?.(message.error);
|
|
||||||
this.setProcessing(false);
|
this.setProcessing(false);
|
||||||
return;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
this.message += message.message;
|
|
||||||
this.onMessage?.({
|
|
||||||
message: this.message,
|
|
||||||
quota: message.quota,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (message.end) {
|
|
||||||
this.onFinished?.(message.hash);
|
|
||||||
this.setProcessing(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public generate(prompt: string, model: string) {
|
public generateWithBlock(prompt: string, model: string): boolean {
|
||||||
this.setProcessing(true);
|
if (this.isProcessing()) {
|
||||||
const token = localStorage.getItem("token") || "";
|
return false;
|
||||||
if (token) {
|
|
||||||
this.connection = new WebSocket(endpoint);
|
|
||||||
this.connection.onopen = () => {
|
|
||||||
this.connection?.send(JSON.stringify({ token, prompt, model } as GenerationForm));
|
|
||||||
};
|
|
||||||
this.connection.onmessage = (event) => {
|
|
||||||
this.handleMessage(JSON.parse(event.data) as GenerationSegmentResponse);
|
|
||||||
};
|
|
||||||
this.connection.onclose = () => {
|
|
||||||
this.setProcessing(false);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public generateWithBlock(prompt: string, model: string): boolean {
|
|
||||||
if (this.isProcessing()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.generate(prompt, model);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
this.generate(prompt, model);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const manager = new GenerationManager();
|
export const manager = new GenerationManager();
|
||||||
|
@ -143,16 +143,17 @@ const resources = {
|
|||||||
"max-length-prompt":
|
"max-length-prompt":
|
||||||
"The content has been truncated due to the context length limit",
|
"The content has been truncated due to the context length limit",
|
||||||
},
|
},
|
||||||
"generate": {
|
generate: {
|
||||||
"title": "AI Project Generator",
|
title: "AI Project Generator",
|
||||||
"input-placeholder": "generate a python game",
|
"input-placeholder": "generate a python game",
|
||||||
"failed": "Generate failed",
|
failed: "Generate failed",
|
||||||
"reason": "Reason: ",
|
reason: "Reason: ",
|
||||||
"success": "Generate success",
|
success: "Generate success",
|
||||||
"success-prompt": "Project generated successfully! Please select the download format.",
|
"success-prompt":
|
||||||
"empty": "generating...",
|
"Project generated successfully! Please select the download format.",
|
||||||
"download": "Download {{name}} format",
|
empty: "generating...",
|
||||||
}
|
download: "Download {{name}} format",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cn: {
|
cn: {
|
||||||
@ -283,16 +284,16 @@ const resources = {
|
|||||||
"max-length": "内容过长",
|
"max-length": "内容过长",
|
||||||
"max-length-prompt": "由于上下文长度限制,内容已被截取",
|
"max-length-prompt": "由于上下文长度限制,内容已被截取",
|
||||||
},
|
},
|
||||||
"generate": {
|
generate: {
|
||||||
"title": "AI 项目生成器",
|
title: "AI 项目生成器",
|
||||||
"input-placeholder": "生成一个python小游戏",
|
"input-placeholder": "生成一个python小游戏",
|
||||||
"failed": "生成失败",
|
failed: "生成失败",
|
||||||
"reason": "原因:",
|
reason: "原因:",
|
||||||
"success": "生成成功",
|
success: "生成成功",
|
||||||
"success-prompt": "成功生成项目!请选择下载格式。",
|
"success-prompt": "成功生成项目!请选择下载格式。",
|
||||||
"empty": "生成中...",
|
empty: "生成中...",
|
||||||
"download": "下载 {{name}} 格式",
|
download: "下载 {{name}} 格式",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ru: {
|
ru: {
|
||||||
@ -436,13 +437,14 @@ const resources = {
|
|||||||
generate: {
|
generate: {
|
||||||
title: "Генератор AI проектов",
|
title: "Генератор AI проектов",
|
||||||
"input-placeholder": "сгенерировать python игру",
|
"input-placeholder": "сгенерировать python игру",
|
||||||
"failed": "Генерация не удалась",
|
failed: "Генерация не удалась",
|
||||||
"reason": "Причина: ",
|
reason: "Причина: ",
|
||||||
"success": "Генерация успешна",
|
success: "Генерация успешна",
|
||||||
"success-prompt": "Проект успешно сгенерирован! Пожалуйста, выберите формат загрузки.",
|
"success-prompt":
|
||||||
"empty": "генерация...",
|
"Проект успешно сгенерирован! Пожалуйста, выберите формат загрузки.",
|
||||||
"download": "Загрузить {{name}} формат",
|
empty: "генерация...",
|
||||||
}
|
download: "Загрузить {{name}} формат",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import "../assets/generation.less";
|
import "../assets/generation.less";
|
||||||
import {useDispatch, useSelector} from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { selectAuthenticated } from "../store/auth.ts";
|
import { selectAuthenticated } from "../store/auth.ts";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Button } from "../components/ui/button.tsx";
|
import { Button } from "../components/ui/button.tsx";
|
||||||
import {ChevronLeft, Cloud, FileDown, Info, LogIn, Send} from "lucide-react";
|
import { ChevronLeft, Cloud, FileDown, Info, LogIn, Send } from "lucide-react";
|
||||||
import {login, rest_api} from "../conf.ts";
|
import { login, rest_api, supportModels } from "../conf.ts";
|
||||||
import router from "../router.ts";
|
import router from "../router.ts";
|
||||||
import { Input } from "../components/ui/input.tsx";
|
import { Input } from "../components/ui/input.tsx";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import SelectGroup from "../components/SelectGroup.tsx";
|
import SelectGroup from "../components/SelectGroup.tsx";
|
||||||
import {manager} from "../conversation/generation.ts";
|
import { manager } from "../conversation/generation.ts";
|
||||||
import {useToast} from "../components/ui/use-toast.ts";
|
import { useToast } from "../components/ui/use-toast.ts";
|
||||||
import {handleGenerationData} from "../utils.ts";
|
import { handleGenerationData } from "../utils.ts";
|
||||||
import {selectModel, setModel} from "../store/chat.ts";
|
import { selectModel, setModel } from "../store/chat.ts";
|
||||||
|
|
||||||
type WrapperProps = {
|
type WrapperProps = {
|
||||||
onSend?: (value: string, model: string) => boolean;
|
onSend?: (value: string, model: string) => boolean;
|
||||||
@ -22,10 +22,10 @@ function Wrapper({ onSend }: WrapperProps) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const [ stayed, setStayed ] = useState<boolean>(false);
|
const [stayed, setStayed] = useState<boolean>(false);
|
||||||
const [ hash, setHash ] = useState<string>("");
|
const [hash, setHash] = useState<string>("");
|
||||||
const [ data, setData ] = useState<string>("");
|
const [data, setData] = useState<string>("");
|
||||||
const [ quota, setQuota ] = useState<number>(0);
|
const [quota, setQuota] = useState<number>(0);
|
||||||
const model = useSelector(selectModel);
|
const model = useSelector(selectModel);
|
||||||
const modelRef = useRef(model);
|
const modelRef = useRef(model);
|
||||||
const auth = useSelector(selectAuthenticated);
|
const auth = useSelector(selectAuthenticated);
|
||||||
@ -45,21 +45,21 @@ function Wrapper({ onSend }: WrapperProps) {
|
|||||||
manager.setMessageHandler(({ message, quota }) => {
|
manager.setMessageHandler(({ message, quota }) => {
|
||||||
setData(message);
|
setData(message);
|
||||||
setQuota(quota);
|
setQuota(quota);
|
||||||
})
|
});
|
||||||
|
|
||||||
manager.setErrorHandler((err: string) => {
|
manager.setErrorHandler((err: string) => {
|
||||||
toast({
|
toast({
|
||||||
title: t('generate.failed'),
|
title: t("generate.failed"),
|
||||||
description: `${t('generate.reason')} ${err}`,
|
description: `${t("generate.reason")} ${err}`,
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
manager.setFinishedHandler((hash: string) => {
|
manager.setFinishedHandler((hash: string) => {
|
||||||
toast({
|
toast({
|
||||||
title: t('generate.success'),
|
title: t("generate.success"),
|
||||||
description: t('generate.success-prompt'),
|
description: t("generate.success-prompt"),
|
||||||
})
|
});
|
||||||
setHash(hash);
|
setHash(hash);
|
||||||
})
|
});
|
||||||
|
|
||||||
function handleSend(model: string = "gpt-3.5-16k") {
|
function handleSend(model: string = "gpt-3.5-16k") {
|
||||||
const target = ref.current as HTMLInputElement | null;
|
const target = ref.current as HTMLInputElement | null;
|
||||||
@ -81,15 +81,19 @@ function Wrapper({ onSend }: WrapperProps) {
|
|||||||
target.focus();
|
target.focus();
|
||||||
target.removeEventListener("keydown", () => {});
|
target.removeEventListener("keydown", () => {});
|
||||||
target.addEventListener("keydown", (e) => {
|
target.addEventListener("keydown", (e) => {
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
// cannot use model here, because model is not updated
|
// cannot use model here, because model is not updated
|
||||||
handleSend(modelRef.current);
|
handleSend(modelRef.current);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
ref.current && (ref.current as HTMLInputElement).removeEventListener("keydown", () => {});
|
ref.current &&
|
||||||
}
|
(ref.current as HTMLInputElement).removeEventListener(
|
||||||
|
"keydown",
|
||||||
|
() => {},
|
||||||
|
);
|
||||||
|
};
|
||||||
}, [ref]);
|
}, [ref]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -98,35 +102,48 @@ function Wrapper({ onSend }: WrapperProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`generation-wrapper`}>
|
<div className={`generation-wrapper`}>
|
||||||
{
|
{stayed ? (
|
||||||
stayed ?
|
<div className={`box`}>
|
||||||
<div className={`box`}>
|
{quota > 0 && (
|
||||||
{ quota > 0 && <div className={`quota-box`}>
|
<div className={`quota-box`}>
|
||||||
<Cloud className={`h-4 w-4 mr-2`} />
|
<Cloud className={`h-4 w-4 mr-2`} />
|
||||||
{quota}
|
{quota}
|
||||||
</div> }
|
</div>
|
||||||
<pre className={`message-box`}>
|
)}
|
||||||
{ handleGenerationData(data) || t('generate.empty') }
|
<pre className={`message-box`}>
|
||||||
</pre>
|
{handleGenerationData(data) || t("generate.empty")}
|
||||||
{
|
</pre>
|
||||||
hash.length > 0 &&
|
{hash.length > 0 && (
|
||||||
<div className={`hash-box`}>
|
<div className={`hash-box`}>
|
||||||
<a className={`download-box`} href={`${rest_api}/generation/download/tar?hash=${hash}`}>
|
<a
|
||||||
<FileDown className={`h-6 w-6`} />
|
className={`download-box`}
|
||||||
<p>{ t('generate.download', { name: "tar.gz"}) }</p>
|
href={`${rest_api}/generation/download/tar?hash=${hash}`}
|
||||||
</a>
|
>
|
||||||
<a className={`download-box`} href={`${rest_api}/generation/download/zip?hash=${hash}`}>
|
<FileDown className={`h-6 w-6`} />
|
||||||
<FileDown className={`h-6 w-6`} />
|
<p>{t("generate.download", { name: "tar.gz" })}</p>
|
||||||
<p>{ t('generate.download', { name: "zip"}) }</p>
|
</a>
|
||||||
</a>
|
<a
|
||||||
|
className={`download-box`}
|
||||||
</div>
|
href={`${rest_api}/generation/download/zip?hash=${hash}`}
|
||||||
}
|
>
|
||||||
</div> :
|
<FileDown className={`h-6 w-6`} />
|
||||||
<div className={`product`}><img src={`/favicon.ico`} alt={""} />AI Code Generator</div>
|
<p>{t("generate.download", { name: "zip" })}</p>
|
||||||
}
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={`product`}>
|
||||||
|
<img src={`/favicon.ico`} alt={""} />
|
||||||
|
AI Code Generator
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className={`generate-box`}>
|
<div className={`generate-box`}>
|
||||||
<Input className={`input`} ref={ref} placeholder={t('generate.input-placeholder')} />
|
<Input
|
||||||
|
className={`input`}
|
||||||
|
ref={ref}
|
||||||
|
placeholder={t("generate.input-placeholder")}
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
size={`icon`}
|
size={`icon`}
|
||||||
className={`action`}
|
className={`action`}
|
||||||
@ -139,7 +156,7 @@ function Wrapper({ onSend }: WrapperProps) {
|
|||||||
<div className={`model-box`}>
|
<div className={`model-box`}>
|
||||||
<SelectGroup
|
<SelectGroup
|
||||||
current={model}
|
current={model}
|
||||||
list={["GPT-3.5", "GPT-3.5-16k", "GPT-4", "GPT-4-32k"]}
|
list={supportModels}
|
||||||
onChange={(value: string) => {
|
onChange={(value: string) => {
|
||||||
dispatch(setModel(value));
|
dispatch(setModel(value));
|
||||||
}}
|
}}
|
||||||
@ -149,7 +166,7 @@ function Wrapper({ onSend }: WrapperProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
function Generation() {
|
function Generation() {
|
||||||
const [ state, setState ] = useState(false);
|
const [state, setState] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const auth = useSelector(selectAuthenticated);
|
const auth = useSelector(selectAuthenticated);
|
||||||
|
|
||||||
@ -170,7 +187,9 @@ function Generation() {
|
|||||||
</Button>
|
</Button>
|
||||||
<Wrapper
|
<Wrapper
|
||||||
onSend={(prompt: string, model: string) => {
|
onSend={(prompt: string, model: string) => {
|
||||||
console.debug(`[generation] create generation request (prompt: ${prompt}, model: ${model.toLowerCase()})`);
|
console.debug(
|
||||||
|
`[generation] create generation request (prompt: ${prompt}, model: ${model.toLowerCase()})`,
|
||||||
|
);
|
||||||
return manager.generateWithBlock(prompt, model.toLowerCase());
|
return manager.generateWithBlock(prompt, model.toLowerCase());
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -3,7 +3,9 @@ import "../assets/chat.less";
|
|||||||
import { Input } from "../components/ui/input.tsx";
|
import { Input } from "../components/ui/input.tsx";
|
||||||
import { Toggle } from "../components/ui/toggle.tsx";
|
import { Toggle } from "../components/ui/toggle.tsx";
|
||||||
import {
|
import {
|
||||||
ChevronDown, ChevronRight, FolderKanban,
|
ChevronDown,
|
||||||
|
ChevronRight,
|
||||||
|
FolderKanban,
|
||||||
Globe,
|
Globe,
|
||||||
LogIn,
|
LogIn,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
@ -21,7 +23,7 @@ import {
|
|||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import type { RootState } from "../store";
|
import type { RootState } from "../store";
|
||||||
import { selectAuthenticated } from "../store/auth.ts";
|
import { selectAuthenticated } from "../store/auth.ts";
|
||||||
import { login } from "../conf.ts";
|
import { login, supportModels } from "../conf.ts";
|
||||||
import {
|
import {
|
||||||
deleteConversation,
|
deleteConversation,
|
||||||
toggleConversation,
|
toggleConversation,
|
||||||
@ -35,7 +37,7 @@ import {
|
|||||||
useAnimation,
|
useAnimation,
|
||||||
useEffectAsync,
|
useEffectAsync,
|
||||||
} from "../utils.ts";
|
} from "../utils.ts";
|
||||||
import {toast, useToast} from "../components/ui/use-toast.ts";
|
import { toast, useToast } from "../components/ui/use-toast.ts";
|
||||||
import { ConversationInstance, Message } from "../conversation/types.ts";
|
import { ConversationInstance, Message } from "../conversation/types.ts";
|
||||||
import {
|
import {
|
||||||
selectCurrent,
|
selectCurrent,
|
||||||
@ -315,17 +317,20 @@ function ChatWrapper() {
|
|||||||
return (
|
return (
|
||||||
<div className={`chat-container`}>
|
<div className={`chat-container`}>
|
||||||
<div className={`chat-wrapper`}>
|
<div className={`chat-wrapper`}>
|
||||||
{
|
{messages.length > 0 ? (
|
||||||
messages.length > 0 ?
|
<ChatInterface />
|
||||||
<ChatInterface /> :
|
) : (
|
||||||
<div className={`chat-product`}>
|
<div className={`chat-product`}>
|
||||||
<Button variant={`outline`} onClick={() => router.navigate('/generate')}>
|
<Button
|
||||||
<FolderKanban className={`h-4 w-4 mr-1.5`} />
|
variant={`outline`}
|
||||||
{ t('generate.title') }
|
onClick={() => router.navigate("/generate")}
|
||||||
<ChevronRight className={`h-4 w-4 ml-2`} />
|
>
|
||||||
</Button>
|
<FolderKanban className={`h-4 w-4 mr-1.5`} />
|
||||||
</div>
|
{t("generate.title")}
|
||||||
}
|
<ChevronRight className={`h-4 w-4 ml-2`} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className={`chat-input`}>
|
<div className={`chat-input`}>
|
||||||
<div className={`input-wrapper`}>
|
<div className={`input-wrapper`}>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
@ -385,21 +390,19 @@ function ChatWrapper() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className={`input-options`}>
|
<div className={`input-options`}>
|
||||||
<div className="flex items-center space-x-2">
|
<SelectGroup
|
||||||
<SelectGroup
|
current={model}
|
||||||
current={model}
|
list={supportModels}
|
||||||
list={["GPT-3.5", "GPT-3.5-16k", "GPT-4", "GPT-4-32k"]}
|
onChange={(model: string) => {
|
||||||
onChange={(model: string) => {
|
if (!auth && model !== "GPT-3.5") {
|
||||||
if (!auth && model !== "GPT-3.5") {
|
toast({
|
||||||
toast({
|
title: t("login-require"),
|
||||||
title: t("login-require"),
|
});
|
||||||
})
|
return;
|
||||||
return;
|
}
|
||||||
}
|
dispatch(setModel(model));
|
||||||
dispatch(setModel(model));
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -199,20 +199,35 @@ export function useDraggableInput(
|
|||||||
|
|
||||||
export function escapeRegExp(str: string): string {
|
export function escapeRegExp(str: string): string {
|
||||||
// convert \n to [enter], \t to [tab], \r to [return], \s to [space], \" to [quote], \' to [single-quote]
|
// convert \n to [enter], \t to [tab], \r to [return], \s to [space], \" to [quote], \' to [single-quote]
|
||||||
return str.replace(/\\n/g, "\n").replace(/\\t/g, "\t").replace(/\\r/g, "\r").replace(/\\s/g, " ").replace(/\\"/g, "\"").replace(/\\'/g, "'");
|
return str
|
||||||
|
.replace(/\\n/g, "\n")
|
||||||
|
.replace(/\\t/g, "\t")
|
||||||
|
.replace(/\\r/g, "\r")
|
||||||
|
.replace(/\\s/g, " ")
|
||||||
|
.replace(/\\"/g, '"')
|
||||||
|
.replace(/\\'/g, "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleLine(data: string, max_line: number, end?: boolean): string {
|
export function handleLine(
|
||||||
|
data: string,
|
||||||
|
max_line: number,
|
||||||
|
end?: boolean,
|
||||||
|
): string {
|
||||||
const segment = data.split("\n");
|
const segment = data.split("\n");
|
||||||
const line = segment.length;
|
const line = segment.length;
|
||||||
if (line > max_line) {
|
if (line > max_line) {
|
||||||
return (end ?? true) ? segment.slice(line - max_line).join("\n") : segment.slice(0, max_line).join("\n");
|
return end ?? true
|
||||||
|
? segment.slice(line - max_line).join("\n")
|
||||||
|
: segment.slice(0, max_line).join("\n");
|
||||||
} else {
|
} else {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleGenerationData(data: string): string {
|
export function handleGenerationData(data: string): string {
|
||||||
data = data.replace(/{\s*"result":\s*{/g, "").trim().replace(/}\s*$/g, "");
|
data = data
|
||||||
|
.replace(/{\s*"result":\s*{/g, "")
|
||||||
|
.trim()
|
||||||
|
.replace(/}\s*$/g, "");
|
||||||
return handleLine(escapeRegExp(data), 6);
|
return handleLine(escapeRegExp(data), 6);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user