mirror of
https://github.com/coaidev/coai.git
synced 2025-05-19 13:00:14 +09:00
update utils
This commit is contained in:
parent
83213a60fb
commit
a4a2398045
@ -54,8 +54,8 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1;
|
||||
top: -30px;
|
||||
right: 2px;
|
||||
top: -34px;
|
||||
right: 0px;
|
||||
user-select: none;
|
||||
|
||||
p {
|
||||
@ -63,7 +63,6 @@
|
||||
line-height: 1;
|
||||
margin: 0 0 0 6px;
|
||||
padding: 0;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
svg {
|
||||
|
@ -6,15 +6,15 @@ import {
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "./ui/dialog.tsx";
|
||||
import {Maximize, Image, MenuSquare, PanelRight, XSquare} from "lucide-react";
|
||||
import { Maximize, Image, MenuSquare, PanelRight, XSquare } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import "../assets/editor.less";
|
||||
import "@/assets/editor.less";
|
||||
import { Textarea } from "./ui/textarea.tsx";
|
||||
import Markdown from "./Markdown.tsx";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Toggle } from "./ui/toggle.tsx";
|
||||
import { mobile } from "../utils.ts";
|
||||
import {Button} from "./ui/button.tsx";
|
||||
import { mobile } from "@/utils/device.ts";
|
||||
import { Button } from "./ui/button.tsx";
|
||||
|
||||
type RichEditorProps = {
|
||||
value: string;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { AlertCircle, File, FileCheck, Plus, X } from "lucide-react";
|
||||
import "../assets/file.less";
|
||||
import "@/assets/file.less";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@ -12,7 +12,7 @@ import {
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Alert, AlertTitle } from "./ui/alert.tsx";
|
||||
import { useToast } from "./ui/use-toast.ts";
|
||||
import { useDraggableInput } from "../utils.ts";
|
||||
import { useDraggableInput } from "@/utils/dom.ts";
|
||||
|
||||
export type FileObject = {
|
||||
name: string;
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from "./ui/dropdown-menu.tsx";
|
||||
import { setLanguage } from "../i18n.ts";
|
||||
import { setLanguage } from "@/i18n.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function I18nProvider() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import "../assets/loader.less";
|
||||
import "@/assets/loader.less";
|
||||
|
||||
type LoaderProps = {
|
||||
className?: string;
|
||||
|
@ -5,16 +5,16 @@ import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import { parseFile } from "./plugins/file.tsx";
|
||||
import "../assets/markdown/all.less";
|
||||
import "@/assets/markdown/all.less";
|
||||
import { useEffect } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { openDialog as openQuotaDialog } from "../store/quota.ts";
|
||||
import { openDialog as openSubscriptionDialog } from "../store/subscription.ts";
|
||||
import { AppDispatch } from "../store";
|
||||
import {Copy} from "lucide-react";
|
||||
import {copyClipboard} from "../utils.ts";
|
||||
import {useToast} from "./ui/use-toast.ts";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import { openDialog as openQuotaDialog } from "@/store/quota.ts";
|
||||
import { openDialog as openSubscriptionDialog } from "@/store/subscription.ts";
|
||||
import { AppDispatch } from "@/store";
|
||||
import { Copy } from "lucide-react";
|
||||
import { copyClipboard } from "@/utils/dom.ts";
|
||||
import { useToast } from "./ui/use-toast.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type MarkdownProps = {
|
||||
children: string;
|
||||
@ -37,7 +37,6 @@ function Markdown({ children, className }: MarkdownProps) {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
document.querySelectorAll(".file-instance").forEach((el) => {
|
||||
const parent = el.parentElement as HTMLElement;
|
||||
@ -76,12 +75,15 @@ function Markdown({ children, className }: MarkdownProps) {
|
||||
return !inline && match ? (
|
||||
<div className={`markdown-syntax`}>
|
||||
<div className={`markdown-syntax-header`}>
|
||||
<Copy className={`h-3 w-3`} onClick={async () => {
|
||||
await copyClipboard(children.toString());
|
||||
toast({
|
||||
title: t("share.copied"),
|
||||
});
|
||||
}} />
|
||||
<Copy
|
||||
className={`h-3 w-3`}
|
||||
onClick={async () => {
|
||||
await copyClipboard(children.toString());
|
||||
toast({
|
||||
title: t("share.copied"),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<p>{match[1]}</p>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Message } from "../conversation/types.ts";
|
||||
import Markdown from "./Markdown.tsx";
|
||||
import { Message } from "@/conversation/types.ts";
|
||||
import Markdown from "@/components/Markdown.tsx";
|
||||
import {
|
||||
Cloud,
|
||||
CloudFog,
|
||||
@ -8,28 +8,30 @@ import {
|
||||
Loader2,
|
||||
MousePointerSquare,
|
||||
Power,
|
||||
RotateCcw, ScanText,
|
||||
RotateCcw,
|
||||
ScanText,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuContent,
|
||||
ContextMenuItem,
|
||||
ContextMenuTrigger,
|
||||
} from "./ui/context-menu.tsx";
|
||||
} from "@/components/ui/context-menu.tsx";
|
||||
import { filterMessage } from "@/utils/processor.ts";
|
||||
import {
|
||||
copyClipboard,
|
||||
filterMessage, getSelectionTextInArea,
|
||||
saveAsFile,
|
||||
getSelectionTextInArea,
|
||||
useInputValue,
|
||||
} from "../utils.ts";
|
||||
} from "@/utils/dom.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "./ui/tooltip.tsx";
|
||||
import {Ref, useRef, useState} from "react";
|
||||
} from "@/components/ui/tooltip.tsx";
|
||||
import { Ref, useRef, useState } from "react";
|
||||
|
||||
type MessageProps = {
|
||||
message: Message;
|
||||
@ -39,7 +41,7 @@ type MessageProps = {
|
||||
};
|
||||
|
||||
function MessageSegment(props: MessageProps) {
|
||||
const [ copied, setCopied ] = useState<string>("");
|
||||
const [copied, setCopied] = useState<string>("");
|
||||
const { t } = useTranslation();
|
||||
const ref = useRef(null);
|
||||
const { message } = props;
|
||||
@ -74,15 +76,11 @@ function MessageSegment(props: MessageProps) {
|
||||
</div>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
{
|
||||
copied.length > 0 && (
|
||||
<ContextMenuItem
|
||||
onClick={() => copyClipboard(copied)}
|
||||
>
|
||||
<ScanText className={`h-4 w-4 mr-2`} /> {t("message.copy-area")}
|
||||
</ContextMenuItem>
|
||||
)
|
||||
}
|
||||
{copied.length > 0 && (
|
||||
<ContextMenuItem onClick={() => copyClipboard(copied)}>
|
||||
<ScanText className={`h-4 w-4 mr-2`} /> {t("message.copy-area")}
|
||||
</ContextMenuItem>
|
||||
)}
|
||||
<ContextMenuItem
|
||||
onClick={() => copyClipboard(filterMessage(message.content))}
|
||||
>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Button } from "./ui/button.tsx";
|
||||
import { selectMessages } from "../store/chat.ts";
|
||||
import { selectMessages } from "@/store/chat.ts";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { MessageSquarePlus } from "lucide-react";
|
||||
import { toggleConversation } from "../conversation/history.ts";
|
||||
import { toggleConversation } from "@/conversation/history.ts";
|
||||
|
||||
function ProjectLink() {
|
||||
const dispatch = useDispatch();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useRegisterSW } from "virtual:pwa-register/react";
|
||||
import { version } from "../conf.ts";
|
||||
import { version } from "@/conf.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useToast } from "./ui/use-toast.ts";
|
||||
import { useEffect } from "react";
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "./ui/select";
|
||||
import { mobile } from "../utils.ts";
|
||||
import { mobile } from "@/utils/device.ts";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Badge } from "./ui/badge.tsx";
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import NavBar from "./NavBar.tsx";
|
||||
import { ThemeProvider } from "../ThemeProvider.tsx";
|
||||
import DialogManager from "../../dialogs";
|
||||
import { ThemeProvider } from "@/components/ThemeProvider.tsx";
|
||||
import DialogManager from "@/dialogs";
|
||||
|
||||
function AppProvider() {
|
||||
return (
|
||||
|
@ -1,10 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { logout, selectUsername } from "../../store/auth.ts";
|
||||
import {
|
||||
openDialog as openQuotaDialog,
|
||||
quotaSelector,
|
||||
} from "../../store/quota.ts";
|
||||
import { logout, selectUsername } from "@/store/auth.ts";
|
||||
import { openDialog as openQuotaDialog, quotaSelector } from "@/store/quota.ts";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@ -12,8 +9,8 @@ import {
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "../ui/dropdown-menu.tsx";
|
||||
import { Button } from "../ui/button.tsx";
|
||||
} from "@/components/ui/dropdown-menu.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import {
|
||||
BadgeCent,
|
||||
Boxes,
|
||||
@ -23,11 +20,11 @@ import {
|
||||
ListStart,
|
||||
Plug,
|
||||
} from "lucide-react";
|
||||
import { openDialog as openSub } from "../../store/subscription.ts";
|
||||
import { openDialog as openPackageDialog } from "../../store/package.ts";
|
||||
import { openDialog as openInvitationDialog } from "../../store/invitation.ts";
|
||||
import { openDialog as openSharingDialog } from "../../store/sharing.ts";
|
||||
import { openDialog as openApiDialog } from "../../store/api.ts";
|
||||
import { openDialog as openSub } from "@/store/subscription.ts";
|
||||
import { openDialog as openPackageDialog } from "@/store/package.ts";
|
||||
import { openDialog as openInvitationDialog } from "@/store/invitation.ts";
|
||||
import { openDialog as openSharingDialog } from "@/store/sharing.ts";
|
||||
import { openDialog as openApiDialog } from "@/store/api.ts";
|
||||
|
||||
type MenuBarProps = {
|
||||
children: React.ReactNode;
|
||||
|
@ -5,16 +5,16 @@ import {
|
||||
selectAuthenticated,
|
||||
selectUsername,
|
||||
validateToken,
|
||||
} from "../../store/auth.ts";
|
||||
import { Button } from "../ui/button.tsx";
|
||||
} from "@/store/auth.ts";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { Menu } from "lucide-react";
|
||||
import { useEffect } from "react";
|
||||
import { login, tokenField } from "../../conf.ts";
|
||||
import { toggleMenu } from "../../store/menu.ts";
|
||||
import ProjectLink from "../ProjectLink.tsx";
|
||||
import ModeToggle from "../ThemeProvider.tsx";
|
||||
import I18nProvider from "../I18nProvider.tsx";
|
||||
import router from "../../router.tsx";
|
||||
import { login, tokenField } from "@/conf.ts";
|
||||
import { toggleMenu } from "@/store/menu.ts";
|
||||
import ProjectLink from "@/components/ProjectLink.tsx";
|
||||
import ModeToggle from "@/components/ThemeProvider.tsx";
|
||||
import I18nProvider from "@/components/I18nProvider.tsx";
|
||||
import router from "@/router.tsx";
|
||||
import MenuBar from "./MenuBar.tsx";
|
||||
|
||||
function NavMenu() {
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Message } from "../../conversation/types.ts";
|
||||
import { Message } from "@/conversation/types.ts";
|
||||
import { useSelector } from "react-redux";
|
||||
import { selectCurrent, selectMessages } from "../../store/chat.ts";
|
||||
import { Button } from "../ui/button.tsx";
|
||||
import { selectCurrent, selectMessages } from "@/store/chat.ts";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import MessageSegment from "../Message.tsx";
|
||||
import { connectionEvent } from "../../events/connection.ts";
|
||||
import MessageSegment from "@/components/Message.tsx";
|
||||
import { connectionEvent } from "@/events/connection.ts";
|
||||
|
||||
function ChatInterface() {
|
||||
const ref = useRef(null);
|
||||
|
@ -1,19 +1,20 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import FileProvider, { FileObject } from "../FileProvider.tsx";
|
||||
import FileProvider, { FileObject } from "@/components/FileProvider.tsx";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { selectAuthenticated, selectInit } from "../../store/auth.ts";
|
||||
import { selectAuthenticated, selectInit } from "@/store/auth.ts";
|
||||
import {
|
||||
selectMessages,
|
||||
selectModel,
|
||||
selectWeb,
|
||||
setWeb,
|
||||
} from "../../store/chat.ts";
|
||||
import { manager } from "../../conversation/manager.ts";
|
||||
import { formatMessage, triggerInstallApp } from "../../utils.ts";
|
||||
import ChatInterface from "./ChatInterface.tsx";
|
||||
import { Button } from "../ui/button.tsx";
|
||||
import router from "../../router.tsx";
|
||||
} from "@/store/chat.ts";
|
||||
import { manager } from "@/conversation/manager.ts";
|
||||
import { formatMessage } from "@/utils/processor.ts";
|
||||
import { triggerInstallApp } from "@/utils/app.ts";
|
||||
import ChatInterface from "@/components/home/ChatInterface.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import router from "@/router.tsx";
|
||||
import {
|
||||
BookMarked,
|
||||
ChevronRight,
|
||||
@ -26,10 +27,10 @@ import {
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "../ui/tooltip.tsx";
|
||||
import { Toggle } from "../ui/toggle.tsx";
|
||||
import { Input } from "../ui/input.tsx";
|
||||
import EditorProvider from "../EditorProvider.tsx";
|
||||
} from "@/components/ui/tooltip.tsx";
|
||||
import { Toggle } from "@/components/ui/toggle.tsx";
|
||||
import { Input } from "@/components/ui/input.tsx";
|
||||
import EditorProvider from "@/components/EditorProvider.tsx";
|
||||
import ModelSelector from "./ModelSelector.tsx";
|
||||
import {
|
||||
Dialog,
|
||||
@ -37,8 +38,9 @@ import {
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../ui/dialog.tsx";
|
||||
import { version } from "../../conf.ts";
|
||||
} from "@/components/ui/dialog.tsx";
|
||||
import { version } from "@/conf.ts";
|
||||
import { getQueryParam } from "@/utils/path.ts";
|
||||
|
||||
function ChatSpace() {
|
||||
const [open, setOpen] = useState(false);
|
||||
@ -145,8 +147,7 @@ function ChatWrapper() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!init) return;
|
||||
const search = new URLSearchParams(window.location.search);
|
||||
const query = (search.get("q") || "").trim();
|
||||
const query = getQueryParam("q").trim();
|
||||
if (query.length > 0) processSend(query, auth, model, web).then();
|
||||
window.history.replaceState({}, "", "/");
|
||||
}, [init]);
|
||||
|
@ -1,16 +1,17 @@
|
||||
import { toggleConversation } from "../../conversation/history.ts";
|
||||
import { filterMessage, mobile } from "../../utils.ts";
|
||||
import { setMenu } from "../../store/menu.ts";
|
||||
import { toggleConversation } from "@/conversation/history.ts";
|
||||
import { mobile } from "@/utils/device.ts";
|
||||
import { filterMessage } from "@/utils/processor.ts";
|
||||
import { setMenu } from "@/store/menu.ts";
|
||||
import { MessageSquare, MoreHorizontal, Share2, Trash2 } from "lucide-react";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "../ui/dropdown-menu.tsx";
|
||||
} from "@/components/ui/dropdown-menu.tsx";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ConversationInstance } from "../../conversation/types.ts";
|
||||
import { ConversationInstance } from "@/conversation/types.ts";
|
||||
import { useState } from "react";
|
||||
|
||||
type ConversationSegmentProps = {
|
||||
|
@ -1,15 +1,16 @@
|
||||
import SelectGroup, { SelectItemProps } from "../SelectGroup.tsx";
|
||||
import { supportModels } from "../../conf.ts";
|
||||
import { selectModel, setModel } from "../../store/chat.ts";
|
||||
import SelectGroup, { SelectItemProps } from "@/components/SelectGroup.tsx";
|
||||
import { login, supportModels } from "@/conf.ts";
|
||||
import { selectModel, setModel } from "@/store/chat.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { selectAuthenticated } from "../../store/auth.ts";
|
||||
import { useToast } from "../ui/use-toast.ts";
|
||||
import { selectAuthenticated } from "@/store/auth.ts";
|
||||
import { useToast } from "@/components/ui/use-toast.ts";
|
||||
import { useEffect } from "react";
|
||||
import { Model } from "../../conversation/types.ts";
|
||||
import { modelEvent } from "../../events/model.ts";
|
||||
import { isSubscribedSelector } from "../../store/subscription.ts";
|
||||
import {teenagerSelector} from "../../store/package.ts";
|
||||
import { Model } from "@/conversation/types.ts";
|
||||
import { modelEvent } from "@/events/model.ts";
|
||||
import { isSubscribedSelector } from "@/store/subscription.ts";
|
||||
import { teenagerSelector } from "@/store/package.ts";
|
||||
import { ToastAction } from "@/components/ui/toast.tsx";
|
||||
|
||||
function GetModel(name: string): Model {
|
||||
return supportModels.find((model) => model.id === name) as Model;
|
||||
@ -42,30 +43,28 @@ function ModelSelector(props: ModelSelectorProps) {
|
||||
}
|
||||
});
|
||||
|
||||
const list = supportModels.map(
|
||||
(model: Model): SelectItemProps => {
|
||||
const array = ["gpt-4", "claude-2"];
|
||||
if (subscription && array.includes(model.id)) {
|
||||
return {
|
||||
name: model.id,
|
||||
value: model.name,
|
||||
badge: { variant: "gold", name: "plus" },
|
||||
} as SelectItemProps;
|
||||
} else if (student && model.id === "claude-2") {
|
||||
return {
|
||||
name: model.id,
|
||||
value: model.name,
|
||||
badge: { variant: "gold", name: "student" },
|
||||
} as SelectItemProps;
|
||||
}
|
||||
|
||||
const list = supportModels.map((model: Model): SelectItemProps => {
|
||||
const array = ["gpt-4", "claude-2"];
|
||||
if (subscription && array.includes(model.id)) {
|
||||
return {
|
||||
name: model.id,
|
||||
value: model.name,
|
||||
badge: { variant: "gold", name: "plus" },
|
||||
} as SelectItemProps;
|
||||
} else if (student && model.id === "claude-2") {
|
||||
return {
|
||||
name: model.id,
|
||||
value: model.name,
|
||||
badge: { variant: "gold", name: "student" },
|
||||
} as SelectItemProps;
|
||||
}
|
||||
|
||||
return {
|
||||
name: model.id,
|
||||
value: model.name,
|
||||
badge: model.free && { variant: "default", name: "free" }
|
||||
} as SelectItemProps;
|
||||
},
|
||||
);
|
||||
badge: model.free && { variant: "default", name: "free" },
|
||||
} as SelectItemProps;
|
||||
});
|
||||
|
||||
return (
|
||||
<SelectGroup
|
||||
@ -81,6 +80,11 @@ function ModelSelector(props: ModelSelectorProps) {
|
||||
if (!auth && model.auth) {
|
||||
toast({
|
||||
title: t("login-require"),
|
||||
action: (
|
||||
<ToastAction altText={t("login")} onClick={login}>
|
||||
{t("login")}
|
||||
</ToastAction>
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -1,27 +1,23 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState } from "../../store";
|
||||
import { selectAuthenticated, selectUsername } from "../../store/auth.ts";
|
||||
import { selectCurrent, selectHistory } from "../../store/chat.ts";
|
||||
import { RootState } from "@/store";
|
||||
import { selectAuthenticated, selectUsername } from "@/store/auth.ts";
|
||||
import { selectCurrent, selectHistory } from "@/store/chat.ts";
|
||||
import { useRef, useState } from "react";
|
||||
import { ConversationInstance } from "../../conversation/types.ts";
|
||||
import { useToast } from "../ui/use-toast.ts";
|
||||
import {
|
||||
copyClipboard,
|
||||
extractMessage,
|
||||
filterMessage,
|
||||
mobile,
|
||||
useAnimation,
|
||||
useEffectAsync,
|
||||
} from "../../utils.ts";
|
||||
import { ConversationInstance } from "@/conversation/types.ts";
|
||||
import { useToast } from "@/components/ui/use-toast.ts";
|
||||
import { extractMessage, filterMessage } from "@/utils/processor.ts";
|
||||
import { copyClipboard } from "@/utils/dom.ts";
|
||||
import { useEffectAsync, useAnimation } from "@/utils/hook.ts";
|
||||
import { mobile } from "@/utils/device.ts";
|
||||
import {
|
||||
deleteAllConversations,
|
||||
deleteConversation,
|
||||
toggleConversation,
|
||||
updateConversationList,
|
||||
} from "../../conversation/history.ts";
|
||||
import { Button } from "../ui/button.tsx";
|
||||
import { setMenu } from "../../store/menu.ts";
|
||||
} from "@/conversation/history.ts";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { setMenu } from "@/store/menu.ts";
|
||||
import {
|
||||
Copy,
|
||||
Eraser,
|
||||
@ -41,15 +37,12 @@ import {
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "../ui/alert-dialog.tsx";
|
||||
import {
|
||||
getSharedLink,
|
||||
shareConversation,
|
||||
} from "../../conversation/sharing.ts";
|
||||
import { Input } from "../ui/input.tsx";
|
||||
import { login } from "../../conf.ts";
|
||||
import MenuBar from "../app/MenuBar.tsx";
|
||||
import { Separator } from "../ui/separator.tsx";
|
||||
} from "@/components/ui/alert-dialog.tsx";
|
||||
import { getSharedLink, shareConversation } from "@/conversation/sharing.ts";
|
||||
import { Input } from "@/components/ui/input.tsx";
|
||||
import { login } from "@/conf.ts";
|
||||
import MenuBar from "@/components/app/MenuBar.tsx";
|
||||
import { Separator } from "@/components/ui/separator.tsx";
|
||||
|
||||
type Operation = {
|
||||
target: ConversationInstance | null;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { File } from "lucide-react";
|
||||
import { saveAsFile } from "../../utils.ts";
|
||||
import { saveAsFile } from "@/utils/dom.ts";
|
||||
|
||||
/**
|
||||
* file format:
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { tokenField, ws_api } from "../conf.ts";
|
||||
import { tokenField, ws_api } from "@/conf.ts";
|
||||
|
||||
export const endpoint = `${ws_api}/chat`;
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { ChatProps, Connection, StreamMessage } from "./connection.ts";
|
||||
import { Message } from "./types.ts";
|
||||
import { sharingEvent } from "../events/sharing.ts";
|
||||
import { connectionEvent } from "../events/connection.ts";
|
||||
import { AppDispatch } from "../store";
|
||||
import { setMessages } from "../store/chat.ts";
|
||||
import { modelEvent } from "../events/model.ts";
|
||||
import { sharingEvent } from "@/events/sharing.ts";
|
||||
import { connectionEvent } from "@/events/connection.ts";
|
||||
import { AppDispatch } from "@/store";
|
||||
import { setMessages } from "@/store/chat.ts";
|
||||
import { modelEvent } from "@/events/model.ts";
|
||||
|
||||
type ConversationCallback = (idx: number, message: Message[]) => boolean;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ws_api } from "../conf.ts";
|
||||
import { ws_api } from "@/conf.ts";
|
||||
|
||||
export const endpoint = `${ws_api}/generation/create`;
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import axios from "axios";
|
||||
import type { ConversationInstance } from "./types.ts";
|
||||
import { setHistory } from "../store/chat.ts";
|
||||
import { setHistory } from "@/store/chat.ts";
|
||||
import { manager } from "./manager.ts";
|
||||
import { AppDispatch } from "../store";
|
||||
import { AppDispatch } from "@/store";
|
||||
|
||||
export async function updateConversationList(
|
||||
dispatch: AppDispatch,
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { Conversation } from "./conversation";
|
||||
import { ConversationMapper, Message } from "./types.ts";
|
||||
import { loadConversation } from "./history.ts";
|
||||
import { Conversation } from "@/conversation/conversation.ts";
|
||||
import { ConversationMapper, Message } from "@/conversation/types.ts";
|
||||
import { loadConversation } from "@/conversation/history.ts";
|
||||
import {
|
||||
addHistory,
|
||||
removeHistory,
|
||||
setCurrent,
|
||||
setMessages,
|
||||
} from "../store/chat.ts";
|
||||
import { useShared } from "../utils.ts";
|
||||
import { ChatProps } from "./connection.ts";
|
||||
import { AppDispatch } from "../store";
|
||||
import { sharingEvent } from "../events/sharing.ts";
|
||||
} from "@/store/chat.ts";
|
||||
import { useShared } from "@/utils/hook.ts";
|
||||
import { ChatProps } from "@/conversation/connection.ts";
|
||||
import { AppDispatch } from "@/store";
|
||||
import { sharingEvent } from "@/events/sharing.ts";
|
||||
|
||||
export class Manager {
|
||||
conversations: Record<number, Conversation>;
|
||||
|
@ -5,9 +5,9 @@ import {
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../components/ui/dialog.tsx";
|
||||
import { Button } from "../components/ui/button.tsx";
|
||||
import "../assets/api.less";
|
||||
} from "@/components/ui/dialog.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import "@/assets/api.less";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
@ -16,12 +16,13 @@ import {
|
||||
setDialog,
|
||||
keySelector,
|
||||
getApiKey,
|
||||
} from "../store/api.ts";
|
||||
import { Input } from "../components/ui/input.tsx";
|
||||
} from "@/store/api.ts";
|
||||
import { Input } from "@/components/ui/input.tsx";
|
||||
import { Copy, ExternalLink } from "lucide-react";
|
||||
import { useToast } from "../components/ui/use-toast.ts";
|
||||
import { copyClipboard, useEffectAsync } from "../utils.ts";
|
||||
import { selectInit } from "../store/auth.ts";
|
||||
import { useToast } from "@/components/ui/use-toast.ts";
|
||||
import { copyClipboard } from "@/utils/dom.ts";
|
||||
import { useEffectAsync } from "@/utils/hook.ts";
|
||||
import { selectInit } from "@/store/auth.ts";
|
||||
|
||||
function ApiKey() {
|
||||
const { t } = useTranslation();
|
||||
|
@ -5,15 +5,15 @@ import {
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../components/ui/dialog.tsx";
|
||||
import { Button } from "../components/ui/button.tsx";
|
||||
} from "@/components/ui/dialog.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { closeDialog, dialogSelector, setDialog } from "../store/invitation.ts";
|
||||
import { Input } from "../components/ui/input.tsx";
|
||||
import { useToast } from "../components/ui/use-toast.ts";
|
||||
import { closeDialog, dialogSelector, setDialog } from "@/store/invitation.ts";
|
||||
import { Input } from "@/components/ui/input.tsx";
|
||||
import { useToast } from "@/components/ui/use-toast.ts";
|
||||
import { useState } from "react";
|
||||
import { getInvitation } from "../conversation/invitation.ts";
|
||||
import { getInvitation } from "@/conversation/invitation.ts";
|
||||
|
||||
function Invitation() {
|
||||
const { t } = useTranslation();
|
||||
|
@ -5,9 +5,9 @@ import {
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../components/ui/dialog.tsx";
|
||||
import { Button } from "../components/ui/button.tsx";
|
||||
import "../assets/package.less";
|
||||
} from "@/components/ui/dialog.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import "@/assets/package.less";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
@ -17,11 +17,11 @@ import {
|
||||
refreshPackageTask,
|
||||
setDialog,
|
||||
teenagerSelector,
|
||||
} from "../store/package.ts";
|
||||
} from "@/store/package.ts";
|
||||
import { useEffect } from "react";
|
||||
import { Gift } from "lucide-react";
|
||||
import { Separator } from "../components/ui/separator.tsx";
|
||||
import { Badge } from "../components/ui/badge.tsx";
|
||||
import { Separator } from "@/components/ui/separator.tsx";
|
||||
import { Badge } from "@/components/ui/badge.tsx";
|
||||
|
||||
function Package() {
|
||||
const { t } = useTranslation();
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
dialogSelector,
|
||||
refreshQuotaTask,
|
||||
setDialog,
|
||||
} from "../store/quota.ts";
|
||||
} from "@/store/quota.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
@ -13,8 +13,8 @@ import {
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../components/ui/dialog.tsx";
|
||||
import "../assets/quota.less";
|
||||
} from "@/components/ui/dialog.tsx";
|
||||
import "@/assets/quota.less";
|
||||
import {
|
||||
BadgePercent,
|
||||
Cloud,
|
||||
@ -24,10 +24,10 @@ import {
|
||||
Info,
|
||||
Plus,
|
||||
} from "lucide-react";
|
||||
import { Input } from "../components/ui/input.tsx";
|
||||
import { testNumberInputEvent } from "../utils.ts";
|
||||
import { Button } from "../components/ui/button.tsx";
|
||||
import { Separator } from "../components/ui/separator.tsx";
|
||||
import { Input } from "@/components/ui/input.tsx";
|
||||
import { testNumberInputEvent } from "@/utils/dom.ts";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { Separator } from "@/components/ui/separator.tsx";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@ -37,10 +37,10 @@ import {
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTrigger,
|
||||
} from "../components/ui/alert-dialog.tsx";
|
||||
} from "@/components/ui/alert-dialog.tsx";
|
||||
import { AlertDialogTitle } from "@radix-ui/react-alert-dialog";
|
||||
import { buyQuota } from "../conversation/addition.ts";
|
||||
import { useToast } from "../components/ui/use-toast.ts";
|
||||
import { buyQuota } from "@/conversation/addition.ts";
|
||||
import { useToast } from "@/components/ui/use-toast.ts";
|
||||
|
||||
type AmountComponentProps = {
|
||||
amount: number;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import "../assets/share-manager.less";
|
||||
import "@/assets/share-manager.less";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import {
|
||||
@ -6,10 +6,10 @@ import {
|
||||
dataSelector,
|
||||
syncData,
|
||||
deleteData,
|
||||
} from "../store/sharing.ts";
|
||||
import { useToast } from "../components/ui/use-toast.ts";
|
||||
import { selectAuthenticated, selectInit } from "../store/auth.ts";
|
||||
import { useEffectAsync } from "../utils.ts";
|
||||
} from "@/store/sharing.ts";
|
||||
import { useToast } from "@/components/ui/use-toast.ts";
|
||||
import { selectAuthenticated, selectInit } from "@/store/auth.ts";
|
||||
import { useEffectAsync } from "@/utils/hook.ts";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@ -17,7 +17,7 @@ import {
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../components/ui/dialog.tsx";
|
||||
} from "@/components/ui/dialog.tsx";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@ -25,9 +25,9 @@ import {
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "../components/ui/table.tsx";
|
||||
import { closeDialog, setDialog } from "../store/sharing.ts";
|
||||
import { Button } from "../components/ui/button.tsx";
|
||||
} from "@/components/ui/table.tsx";
|
||||
import { closeDialog, setDialog } from "@/store/sharing.ts";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { useMemo } from "react";
|
||||
import { Eye, MoreHorizontal, Trash2 } from "lucide-react";
|
||||
import {
|
||||
@ -35,8 +35,8 @@ import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "../components/ui/dropdown-menu.tsx";
|
||||
import { getSharedLink, SharingPreviewForm } from "../conversation/sharing.ts";
|
||||
} from "@/components/ui/dropdown-menu.tsx";
|
||||
import { getSharedLink, SharingPreviewForm } from "@/conversation/sharing.ts";
|
||||
|
||||
type ShareTableProps = {
|
||||
data: SharingPreviewForm[];
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
refreshSubscriptionTask,
|
||||
setDialog,
|
||||
usageSelector,
|
||||
} from "../store/subscription.ts";
|
||||
} from "@/store/subscription.ts";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@ -15,12 +15,12 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "../components/ui/dialog.tsx";
|
||||
} from "@/components/ui/dialog.tsx";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useToast } from "../components/ui/use-toast.ts";
|
||||
import { useToast } from "@/components/ui/use-toast.ts";
|
||||
import React, { useEffect } from "react";
|
||||
import "../assets/subscription.less";
|
||||
import "@/assets/subscription.less";
|
||||
import {
|
||||
BookText,
|
||||
Calendar,
|
||||
@ -35,16 +35,16 @@ import {
|
||||
ServerCrash,
|
||||
Webhook,
|
||||
} from "lucide-react";
|
||||
import { Button } from "../components/ui/button.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "../components/ui/select.tsx";
|
||||
import { Badge } from "../components/ui/badge.tsx";
|
||||
import { buySubscription } from "../conversation/addition.ts";
|
||||
} from "@/components/ui/select.tsx";
|
||||
import { Badge } from "@/components/ui/badge.tsx";
|
||||
import { buySubscription } from "@/conversation/addition.ts";
|
||||
|
||||
function calc_prize(month: number): number {
|
||||
const base = 32 * month;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Toaster } from "../components/ui/toaster.tsx";
|
||||
import { Toaster } from "@/components/ui/toaster.tsx";
|
||||
import Quota from "./Quota.tsx";
|
||||
import ApiKey from "./ApiKey.tsx";
|
||||
import Package from "./Package.tsx";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { EventCommitter } from "./struct.ts";
|
||||
import { Message } from "../conversation/types.ts";
|
||||
import { Message } from "@/conversation/types.ts";
|
||||
|
||||
export type SharingEvent = {
|
||||
refer: string;
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { useToast } from "../components/ui/use-toast.ts";
|
||||
import { useToast } from "@/components/ui/use-toast.ts";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { ToastAction } from "../components/ui/toast.tsx";
|
||||
import { login } from "../conf.ts";
|
||||
import { ToastAction } from "@/components/ui/toast.tsx";
|
||||
import { login } from "@/conf.ts";
|
||||
import { useEffect } from "react";
|
||||
import Loader from "../components/Loader.tsx";
|
||||
import "../assets/auth.less";
|
||||
import Loader from "@/components/Loader.tsx";
|
||||
import "@/assets/auth.less";
|
||||
import axios from "axios";
|
||||
import { validateToken } from "../store/auth.ts";
|
||||
import { validateToken } from "@/store/auth.ts";
|
||||
import { useDispatch } from "react-redux";
|
||||
import router from "../router.tsx";
|
||||
import router from "@/router.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function Auth() {
|
||||
|
@ -1,17 +1,17 @@
|
||||
import "../assets/generation.less";
|
||||
import "@/assets/generation.less";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "../components/ui/button.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { ChevronLeft, Cloud, FileDown, Send } from "lucide-react";
|
||||
import { rest_api } from "../conf.ts";
|
||||
import router from "../router.tsx";
|
||||
import { Input } from "../components/ui/input.tsx";
|
||||
import { rest_api } from "@/conf.ts";
|
||||
import router from "@/router.tsx";
|
||||
import { Input } from "@/components/ui/input.tsx";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { manager } from "../conversation/generation.ts";
|
||||
import { useToast } from "../components/ui/use-toast.ts";
|
||||
import { handleGenerationData } from "../utils.ts";
|
||||
import { selectModel } from "../store/chat.ts";
|
||||
import ModelSelector from "../components/home/ModelSelector.tsx";
|
||||
import { manager } from "@/conversation/generation.ts";
|
||||
import { useToast } from "@/components/ui/use-toast.ts";
|
||||
import { handleGenerationData } from "@/utils/processor.ts";
|
||||
import { selectModel } from "@/store/chat.ts";
|
||||
import ModelSelector from "@/components/home/ModelSelector.tsx";
|
||||
|
||||
type WrapperProps = {
|
||||
onSend?: (value: string, model: string) => boolean;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import "../assets/home.less";
|
||||
import "../assets/chat.less";
|
||||
import ChatWrapper from "../components/home/ChatWrapper.tsx";
|
||||
import SideBar from "../components/home/SideBar.tsx";
|
||||
import "@/assets/home.less";
|
||||
import "@/assets/chat.less";
|
||||
import ChatWrapper from "@/components/home/ChatWrapper.tsx";
|
||||
import SideBar from "@/components/home/SideBar.tsx";
|
||||
|
||||
function Home() {
|
||||
return (
|
||||
|
@ -1,7 +1,7 @@
|
||||
import "../assets/404.less";
|
||||
import { Button } from "../components/ui/button.tsx";
|
||||
import "@/assets/404.less";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { HelpCircle } from "lucide-react";
|
||||
import router from "../router.tsx";
|
||||
import router from "@/router.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function NotFound() {
|
||||
|
@ -1,20 +1,21 @@
|
||||
import "../assets/sharing.less";
|
||||
import "@/assets/sharing.less";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {
|
||||
viewConversation,
|
||||
ViewData,
|
||||
ViewForm,
|
||||
} from "../conversation/sharing.ts";
|
||||
import { copyClipboard, saveAsFile, useEffectAsync } from "../utils.ts";
|
||||
} from "@/conversation/sharing.ts";
|
||||
import { copyClipboard, saveAsFile } from "@/utils/dom.ts";
|
||||
import { useEffectAsync } from "@/utils/hook.ts";
|
||||
import { useState } from "react";
|
||||
import { Copy, File, HelpCircle, Loader2, MessagesSquare } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import MessageSegment from "../components/Message.tsx";
|
||||
import { Button } from "../components/ui/button.tsx";
|
||||
import router from "../router.tsx";
|
||||
import { useToast } from "../components/ui/use-toast.ts";
|
||||
import { sharingEvent } from "../events/sharing.ts";
|
||||
import { Message } from "../conversation/types.ts";
|
||||
import MessageSegment from "@/components/Message.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import router from "@/router.tsx";
|
||||
import { useToast } from "@/components/ui/use-toast.ts";
|
||||
import { sharingEvent } from "@/events/sharing.ts";
|
||||
import { Message } from "@/conversation/types.ts";
|
||||
|
||||
type SharingFormProps = {
|
||||
refer?: string;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { getKey } from "../conversation/addition.ts";
|
||||
import { getKey } from "@/conversation/addition.ts";
|
||||
import { AppDispatch, RootState } from "./index.ts";
|
||||
|
||||
export const apiSlice = createSlice({
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import axios from "axios";
|
||||
import { tokenField } from "../conf.ts";
|
||||
import { tokenField } from "@/conf.ts";
|
||||
import { AppDispatch } from "./index.ts";
|
||||
|
||||
export const authSlice = createSlice({
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { ConversationInstance, Model } from "../conversation/types.ts";
|
||||
import { Message } from "../conversation/types.ts";
|
||||
import { insertStart } from "../utils.ts";
|
||||
import { ConversationInstance, Model } from "@/conversation/types.ts";
|
||||
import { Message } from "@/conversation/types.ts";
|
||||
import { insertStart } from "@/utils/base.ts";
|
||||
import { RootState } from "./index.ts";
|
||||
import { supportModels } from "../conf.ts";
|
||||
import { supportModels } from "@/conf.ts";
|
||||
|
||||
type initialStateType = {
|
||||
history: ConversationInstance[];
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { mobile } from "../utils.ts";
|
||||
import { mobile } from "@/utils/device.ts";
|
||||
|
||||
export const menuSlice = createSlice({
|
||||
name: "menu",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { getPackage } from "../conversation/addition.ts";
|
||||
import { getPackage } from "@/conversation/addition.ts";
|
||||
import { AppDispatch } from "./index.ts";
|
||||
|
||||
export const packageSlice = createSlice({
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
deleteSharing,
|
||||
listSharing,
|
||||
SharingPreviewForm,
|
||||
} from "../conversation/sharing.ts";
|
||||
} from "@/conversation/sharing.ts";
|
||||
|
||||
export const sharingSlice = createSlice({
|
||||
name: "sharing",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
import { getSubscription } from "../conversation/addition.ts";
|
||||
import { getSubscription } from "@/conversation/addition.ts";
|
||||
import { AppDispatch } from "./index.ts";
|
||||
|
||||
export const subscriptionSlice = createSlice({
|
||||
|
284
app/src/utils.ts
284
app/src/utils.ts
@ -1,284 +0,0 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { FileObject } from "./components/FileProvider.tsx";
|
||||
|
||||
export let event: BeforeInstallPromptEvent | undefined;
|
||||
export let mobile = isMobile();
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
mobile = isMobile();
|
||||
});
|
||||
|
||||
window.addEventListener("beforeinstallprompt", (e: Event) => {
|
||||
// e.preventDefault();
|
||||
event = e as BeforeInstallPromptEvent;
|
||||
});
|
||||
|
||||
export function triggerInstallApp() {
|
||||
if (!event) return;
|
||||
try {
|
||||
event.prompt();
|
||||
event.userChoice.then((choice: any) => {
|
||||
console.debug(`[service] installed app (status: ${choice.outcome})`);
|
||||
});
|
||||
} catch (err) {
|
||||
console.debug("[service] install app error", err);
|
||||
}
|
||||
|
||||
event = undefined;
|
||||
}
|
||||
|
||||
export function isMobile(): boolean {
|
||||
return (
|
||||
(document.documentElement.clientWidth || window.innerWidth) <= 668 ||
|
||||
(document.documentElement.clientHeight || window.innerHeight) <= 468 ||
|
||||
navigator.userAgent.includes("Mobile")
|
||||
);
|
||||
}
|
||||
|
||||
export function useEffectAsync<T>(effect: () => Promise<T>, deps?: any[]) {
|
||||
return useEffect(() => {
|
||||
effect().catch((err) =>
|
||||
console.debug("[runtime] error during use effect", err),
|
||||
);
|
||||
}, deps);
|
||||
}
|
||||
|
||||
export function useAnimation(
|
||||
ref: React.MutableRefObject<any>,
|
||||
cls: string,
|
||||
min?: number,
|
||||
): (() => number) | undefined {
|
||||
if (!ref.current) return;
|
||||
const target = ref.current as HTMLButtonElement;
|
||||
const stamp = Date.now();
|
||||
target.classList.add(cls);
|
||||
|
||||
return function () {
|
||||
const duration = Date.now() - stamp;
|
||||
const timeout = min ? Math.max(min - duration, 0) : 0;
|
||||
setTimeout(() => target.classList.remove(cls), timeout);
|
||||
return timeout;
|
||||
};
|
||||
}
|
||||
|
||||
export function useShared<T>(): {
|
||||
hook: (v: T) => void;
|
||||
useHook: () => Promise<T>;
|
||||
} {
|
||||
let value: T | undefined = undefined;
|
||||
return {
|
||||
hook: (v: T) => {
|
||||
value = v;
|
||||
},
|
||||
useHook: () => {
|
||||
return new Promise<T>((resolve) => {
|
||||
if (value) return resolve(value);
|
||||
const interval = setInterval(() => {
|
||||
if (value) {
|
||||
clearInterval(interval);
|
||||
resolve(value);
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function insert<T>(arr: T[], idx: number, value: T): T[] {
|
||||
return [...arr.slice(0, idx), value, ...arr.slice(idx)];
|
||||
}
|
||||
|
||||
export function insertStart<T>(arr: T[], value: T): T[] {
|
||||
return [value, ...arr];
|
||||
}
|
||||
|
||||
export function remove<T>(arr: T[], idx: number): T[] {
|
||||
return [...arr.slice(0, idx), ...arr.slice(idx + 1)];
|
||||
}
|
||||
|
||||
export function replace<T>(arr: T[], idx: number, value: T): T[] {
|
||||
return [...arr.slice(0, idx), value, ...arr.slice(idx + 1)];
|
||||
}
|
||||
|
||||
export function move<T>(arr: T[], from: number, to: number): T[] {
|
||||
const value = arr[from];
|
||||
return insert(remove(arr, from), to, value);
|
||||
}
|
||||
|
||||
export async function copyClipboard(text: string) {
|
||||
if (!navigator.clipboard) {
|
||||
const input = document.createElement("input");
|
||||
input.value = text;
|
||||
input.style.position = "absolute";
|
||||
input.style.left = "-9999px";
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(input);
|
||||
return;
|
||||
}
|
||||
await navigator.clipboard.writeText(text);
|
||||
}
|
||||
|
||||
export function getQueryParams() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const obj: Record<string, string> = {};
|
||||
for (const [key, value] of params.entries()) {
|
||||
obj[key] = value;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
export function getQueryParam(key: string): string {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return params.get(key) || "";
|
||||
}
|
||||
|
||||
export function saveAsFile(filename: string, content: string) {
|
||||
const a = document.createElement("a");
|
||||
a.href = URL.createObjectURL(new Blob([content]));
|
||||
a.download = filename;
|
||||
a.click();
|
||||
}
|
||||
|
||||
export function replaceInputValue(
|
||||
input: HTMLInputElement | undefined,
|
||||
value: string,
|
||||
) {
|
||||
return input && (input.value = value);
|
||||
}
|
||||
|
||||
export function useInputValue(id: string, value: string) {
|
||||
const input = document.getElementById(id) as HTMLInputElement | undefined;
|
||||
return input && replaceInputValue(input, value) && input.focus();
|
||||
}
|
||||
|
||||
export function testNumberInputEvent(e: any): boolean {
|
||||
if (
|
||||
/^[0-9]+$/.test(e.key) ||
|
||||
["Backspace", "Delete", "ArrowLeft", "ArrowRight", "Tab"].includes(e.key)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
export function formatMessage(file: FileObject, message: string): string {
|
||||
message = message.trim();
|
||||
if (file.name.length > 0 || file.content.length > 0) {
|
||||
return `
|
||||
\`\`\`file
|
||||
[[${file.name}]]
|
||||
${file.content}
|
||||
\`\`\`
|
||||
|
||||
${message}`;
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
export function filterMessage(message: string): string {
|
||||
return message.replace(/```file\n\[\[.*]]\n[\s\S]*?\n```\n\n/g, "");
|
||||
}
|
||||
|
||||
export function extractMessage(
|
||||
message: string,
|
||||
length: number = 50,
|
||||
flow: string = "...",
|
||||
) {
|
||||
return message.length > length ? message.slice(0, length) + flow : message;
|
||||
}
|
||||
|
||||
export function useDraggableInput(
|
||||
t: any,
|
||||
toast: any,
|
||||
target: HTMLLabelElement,
|
||||
handleChange: (filename?: string, content?: string) => void,
|
||||
) {
|
||||
target.addEventListener("dragover", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
target.addEventListener("drop", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const file = e.dataTransfer?.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const data = e.target?.result as string;
|
||||
if (!/^[\x00-\x7F]*$/.test(data)) {
|
||||
toast({
|
||||
title: t("file.parse-error"),
|
||||
description: t("file.parse-error-prompt"),
|
||||
});
|
||||
handleChange();
|
||||
} else {
|
||||
handleChange(file.name, e.target?.result as string);
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
} else {
|
||||
handleChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function escapeRegExp(str: string): string {
|
||||
// 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, "'");
|
||||
}
|
||||
|
||||
export function handleLine(
|
||||
data: string,
|
||||
max_line: number,
|
||||
end?: boolean,
|
||||
): string {
|
||||
const segment = data.split("\n");
|
||||
const line = segment.length;
|
||||
if (line > max_line) {
|
||||
return end ?? true
|
||||
? segment.slice(line - max_line).join("\n")
|
||||
: segment.slice(0, max_line).join("\n");
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export function handleGenerationData(data: string): string {
|
||||
data = data
|
||||
.replace(/{\s*"result":\s*{/g, "")
|
||||
.trim()
|
||||
.replace(/}\s*$/g, "");
|
||||
return handleLine(escapeRegExp(data), 6);
|
||||
}
|
||||
|
||||
export function getSelectionText(): string {
|
||||
if (window.getSelection) {
|
||||
return window.getSelection()?.toString() || "";
|
||||
} else if (document.getSelection && document.getSelection()?.toString()) {
|
||||
return document.getSelection()?.toString() || "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// browser compatibility issue
|
||||
export function getSelectionTextInArea(el: HTMLElement): string {
|
||||
const selection = window.getSelection();
|
||||
if (!selection) return "";
|
||||
const range = selection.getRangeAt(0);
|
||||
const preSelectionRange = range.cloneRange();
|
||||
preSelectionRange.selectNodeContents(el);
|
||||
preSelectionRange.setEnd(range.startContainer, range.startOffset);
|
||||
const start = preSelectionRange.toString().length;
|
||||
return el.innerText.slice(start, start + range.toString().length);
|
||||
}
|
29
app/src/utils/app.ts
Normal file
29
app/src/utils/app.ts
Normal file
@ -0,0 +1,29 @@
|
||||
export let event: BeforeInstallPromptEvent | undefined;
|
||||
|
||||
window.addEventListener("beforeinstallprompt", (e: Event) => {
|
||||
// e.preventDefault();
|
||||
console.debug(`[service] catch event from app install prompt`);
|
||||
event = e as BeforeInstallPromptEvent;
|
||||
});
|
||||
|
||||
export function triggerInstallApp() {
|
||||
/**
|
||||
* Trigger install app prompt
|
||||
* Warning: this is a browser experimental feature, it may not work on some browsers
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/BeforeInstallPromptEvent
|
||||
*
|
||||
* @example
|
||||
* triggerInstallApp();
|
||||
*/
|
||||
if (!event) return;
|
||||
try {
|
||||
event.prompt();
|
||||
event.userChoice.then((choice: any) => {
|
||||
console.debug(`[service] installed app (status: ${choice.outcome})`);
|
||||
});
|
||||
} catch (err) {
|
||||
console.debug("[service] install app error", err);
|
||||
}
|
||||
|
||||
event = undefined;
|
||||
}
|
20
app/src/utils/base.ts
Normal file
20
app/src/utils/base.ts
Normal file
@ -0,0 +1,20 @@
|
||||
export function insert<T>(arr: T[], idx: number, value: T): T[] {
|
||||
return [...arr.slice(0, idx), value, ...arr.slice(idx)];
|
||||
}
|
||||
|
||||
export function insertStart<T>(arr: T[], value: T): T[] {
|
||||
return [value, ...arr];
|
||||
}
|
||||
|
||||
export function remove<T>(arr: T[], idx: number): T[] {
|
||||
return [...arr.slice(0, idx), ...arr.slice(idx + 1)];
|
||||
}
|
||||
|
||||
export function replace<T>(arr: T[], idx: number, value: T): T[] {
|
||||
return [...arr.slice(0, idx), value, ...arr.slice(idx + 1)];
|
||||
}
|
||||
|
||||
export function move<T>(arr: T[], from: number, to: number): T[] {
|
||||
const value = arr[from];
|
||||
return insert(remove(arr, from), to, value);
|
||||
}
|
13
app/src/utils/device.ts
Normal file
13
app/src/utils/device.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export let mobile = isMobile();
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
mobile = isMobile();
|
||||
});
|
||||
|
||||
export function isMobile(): boolean {
|
||||
return (
|
||||
(document.documentElement.clientWidth || window.innerWidth) <= 668 ||
|
||||
(document.documentElement.clientHeight || window.innerHeight) <= 468 ||
|
||||
navigator.userAgent.includes("Mobile")
|
||||
);
|
||||
}
|
177
app/src/utils/dom.ts
Normal file
177
app/src/utils/dom.ts
Normal file
@ -0,0 +1,177 @@
|
||||
export async function copyClipboard(text: string) {
|
||||
/**
|
||||
* Copy text to clipboard
|
||||
* @param text Text to copy
|
||||
* @example
|
||||
* await copyClipboard("Hello world!");
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText
|
||||
*/
|
||||
|
||||
if (!navigator.clipboard) {
|
||||
const input = document.createElement("input");
|
||||
input.value = text;
|
||||
input.style.position = "absolute";
|
||||
input.style.left = "-9999px";
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(input);
|
||||
return;
|
||||
}
|
||||
|
||||
await navigator.clipboard.writeText(text);
|
||||
}
|
||||
|
||||
export function saveAsFile(filename: string, content: string) {
|
||||
/**
|
||||
* Save text as file
|
||||
* @param filename Filename
|
||||
* @param content File content
|
||||
* @example
|
||||
* saveAsFile("hello.txt", "Hello world!");
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Blob
|
||||
*/
|
||||
|
||||
const a = document.createElement("a");
|
||||
a.href = URL.createObjectURL(new Blob([content]));
|
||||
a.download = filename;
|
||||
a.click();
|
||||
}
|
||||
|
||||
export function getSelectionText(): string {
|
||||
/**
|
||||
* Get selected text
|
||||
* @example
|
||||
* const text = getSelectionText();
|
||||
* console.log(text);
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection
|
||||
*/
|
||||
|
||||
if (window.getSelection) {
|
||||
return window.getSelection()?.toString() || "";
|
||||
} else if (document.getSelection && document.getSelection()?.toString()) {
|
||||
return document.getSelection()?.toString() || "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
export function getSelectionTextInArea(el: HTMLElement): string {
|
||||
/**
|
||||
* Get selected text in element
|
||||
* @param el Element
|
||||
* @example
|
||||
* const text = getSelectionTextInArea(document.getElementById("textarea"));
|
||||
* console.log(text);
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection
|
||||
*/
|
||||
|
||||
const selection = window.getSelection();
|
||||
if (!selection) return "";
|
||||
const range = selection.getRangeAt(0);
|
||||
const preSelectionRange = range.cloneRange();
|
||||
preSelectionRange.selectNodeContents(el);
|
||||
preSelectionRange.setEnd(range.startContainer, range.startOffset);
|
||||
const start = preSelectionRange.toString().length;
|
||||
return el.innerText.slice(start, start + range.toString().length);
|
||||
}
|
||||
|
||||
export function useDraggableInput(
|
||||
t: any,
|
||||
toast: any,
|
||||
target: HTMLLabelElement,
|
||||
handleChange: (filename?: string, content?: string) => void,
|
||||
) {
|
||||
/**
|
||||
* Make input element draggable
|
||||
* @param t i18n function
|
||||
* @param toast Toast function
|
||||
* @param target Input element
|
||||
* @param handleChange Handle change function
|
||||
* @example
|
||||
* const input = document.getElementById("input") as HTMLLabelElement;
|
||||
* useDraggableInput(t, toast, input, handleChange);
|
||||
*/
|
||||
|
||||
target.addEventListener("dragover", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
target.addEventListener("drop", (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const file = e.dataTransfer?.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const data = e.target?.result as string;
|
||||
if (!/^[\x00-\x7F]*$/.test(data)) {
|
||||
toast({
|
||||
title: t("file.parse-error"),
|
||||
description: t("file.parse-error-prompt"),
|
||||
});
|
||||
handleChange();
|
||||
} else {
|
||||
handleChange(file.name, e.target?.result as string);
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
} else {
|
||||
handleChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function testNumberInputEvent(e: any): boolean {
|
||||
/**
|
||||
* Test if input event is valid for number input
|
||||
* @param e Input event
|
||||
* @example
|
||||
* const handler = (e: any) => {
|
||||
* if (testNumberInputEvent(e)) {
|
||||
* // do something
|
||||
* }
|
||||
* return;
|
||||
* }
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
|
||||
*/
|
||||
|
||||
if (
|
||||
/^[0-9]+$/.test(e.key) ||
|
||||
["Backspace", "Delete", "ArrowLeft", "ArrowRight", "Tab"].includes(e.key)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
export function replaceInputValue(
|
||||
input: HTMLInputElement | undefined,
|
||||
value: string,
|
||||
) {
|
||||
/**
|
||||
* Replace input value and focus
|
||||
* @param input Input element
|
||||
* @param value New value
|
||||
* @example
|
||||
* const input = document.getElementById("input") as HTMLInputElement;
|
||||
* replaceInputValue(input, "Hello world!");
|
||||
*/
|
||||
|
||||
return input && (input.value = value);
|
||||
}
|
||||
|
||||
export function useInputValue(id: string, value: string) {
|
||||
/**
|
||||
* Replace input value and focus
|
||||
* @param id Input element id
|
||||
* @param value New value
|
||||
* @example
|
||||
* const input = document.getElementById("input") as HTMLInputElement;
|
||||
* useInputValue("input", "Hello world!");
|
||||
*/
|
||||
|
||||
const input = document.getElementById(id) as HTMLInputElement | undefined;
|
||||
return input && replaceInputValue(input, value) && input.focus();
|
||||
}
|
79
app/src/utils/hook.ts
Normal file
79
app/src/utils/hook.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
export function useEffectAsync<T>(effect: () => Promise<T>, deps?: any[]) {
|
||||
/**
|
||||
* useEffect with async/await support
|
||||
*
|
||||
* @example
|
||||
* useEffectAsync(async () => {
|
||||
* const result = await fetch("https://api.example.com");
|
||||
* console.log(result);
|
||||
* }, []);
|
||||
*/
|
||||
|
||||
return useEffect(() => {
|
||||
effect().catch((err) =>
|
||||
console.debug("[runtime] error during use effect", err),
|
||||
);
|
||||
}, deps);
|
||||
}
|
||||
|
||||
export function useAnimation(
|
||||
ref: React.MutableRefObject<any>,
|
||||
cls: string,
|
||||
min?: number,
|
||||
): (() => number) | undefined {
|
||||
/**
|
||||
* Add animation class to react ref element and remove it after min ms when returned function is called
|
||||
*
|
||||
* @example
|
||||
* const animation = useAnimation(ref, "animate", 1000);
|
||||
* axios.get("https://api.example.com")
|
||||
* .finally(() => animation());
|
||||
*/
|
||||
if (!ref.current) return;
|
||||
const target = ref.current as HTMLButtonElement;
|
||||
const stamp = Date.now();
|
||||
target.classList.add(cls);
|
||||
|
||||
return function () {
|
||||
const duration = Date.now() - stamp;
|
||||
const timeout = min ? Math.max(min - duration, 0) : 0;
|
||||
setTimeout(() => target.classList.remove(cls), timeout);
|
||||
return timeout;
|
||||
};
|
||||
}
|
||||
|
||||
export function useShared<T>(): {
|
||||
hook: (v: T) => void;
|
||||
useHook: () => Promise<T>;
|
||||
} {
|
||||
/**
|
||||
* Share value between components, useful for sharing data between components / redux dispatches
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* const dispatch = useDispatch();
|
||||
* const { hook, useHook } = useShared<string>();
|
||||
*
|
||||
* dispatch(updateMigration({ hook }));
|
||||
* const response = await useHook();
|
||||
*/
|
||||
let value: T | undefined = undefined;
|
||||
return {
|
||||
hook: (v: T) => {
|
||||
value = v;
|
||||
},
|
||||
useHook: () => {
|
||||
return new Promise<T>((resolve) => {
|
||||
if (value) return resolve(value);
|
||||
const interval = setInterval(() => {
|
||||
if (value) {
|
||||
clearInterval(interval);
|
||||
resolve(value);
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
31
app/src/utils/path.ts
Normal file
31
app/src/utils/path.ts
Normal file
@ -0,0 +1,31 @@
|
||||
export function getQueryParams() {
|
||||
/**
|
||||
* Get query params from url
|
||||
*
|
||||
* @example
|
||||
* // https://example.com?foo=bar&baz=qux
|
||||
* getQueryParams();
|
||||
* // { foo: "bar", baz: "qux" }
|
||||
*/
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const obj: Record<string, string> = {};
|
||||
for (const [key, value] of params.entries()) {
|
||||
obj[key] = value;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
export function getQueryParam(key: string): string {
|
||||
/**
|
||||
* Get query param from url
|
||||
*
|
||||
* @example
|
||||
* // https://example.com?foo=bar&baz=qux
|
||||
* getQueryParam("foo");
|
||||
* // "bar"
|
||||
*/
|
||||
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return params.get(key) || "";
|
||||
}
|
63
app/src/utils/processor.ts
Normal file
63
app/src/utils/processor.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { FileObject } from "@/components/FileProvider.tsx";
|
||||
|
||||
export function formatMessage(file: FileObject, message: string): string {
|
||||
message = message.trim();
|
||||
if (file.name.length > 0 || file.content.length > 0) {
|
||||
return `
|
||||
\`\`\`file
|
||||
[[${file.name}]]
|
||||
${file.content}
|
||||
\`\`\`
|
||||
|
||||
${message}`;
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
export function filterMessage(message: string): string {
|
||||
return message.replace(/```file\n\[\[.*]]\n[\s\S]*?\n```\n\n/g, "");
|
||||
}
|
||||
|
||||
export function extractMessage(
|
||||
message: string,
|
||||
length: number = 50,
|
||||
flow: string = "...",
|
||||
) {
|
||||
return message.length > length ? message.slice(0, length) + flow : message;
|
||||
}
|
||||
|
||||
export function escapeRegExp(str: string): string {
|
||||
// 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, "'");
|
||||
}
|
||||
|
||||
export function handleLine(
|
||||
data: string,
|
||||
max_line: number,
|
||||
end?: boolean,
|
||||
): string {
|
||||
const segment = data.split("\n");
|
||||
const line = segment.length;
|
||||
if (line > max_line) {
|
||||
return end ?? true
|
||||
? segment.slice(line - max_line).join("\n")
|
||||
: segment.slice(0, max_line).join("\n");
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export function handleGenerationData(data: string): string {
|
||||
data = data
|
||||
.replace(/{\s*"result":\s*{/g, "")
|
||||
.trim()
|
||||
.replace(/}\s*$/g, "");
|
||||
return handleLine(escapeRegExp(data), 6);
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"types": [
|
||||
"vite-plugin-pwa/react",
|
||||
"vite-plugin-pwa/react"
|
||||
],
|
||||
|
||||
/* Bundler mode */
|
||||
@ -21,12 +21,15 @@
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"include": [
|
||||
"src",
|
||||
"*.ts",
|
||||
],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"references": [{ "path": "./tsconfig.node.json" }],
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user