chore: update model market tags and tailwind-merged

This commit is contained in:
Zhang Minghan 2024-01-15 20:34:59 +08:00
parent a340fd86c8
commit 7f1c8fad99
18 changed files with 92 additions and 49 deletions

View File

@ -39,9 +39,18 @@ export type ConversationInstance = {
export type ConversationMapper = Record<Id, Conversation>;
export type PlanItem = {
id: string;
name: string;
value: string;
icon: string;
models: string[];
};
export type Plan = {
level: number;
price: number;
items: PlanItem[];
};
export type SubscriptionUsage = Record<

View File

@ -302,7 +302,7 @@ input[type="number"] {
p {
margin: auto;
color: hsl(var(--text));
color: hsl(var(--text-dark));
transform: translateY(-1px);
}
}

View File

@ -1,5 +1,6 @@
import { deeptrainApiEndpoint, useDeeptrain } from "@/utils/env.ts";
import { ImgHTMLAttributes, useMemo } from "react";
import { cn } from "@/components/ui/lib/utils.ts";
export interface AvatarProps extends ImgHTMLAttributes<HTMLElement> {
username: string;
@ -11,7 +12,7 @@ function Avatar({ username, ...props }: AvatarProps) {
[username],
);
const color = useMemo(() => {
const background = useMemo(() => {
const colors = [
"bg-red-500",
"bg-yellow-500",
@ -28,8 +29,8 @@ function Avatar({ username, ...props }: AvatarProps) {
return useDeeptrain ? (
<img {...props} src={`${deeptrainApiEndpoint}/avatar/${username}`} alt="" />
) : (
<div {...props} className={`avatar ${color}`}>
<p>{code}</p>
<div {...props} className={cn("avatar", background)}>
<p className={`text-white`}>{code}</p>
</div>
);
}

View File

@ -16,6 +16,7 @@ import { Toggle } from "./ui/toggle.tsx";
import { mobile } from "@/utils/device.ts";
import { Button } from "./ui/button.tsx";
import { ChatAction } from "@/components/home/assemblies/ChatAction.tsx";
import { cn } from "@/components/ui/lib/utils.ts";
type RichEditorProps = {
value: string;
@ -104,9 +105,11 @@ function RichEditor({ value, onChange, maxLength }: RichEditorProps) {
</div>
<div className={`editor-wrapper`}>
<div
className={`editor-object ${openInput ? "show-editor" : ""} ${
openPreview ? "show-preview" : ""
}`}
className={cn(
"editor-object",
openInput && "show-editor",
openPreview && "show-preview",
)}
>
{openInput && (
<Textarea

View File

@ -27,6 +27,7 @@ import { useSelector } from "react-redux";
import { isHighContextModel } from "@/conf.ts";
import { selectModel } from "@/store/chat.ts";
import { ChatAction } from "@/components/home/assemblies/ChatAction.tsx";
import { cn } from "@/components/ui/lib/utils.ts";
const MaxFileSize = 1024 * 1024 * 25; // 25MB File Size Limit
const MaxPromptSize = 5000; // 5000 Prompt Size Limit (to avoid token overflow)
@ -64,7 +65,7 @@ function FileProvider({ value, onChange }: FileProviderProps) {
<DialogTrigger asChild>
<ChatAction
text={t("file.upload")}
className={value.length > 0 ? "active" : ""}
className={cn(value.length > 0 && "active")}
>
<Plus className={`h-4 w-4`} />
</ChatAction>

View File

@ -35,7 +35,7 @@ export type PopupFieldProps<T> = {
className?: string;
classNameInput?: string;
classNameLabel?: string;
}
};
export type PopupFieldTextProps = PopupFieldProps<string>;
export type PopupFieldNumberProps = PopupFieldProps<number>;

View File

@ -1,6 +1,7 @@
import { useTranslation } from "react-i18next";
import { useMemo } from "react";
import { isEmailValid } from "@/utils/form.ts";
import { cn } from "@/components/ui/lib/utils.ts";
function Required() {
return <span className={`text-red-500 mr-0.5`}>*</span>;
@ -27,9 +28,10 @@ export function LengthRangeRequired({
return (
<span
className={`ml-1 text-red-500 transition-opacity ${
onDisplay ? "" : "opacity-0"
}`}
className={cn(
"ml-1 text-red-500 transition-opacity",
!onDisplay && "opacity-0",
)}
>
({t("auth.length-range", { min, max })})
</span>
@ -55,9 +57,10 @@ export function SameRequired({
return (
<span
className={`ml-1 text-red-500 transition-opacity ${
onDisplay ? "" : "opacity-0"
}`}
className={cn(
"ml-1 text-red-500 transition-opacity",
!onDisplay && "opacity-0",
)}
>
({t("auth.same-rule")})
</span>
@ -78,9 +81,10 @@ export function EmailRequire({ content, hideOnEmpty }: EmailRequireProps) {
return (
<span
className={`ml-1 text-red-500 transition-opacity ${
onDisplay ? "" : "opacity-0"
}`}
className={cn(
"ml-1 text-red-500 transition-opacity",
!onDisplay && "opacity-0",
)}
>
({t("auth.invalid-email")})
</span>

View File

@ -16,6 +16,7 @@ import router from "@/router.tsx";
import { useLocation } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { mobile } from "@/utils/device.ts";
import { cn } from "@/components/ui/lib/utils.ts";
type MenuItemProps = {
title: string;
@ -39,7 +40,7 @@ function MenuItem({ title, icon, path }: MenuItemProps) {
};
return (
<div className={`menu-item ${active ? "active" : ""}`} onClick={redirect}>
<div className={cn("menu-item", active && "active")} onClick={redirect}>
<div className={`menu-item-icon`}>{icon}</div>
<div className={`menu-item-title`}>{title}</div>
</div>
@ -50,7 +51,7 @@ function MenuBar() {
const { t } = useTranslation();
const open = useSelector(selectMenu);
return (
<div className={`admin-menu ${open ? "open" : ""}`}>
<div className={cn("admin-menu", open && "open")}>
<MenuItem title={t("admin.dashboard")} icon={<Gauge />} path={"/"} />
<MenuItem title={t("admin.user")} icon={<Users />} path={"/users"} />
<MenuItem

View File

@ -42,6 +42,7 @@ import {
} from "@/admin/api/channel.ts";
import { toast } from "@/components/ui/use-toast.ts";
import { useEffectAsync } from "@/utils/hook.ts";
import { cn } from "@/components/ui/lib/utils.ts";
const initialState: Channel = {
id: -1,
@ -484,7 +485,7 @@ function ChannelEditor({ display, id, setEnabled }: ChannelEditorProps) {
onSelect={() =>
dispatch({ type: "add-group", value: item })
}
className={`px-2 ${idx > 1 ? "gold-text" : ""}`}
className={cn("px-2", idx > 1 && "gold-text")}
>
{item}
</CommandItem>

View File

@ -36,6 +36,7 @@ import ChatInput from "@/components/home/assemblies/ChatInput.tsx";
import ScrollAction from "@/components/home/assemblies/ScrollAction.tsx";
import { connectionEvent } from "@/events/connection.ts";
import { chatEvent } from "@/events/chat.ts";
import { cn } from "@/components/ui/lib/utils.ts";
type InterfaceProps = {
setWorking: (working: boolean) => void;
@ -166,7 +167,7 @@ function ChatWrapper() {
<div className={`input-wrapper`}>
<div className={`chat-box`}>
<ChatInput
className={align ? "align" : ""}
className={cn(align && "align")}
target={target}
value={input}
onValueChange={setInput}

View File

@ -14,6 +14,7 @@ import { useTranslation } from "react-i18next";
import { ConversationInstance } from "@/api/types.ts";
import { useState } from "react";
import { closeMarket } from "@/store/chat.ts";
import { cn } from "@/components/ui/lib/utils.ts";
type ConversationSegmentProps = {
conversation: ConversationInstance;
@ -35,7 +36,7 @@ function ConversationSegment({
return (
<div
className={`conversation ${current === conversation.id ? "active" : ""}`}
className={cn("conversation", current === conversation.id && "active")}
onClick={async (e) => {
const target = e.target as HTMLElement;
if (

View File

@ -39,12 +39,23 @@ import {
DropResult,
} from "react-beautiful-dnd";
import { savePreferenceModels } from "@/utils/storage.ts";
import { cn } from "@/components/ui/lib/utils.ts";
type SearchBarProps = {
value: string;
onChange: (value: string) => void;
};
function getTags(model: Model): string[] {
let raw = model.tag || [];
if (model.free && !raw.includes("free")) raw = ["free", ...raw];
if (model.high_context && !raw.includes("high-context"))
raw = ["high-context", ...raw];
return raw;
}
function SearchBar({ value, onChange }: SearchBarProps) {
const { t } = useTranslation();
@ -59,7 +70,7 @@ function SearchBar({ value, onChange }: SearchBarProps) {
/>
<X
size={16}
className={`clear-icon ${value.length > 0 ? "active" : ""}`}
className={cn("clear-icon", value.length > 0 && "active")}
onClick={() => onChange("")}
/>
</div>
@ -107,6 +118,8 @@ function ModelItem({
return isUrl(model.avatar) ? model.avatar : `/icons/${model.avatar}`;
}, [model]);
const tags = useMemo((): string[] => getTags(model), [model]);
return (
<div
className={`model-item ${className}`}
@ -135,19 +148,18 @@ function ModelItem({
<GripVertical className={`grip-icon h-4 w-4 translate-x-[-1rem]`} />
<img className={`model-avatar`} src={avatar} alt={model.name} />
<div className={`model-info`}>
<p className={`model-name ${pro ? "pro" : ""}`}>{model.name}</p>
<p className={cn("model-name", pro && "pro")}>{model.name}</p>
{model.description && (
<p className={`model-description`}>{model.description}</p>
)}
<div className={`model-tag`}>
{model.tag &&
model.tag.map((tag, index) => {
return (
<span className={`tag-item`} key={index}>
{t(`tag.${tag}`)}
</span>
);
})}
{tags.map((tag, index) => {
return (
<span className={`tag-item`} key={index}>
{t(`tag.${tag}`)}
</span>
);
})}
</div>
</div>
<div className={`grow`} />
@ -192,8 +204,10 @@ function MarketPlace({ search }: MarketPlaceProps) {
const raw = splitList(search.toLowerCase(), [" ", ",", ";", "-"]);
return supportModels.filter((model) => {
const name = model.name.toLowerCase();
const tag = (model.tag || []).join(" ").toLowerCase();
const tag_translated = (model.tag || [])
const tag = getTags(model);
const tag_name = tag.join(" ").toLowerCase();
const tag_translated_name = tag
.map((item) => t(`tag.${item}`))
.join(" ")
.toLowerCase();
@ -202,8 +216,8 @@ function MarketPlace({ search }: MarketPlaceProps) {
return raw.every(
(item) =>
name.includes(item) ||
tag.includes(item) ||
tag_translated.includes(item) ||
tag_name.includes(item) ||
tag_translated_name.includes(item) ||
id.includes(item),
);
});
@ -251,7 +265,7 @@ function MarketPlace({ search }: MarketPlaceProps) {
{(provided) => (
<ModelItem
model={model}
className={`${select === model.id ? "active" : ""}`}
className={cn(select === model.id && "active")}
forwardRef={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}

View File

@ -43,6 +43,7 @@ import MenuBar from "@/components/app/MenuBar.tsx";
import { Separator } from "@/components/ui/separator.tsx";
import { goAuth } from "@/utils/app.ts";
import Avatar from "@/components/Avatar.tsx";
import { cn } from "@/components/ui/lib/utils.ts";
type Operation = {
target: ConversationInstance | null;
@ -347,7 +348,7 @@ function SideBar() {
}, []);
return (
<div className={`sidebar ${open ? "open" : ""}`}>
<div className={cn("sidebar", open && "open")}>
{auth ? (
<div className={`sidebar-content`}>
<SidebarAction setOperateConversation={setOperateConversation} />

View File

@ -4,6 +4,7 @@ import { useDispatch, useSelector } from "react-redux";
import { useTranslation } from "react-i18next";
import React, { useEffect, useRef } from "react";
import { openDialog } from "@/store/settings.ts";
import { cn } from "@/components/ui/lib/utils.ts";
type ChatActionProps = {
className?: string;
@ -53,11 +54,11 @@ export function WebAction({ visible }: WebActionProps) {
return (
visible && (
<ChatAction
className={web ? "active" : ""}
className={cn(web && "active")}
text={t("chat.web")}
onClick={() => dispatch(toggleWeb())}
>
<Globe className={`h-4 w-4 web ${web ? "enable" : ""}`} />
<Globe className={cn("h-4 w-4 web", web && "enable")} />
</ChatAction>
)
);

View File

@ -41,6 +41,7 @@ import { selectAuthenticated } from "@/store/auth.ts";
import { ToastAction } from "@/components/ui/toast.tsx";
import { deeptrainEndpoint, docsEndpoint, useDeeptrain } from "@/utils/env.ts";
import { useRedeem } from "@/api/redeem.ts";
import { cn } from "@/components/ui/lib/utils.ts";
type AmountComponentProps = {
amount: number;
@ -57,7 +58,7 @@ function AmountComponent({
const { t } = useTranslation();
return (
<div className={`amount ${active ? "active" : ""}`} onClick={onClick}>
<div className={cn("amount", active && "active")} onClick={onClick}>
{!other ? (
<>
<div className={`amount-title`}>

View File

@ -20,6 +20,7 @@ import { Label } from "@/components/ui/label.tsx";
import { rest_api, tokenField, ws_api } from "@/conf.ts";
import { getMemory } from "@/utils/memory.ts";
import { Progress } from "@/components/ui/progress.tsx";
import { cn } from "@/components/ui/lib/utils.ts";
type ProgressProps = {
current: number;
@ -157,7 +158,7 @@ function ArticleContent() {
onPressedChange={(state: boolean) => dispatch(setWeb(state))}
variant={`outline`}
>
<Globe className={`h-4 w-4 web ${web ? "enable" : ""}`} />
<Globe className={cn("h-4 w-4 web", web && "enable")} />
</Toggle>
<Label className={`ml-2.5 whitespace-nowrap`}>
{t("article.web-checkbox")}

View File

@ -31,6 +31,7 @@ import { Button } from "@/components/ui/button.tsx";
import { updateMarket } from "@/admin/api/market.ts";
import { Combobox } from "@/components/ui/combo-box.tsx";
import { channelModels } from "@/admin/channel.ts";
import { cn } from "@/components/ui/lib/utils.ts";
type Model = RawModel & {
seed?: string;
@ -275,7 +276,7 @@ function MarketImage({ image, idx, dispatch }: MarketImageProps) {
variant={`outline`}
size={`sm`}
pressed={source === image}
className={`market-image ${source === image ? "active" : ""}`}
className={cn("market-image", source === image ? "active" : "")}
onPressedChange={(state) => {
if (!state) return;
if (customized) {
@ -374,7 +375,7 @@ function Market() {
);
}, [form]);
const doCheck = (index: number) => {
const checked = (index: number) => {
return useMemo((): boolean => {
const model = form[index];
@ -429,9 +430,10 @@ function Market() {
>
{(provided) => (
<div
className={`market-item ${
doCheck(index) ? "" : "error"
}`}
className={cn(
"market-item",
!checked(index) && "error",
)}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}

View File

@ -10,12 +10,13 @@ import UserTable from "@/components/admin/UserTable.tsx";
import { mobile } from "@/utils/device.ts";
import Tips from "@/components/Tips.tsx";
import RedeemTable from "@/components/admin/RedeemTable.tsx";
import { cn } from "@/components/ui/lib/utils.ts";
function Users() {
const { t } = useTranslation();
return (
<div className={`user-interface ${mobile ? "mobile" : ""}`}>
<div className={cn("user-interface", mobile && "mobile")}>
<Card className={`admin-card`}>
<CardHeader className={`select-none`}>
<CardTitle>{t("admin.user")}</CardTitle>