feat: optiminize

This commit is contained in:
butterfly 2024-04-26 01:31:03 +08:00
parent 59583e53bd
commit 48e8c0a194
30 changed files with 304 additions and 263 deletions

View File

@ -76,7 +76,7 @@ export default function ActionsBar(props: ActionsBarProps) {
return (
<div
key={action.id}
className={` shrink-1 grow-0 basis-[${
className={` cursor-pointer shrink-1 grow-0 basis-[${
(100 - 1) / arr.length
}%] flex flex-col items-center justify-center gap-0.5
${
@ -98,7 +98,7 @@ export default function ActionsBar(props: ActionsBarProps) {
return (
<div
key={action.id}
className={`p-3 ${
className={`cursor-pointer p-3 ${
selected === action.id
? "bg-actions-bar-btn-default"
: "bg-transparent"

View File

@ -4,22 +4,22 @@ export interface CardProps {
className?: string;
children?: ReactNode;
title?: ReactNode;
inMobile?: boolean;
}
export default function Card(props: CardProps) {
const { className, children, title, inMobile } = props;
let titleClassName = "ml-4 mb-3";
if (inMobile) {
titleClassName = "ml-3 mb-3";
}
const { className, children, title } = props;
return (
<>
{title && (
<div
className={`capitalize font-black font-setting-card-title text-sm-mobile font-weight-setting-card-title ${titleClassName}`}
className={`
capitalize font-black font-setting-card-title text-sm-mobile font-weight-setting-card-title
mb-3
ml-3
md:ml-4
`}
>
{title}
</div>

View File

@ -4,33 +4,23 @@ import { RequestMessage } from "@/app/client/api";
interface ImgsProps {
message: RequestMessage;
isMobileScreen?: boolean;
}
export default function Imgs(props: ImgsProps) {
const { message, isMobileScreen } = props;
const { message } = props;
const imgSrcs = getMessageImages(message);
if (imgSrcs.length < 1) {
return <></>;
}
let imgVars = {
const imgVars = {
"--imgs-width": `calc(var(--max-message-width) - ${
imgSrcs.length - 1
}*0.25rem)`,
"--img-width": `calc(var(--imgs-width)/ ${imgSrcs.length})`,
};
if (isMobileScreen) {
imgVars = {
"--imgs-width": `calc(var(--max-message-width) - ${
imgSrcs.length - 1
}*0.25rem)`,
"--img-width": `calc(var(--imgs-width)/ ${imgSrcs.length})`,
};
}
return (
<div
className={`w-[100%] mt-[0.625rem] flex gap-1`}

View File

@ -73,7 +73,7 @@ export default function Input(props: CommonInputProps & InputProps) {
}}
/>
{type == "password" && (
<div onClick={() => setShow((pre) => !pre)}>
<div className=" cursor-pointer" onClick={() => setShow((pre) => !pre)}>
{show ? <PasswordVisible /> : <PasswordInvisible />}
</div>
)}

View File

@ -52,20 +52,13 @@ export function ListItem(props: ListItemProps) {
const context = useContext(ListContext);
const [childrenMeta, setMeta] = useState<ChildrenMeta>({});
const [childrenType, setMeta] = useState<ChildrenMeta["type"]>("unknown");
const { isMobileScreen, inputNextLine, rangeNextLine } = context;
let containerClassName = "py-3";
let titleClassName = "";
if (isMobileScreen) {
containerClassName = "py-2";
titleClassName = "";
}
const { inputNextLine, rangeNextLine } = context;
let internalNextLine;
switch (childrenMeta.type) {
switch (childrenType) {
case "input":
internalNextLine = !!(nextline || inputNextLine);
break;
@ -76,20 +69,18 @@ export function ListItem(props: ListItemProps) {
internalNextLine = false;
}
const update = useCallback((m: ChildrenMeta) => {
setMeta(m);
const updateType = useCallback((m: ChildrenMeta) => {
setMeta(m.type);
}, []);
return (
<div
className={`relative after:h-[0.5px] after:bottom-0 after:w-[100%] after:left-0 after:absolute last:after:hidden after:bg-list-item-divider ${
internalNextLine ? "" : "flex gap-3"
} justify-between items-center px-0 ${containerClassName} ${className}`}
} justify-between items-center px-0 py-2 md:py-3 ${className}`}
onClick={onClick}
>
<div
className={`flex-1 flex flex-col justify-start gap-1 ${titleClassName}`}
>
<div className={`flex-1 flex flex-col justify-start gap-1`}>
<div className=" font-common text-sm-mobile font-weight-[500] line-clamp-1">
{title}
</div>
@ -97,7 +88,7 @@ export function ListItem(props: ListItemProps) {
<div className={` text-sm text-text-list-subtitle`}>{subTitle}</div>
)}
</div>
<ListContext.Provider value={{ ...context, update }}>
<ListContext.Provider value={{ ...context, update: updateType }}>
<div
className={`${
internalNextLine ? "mt-[0.625rem]" : "max-w-[70%]"

View File

@ -1,4 +1,3 @@
import useMobileScreen from "@/app/hooks/useMobileScreen";
import BotIcon from "@/app/icons/bot.svg";
import LoadingIcon from "@/app/icons/three-dots.svg";
@ -16,15 +15,17 @@ export default function Loading({
theme = getCSSVar("--default-container-bg");
}
const isMobileScreen = useMobileScreen();
return (
<div
className={`flex flex-col justify-center items-center w-[100%] ${
isMobileScreen
? "h-[100%]"
: `my-2.5 ml-1 mr-2.5 rounded-md h-[calc(100%-1.25rem)]`
}`}
className={`
flex flex-col justify-center items-center w-[100%]
h-[100%]
md:my-2.5
md:ml-1
md:mr-2.5
md:rounded-md
md:h-[calc(100%-1.25rem)]
`}
style={{ background: useSkeleton ? theme : "" }}
>
{!noLogo && <BotIcon />}

View File

@ -59,23 +59,20 @@ export default function MenuLayout<
),
});
let containerClassName = "flex h-[100%] w-[100%]";
let listClassName =
"relative basis-sidebar h-[calc(100%-1.25rem)] pb-6 max-md:px-4 max-md:pb-4 rounded-md my-2.5 bg-menu";
let panelClassName = "flex-1 h-[100%] w-page";
if (isMobileScreen) {
containerClassName = "h-[100%] w-[100%] relative bg-center";
listClassName = `h-[100%] w-[100%] flex-1 px-4`;
panelClassName = `transition-all duration-300 absolute top-0 max-h-[100vh] w-[100%] ${
showPanel ? "left-0" : "left-[101%]"
} z-10`;
}
return (
<div className={`${containerClassName}`}>
<div
className={`flex flex-col px-6 ${listClassName}`}
className={`
h-[100%] w-[100%] relative bg-center
md:flex
`}
>
<div
className={`
flex flex-col px-6
h-[100%]
max-md:w-[100%] max-md:px-4 max-md:pb-4 max-md:flex-1
md:relative md:basis-sidebar md:h-[calc(100%-1.25rem)] md:pb-6 md:rounded-md md:my-2.5 md:bg-menu
`}
onClick={(e) => {
if (e.target === e.currentTarget) {
navigate(Path.Home);
@ -101,7 +98,14 @@ export default function MenuLayout<
</div>
)}
</div>
<div className={`${panelClassName}`}>
<div
className={`
md:flex-1 md:h-[100%] md:w-page
max-md:transition-all max-md:duration-300 max-md:absolute max-md:top-0 max-md:max-h-[100vh] max-md:w-[100%] ${
showPanel ? "max-md:left-0" : "max-md:left-[101%]"
} max-md:z-10
`}
>
<PanelComponent
{...props}
setShowPanel={setShowPanel}

View File

@ -184,7 +184,7 @@ export default function Popover(props: {
)}
{createPortal(
<div
className={`${popoverCommonClass} ${popoverClassName}`}
className={`${popoverCommonClass} ${popoverClassName} cursor-pointer`}
style={{ zIndex: baseZIndex + 1, ...placementStyle }}
>
{content}

View File

@ -25,20 +25,13 @@ export default function Screen(props: ScreenProps) {
useListenWinResize();
let containerClassName = "flex h-[100%] w-[100%] bg-center overflow-hidden";
let sidebarClassName = "flex-0 overflow-hidden";
let pageClassName = "flex-1 h-[100%] min-w-0 overflow-hidden";
if (isMobileScreen) {
containerClassName =
"relative flex flex-col-reverse h-[100%] w-[100%] bg-center";
sidebarClassName = "absolute w-[100%] bottom-0 z-10";
pageClassName = "w-[100%] h-[100%]";
}
return (
<div
className={`bg-global ${containerClassName}`}
className={`
bg-global flex h-[100%] w-[100%] bg-center
max-md:relative max-md:flex-col-reverse
md:overflow-hidden
`}
style={{
direction: getLang() === "ar" ? "rtl" : "ltr",
}}
@ -47,16 +40,26 @@ export default function Screen(props: ScreenProps) {
props.noAuth
) : (
<>
<div className={sidebarClassName} id={SIDEBAR_ID}>
<div
className={`
max-md:absolute max-md:w-[100%] max-md:bottom-0 max-md:z-10
md:flex-0 md:overflow-hidden
`}
id={SIDEBAR_ID}
>
{props.sidebar}
</div>
<div
className={pageClassName}
className={`
h-[100%]
max-md:w-[100%]
md:flex-1 md:min-w-0 md:overflow-hidden
`}
id={SlotID.AppBody}
style={{
// #3016 disable transition on ios mobile screen
transition: isMobileScreen && isIOSMobile ? "none" : undefined,
transition: isIOSMobile ? "none" : undefined,
}}
>
{props.children}

View File

@ -52,13 +52,13 @@ const Select = <Value extends number | string>(props: SearchProps<Value>) => {
const content = (
<div
className={`px-2 py-2 flex flex-col gap-1 overflow-y-auto overflow-x-hidden`}
className={` flex flex-col gap-1 overflow-y-auto overflow-x-hidden`}
style={{ maxHeight }}
>
{options?.map((o) => (
<div
key={o.value}
className={`flex items-center p-3 gap-2 rounded-md hover:bg-select-option-hovered`}
className={`flex items-center p-3 gap-2 rounded-action-btn hover:bg-select-option-hovered`}
onClick={() => {
onSelect?.(o.value);
}}
@ -79,13 +79,13 @@ const Select = <Value extends number | string>(props: SearchProps<Value>) => {
placement={
position?.poi.relativePosition[1] !== Orientation.bottom ? "rb" : "rt"
}
popoverClassName="border border-select-popover rounded-md shadow-select-popover-shadow w-actions-popover bg-select-popover-panel dark:bg-select-popover-panel-dark"
popoverClassName="border border-select-popover rounded-lg shadow-select-popover-shadow w-actions-popover bg-select-popover-panel dark:bg-select-popover-panel-dark"
onShow={(e) => {
getRelativePosition(contentRef.current!, "");
}}
>
<div
className={`flex items-center gap-3 py-2 px-3 bg-select rounded-action-btn font-time text-sm-title ${selectClassName}`}
className={`flex items-center gap-3 py-2 px-3 bg-select rounded-action-btn font-time text-sm-title ${selectClassName} cursor-pointer`}
ref={contentRef}
>
<div className={`flex items-center gap-2 flex-1`}>

View File

@ -1,5 +1,6 @@
import { useContext, useEffect, useRef } from "react";
import { ListContext } from "../List";
import { ListContext } from "@/app/components/List";
import useResizeObserver from "use-resize-observer";
interface SlideRangeProps {
className?: string;
@ -30,11 +31,15 @@ export default function SlideRange(props: SlideRangeProps) {
const slideRef = useRef<HTMLDivElement>(null);
const { ref, width = 1 } = useResizeObserver<HTMLDivElement>();
ref(slideRef.current);
const transformToWidth = (x: number = start) => {
const abs = x - start;
const maxWidth = (slideRef.current?.clientWidth || 1) - margin * 2;
const radio = stroke / maxWidth;
return abs / radio;
const maxWidth = width - margin * 2;
const result = (abs / stroke) * maxWidth;
return result;
};
const setProperty = (value?: number) => {
@ -44,37 +49,36 @@ export default function SlideRange(props: SlideRangeProps) {
`${initWidth + margin}px`,
);
};
useEffect(() => {
setProperty(value);
update?.({ type: "range" });
}, []);
}, [width]);
return (
<div
className={`flex flex-col justify-center items-end gap-1 w-[100%] ${className} ${rangeClassName}`}
>
{!!description && (
<div className="text-text-slider-block text-common text-sm">
<div className="text-text-slider-block text-common text-sm ">
{description}
</div>
)}
<div
className="flex my-1.5 relative w-[100%] h-1.5 bg-slider rounded-slide"
className="flex my-1.5 relative w-[100%] h-1.5 bg-slider rounded-slide cursor-pointer"
ref={slideRef}
>
<div className="absolute top-0 h-[100%] w-[var(--slide-value-size)] pointer-events-none bg-slider-slided-travel rounded-slide">
<div className="cursor-pointer absolute marker:top-0 h-[100%] w-[var(--slide-value-size)] bg-slider-slided-travel rounded-slide">
&nbsp;
</div>
<div
className=" absolute w-[30px] top-[50%] translate-y-[-50%] left-[var(--slide-value-size)] translate-x-[-50%] pointer-events-none h-slide-btn leading-slide-btn text-sm-mobile text-center rounded-slide border border-slider-block bg-slider-block"
className="cursor-pointer absolute z-1 w-[30px] top-[50%] translate-y-[-50%] left-[var(--slide-value-size)] translate-x-[-50%] h-slide-btn leading-slide-btn text-sm-mobile text-center rounded-slide border border-slider-block bg-slider-block"
// onPointerDown={onPointerDown}
>
{value}
</div>
<input
type="range"
className="w-[100%] h-[100%] opacity-0"
className="w-[100%] h-[100%] opacity-0 cursor-pointer"
value={value}
min={start}
max={start + stroke}

View File

@ -15,7 +15,7 @@ export default function Switch(props: SwitchProps) {
<RadixSwitch.Root
checked={value}
onCheckedChange={onChange}
className={` flex w-switch h-switch p-0.5 box-content rounded-md ${switchClassName} ${
className={` cursor-pointer flex w-switch h-switch p-0.5 box-content rounded-md ${switchClassName} ${
value
? "bg-switch-checked justify-end"
: "bg-switch-unchecked justify-start"

View File

@ -115,23 +115,23 @@ export function ChatActions(props: {
icon: props.uploading ? <LoadingButtonIcon /> : <ImageIcon />,
placement: "left",
},
{
onClick: nextTheme,
text: Locale.Chat.InputActions.Theme[theme],
isShow: true,
icon: (
<>
{theme === Theme.Auto ? (
<AutoIcon />
) : theme === Theme.Light ? (
<LightIcon />
) : theme === Theme.Dark ? (
<DarkIcon />
) : null}
</>
),
placement: "left",
},
// {
// onClick: nextTheme,
// text: Locale.Chat.InputActions.Theme[theme],
// isShow: true,
// icon: (
// <>
// {theme === Theme.Auto ? (
// <AutoIcon />
// ) : theme === Theme.Light ? (
// <LightIcon />
// ) : theme === Theme.Dark ? (
// <DarkIcon />
// ) : null}
// </>
// ),
// placement: "left",
// },
{
onClick: props.showPromptHints,
text: Locale.Chat.InputActions.Prompt,
@ -201,6 +201,7 @@ export function ChatActions(props: {
placement="rt"
noArrow
popoverClassName="border border-chat-actions-popover-mobile rounded-md shadow-chat-actions-popover-mobile w-actions-popover bg-chat-actions-popover-panel-mobile "
className=" cursor-pointer"
>
<AddCircleIcon />
</Popover>
@ -222,7 +223,7 @@ export function ChatActions(props: {
placement={ind ? "t" : "lt"}
>
<div
className="h-[32px] w-[32px] flex items-center justify-center hover:bg-chat-actions-btn-hovered hover:rounded-action-btn"
className=" cursor-pointer h-[32px] w-[32px] flex items-center justify-center hover:bg-chat-actions-btn-hovered hover:rounded-action-btn"
onClick={act.onClick}
>
{act.icon}
@ -242,7 +243,7 @@ export function ChatActions(props: {
placement={ind === arr.length - 1 ? "rt" : "t"}
>
<div
className="h-[32px] w-[32px] flex items-center justify-center hover:bg-chat-actions-btn-hovered hover:rounded-action-btn"
className=" cursor-pointer h-[32px] w-[32px] flex items-center justify-center hover:bg-chat-actions-btn-hovered hover:rounded-action-btn"
onClick={act.onClick}
>
{act.icon}

View File

@ -30,21 +30,13 @@ export default function ChatHeader(props: ChatHeaderProps) {
const currentModel = chatStore.currentSession().mask.modelConfig.model;
let containerClassName = "";
let titleClassName = "mr-4";
let mainTitleClassName = "";
let subTitleClassName = "";
if (isMobileScreen) {
containerClassName = "h-menu-title-mobile";
titleClassName = "flex flex-col items-center justify-center gap-0.5 text";
mainTitleClassName = "text-sm-title h-[19px] leading-5";
subTitleClassName = "text-sm-mobile-tab leading-4";
}
return (
<div
className={`absolute w-[100%] backdrop-blur-[30px] z-20 flex flex-0 justify-between items-center px-6 py-4 gap-chat-header-gap sm:border-b sm:border-chat-header-bottom ${containerClassName}`}
className={`
absolute w-[100%] backdrop-blur-[30px] z-20 flex flex-0 justify-between items-center px-6 py-4 gap-chat-header-gap
sm:border-b sm:border-chat-header-bottom
max-md:h-menu-title-mobile
`}
data-tauri-drag-region
>
<div
@ -54,26 +46,38 @@ export default function ChatHeader(props: ChatHeaderProps) {
</div>
{isMobileScreen ? (
<div onClick={() => navigate(Path.Home)}>
<div className=" cursor-pointer" onClick={() => navigate(Path.Home)}>
<GobackIcon />
</div>
) : (
<LogIcon />
)}
<div className={`flex-1 ${titleClassName}`}>
<div
className={`line-clamp-1 cursor-pointer text-text-chat-header-title text-chat-header-title font-common ${mainTitleClassName}`}
className={`
flex-1
max-md:flex max-md:flex-col max-md:items-center max-md:justify-center max-md:gap-0.5 max-md:text
md:mr-4
`}
>
<div
className={`
line-clamp-1 cursor-pointer text-text-chat-header-title text-chat-header-title font-common
max-md:text-sm-title max-md:h-chat-header-title-mobile max-md:leading-5
`}
onClickCapture={() => setIsEditingMessage(true)}
>
{!session.topic ? DEFAULT_TOPIC : session.topic}
</div>
<div
className={`text-text-chat-header-subtitle text-sm ${subTitleClassName}`}
className={`
text-text-chat-header-subtitle text-sm
max-md:text-sm-mobile-tab max-md:leading-4
`}
>
{isMobileScreen ? (
<div
className="flex items-center gap-1"
className="flex items-center gap-1 cursor-pointer"
onClick={() => showModelSelector(true)}
>
{currentModel}
@ -86,6 +90,7 @@ export default function ChatHeader(props: ChatHeaderProps) {
</div>
<div
className=" cursor-pointer"
onClick={() => {
setShowExport(true);
}}

View File

@ -203,29 +203,28 @@ export default forwardRef<ChatInputPanelInstance, ChatInputPanelProps>(
emitImages: setAttachImages,
setUploading,
});
let containerClassName = "border-t border-chat-input-top";
let inputClassName = " flex flex-col px-5 pb-5";
let actionsClassName = "py-2.5";
let labelClassName = "rounded-md p-4 gap-4";
let textarea = "min-h-chat-input";
if (isMobileScreen) {
containerClassName = "rounded-tl-md rounded-tr-md";
inputClassName = "flex flex-row-reverse items-center gap-2 p-3";
actionsClassName = "";
labelClassName = " rounded-chat-input p-3 gap-3 flex-1";
textarea = "h-chat-input-mobile";
}
return (
<div className={`relative w-[100%] box-border ${containerClassName}`}>
<div
className={`
relative w-[100%] box-border
max-md:rounded-tl-md max-md:rounded-tr-md
md:border-t md:border-chat-input-top
`}
>
<PromptHints
prompts={promptHints}
onPromptSelect={onPromptSelect}
className=" border-chat-input-top"
/>
<div className={`${inputClassName}`}>
<div
className={`
flex
max-md:flex-row-reverse max-md:items-center max-md:gap-2 max-md:p-3
md:flex-col md:px-5 md:pb-5
`}
>
<ChatActions
showModelSelector={showModelSelector}
uploadImage={uploadImage}
@ -246,11 +245,18 @@ export default forwardRef<ChatInputPanelInstance, ChatInputPanelProps>(
setUserInput("/");
onSearch("");
}}
className={actionsClassName}
className={`
md:py-2.5
`}
isMobileScreen={isMobileScreen}
/>
<label
className={`cursor-text flex flex-col bg-chat-panel-input-hood border border-chat-input-hood sm:focus-within:border-chat-input-hood-focus sm:focus-within:shadow-chat-input-hood-focus-shadow ${labelClassName}`}
className={`
cursor-text flex flex-col bg-chat-panel-input-hood border border-chat-input-hood
sm:focus-within:border-chat-input-hood-focus sm:focus-within:shadow-chat-input-hood-focus-shadow
rounded-chat-input p-3 gap-3 max-md:flex-1
md:rounded-md md:p-4 md:gap-4
`}
htmlFor="chat-input"
>
{attachImages.length != 0 && (
@ -273,7 +279,11 @@ export default forwardRef<ChatInputPanelInstance, ChatInputPanelProps>(
<textarea
id="chat-input"
ref={inputRef}
className={`leading-[19px] flex-1 focus:outline-none focus:shadow-none focus:border-none ${textarea} resize-none`}
className={`
leading-[19px] flex-1 focus:outline-none focus:shadow-none focus:border-none resize-none
max-md:h-chat-input-mobile
md:min-h-chat-input
`}
placeholder={
isMobileScreen
? Locale.Chat.Input(submitKey, isMobileScreen)

View File

@ -228,7 +228,7 @@ export default function ChatMessagePanel(props: ChatMessagePanelProps) {
: "text-text-chat-message-markdown-bot"
}`}
/>
<Imgs message={message} isMobileScreen={isMobileScreen} />
<Imgs message={message} />
</div>
<MessageActions
className={actionsBarPosition}

View File

@ -273,13 +273,11 @@ function _Chat() {
return (
<div
className={`relative flex flex-col ${
isMobileScreen
? "absolute h-[100vh] w-[100%]"
: "h-[calc(100%-1.25rem)]"
} overflow-hidden ${
isMobileScreen ? "" : `my-2.5 ml-1 mr-2.5 rounded-md`
} bg-chat-panel`}
className={`
relative flex flex-col overflow-hidden bg-chat-panel
max-md:absolute max-md:h-[100vh] max-md:w-[100%]
md:h-[calc(100%-1.25rem)] md:my-2.5 md:ml-1 md:mr-2.5 md:rounded-md
`}
key={session.id}
>
<ChatHeader

View File

@ -8,7 +8,7 @@ export default function ClearContextDivider() {
return (
<div
className={`mt-6 mb-8 flex items-center justify-center gap-2.5`}
className={`mt-6 mb-8 flex items-center justify-center gap-2.5 max-md:cursor-pointer`}
onClick={() => {
if (!isMobileScreen) {
return;
@ -24,9 +24,10 @@ export default function ClearContextDivider() {
{Locale.Context.Clear}
</div>
<div
className={`text-text-chat-panel-message-clear-revert underline font-common ${
!isMobileScreen ? " cursor-pointer" : ""
}`}
className={`
text-text-chat-panel-message-clear-revert underline font-common
md:cursor-pointer
`}
onClick={() => {
if (isMobileScreen) {
return;

View File

@ -263,7 +263,7 @@ export default function MessageActions(props: MessageActionsProps) {
className={`
absolute z-10
${isUser ? "right-0" : "left-0"}
transition-all duration-500
transition-all duration-300
opacity-0
pointer-events-none
group-hover:opacity-100

View File

@ -11,20 +11,20 @@ import Locale from "@/app/locales";
import { useLocation, useNavigate } from "react-router-dom";
import { Path } from "@/app/constant";
import { Mask } from "@/app/store/mask";
import { useRef, useEffect, useMemo, useContext } from "react";
import { useRef, useEffect } from "react";
import { showConfirm } from "@/app/components/ui-lib";
import AddIcon from "@/app/icons/addIcon.svg";
import NextChatTitle from "@/app/icons/nextchatTitle.svg";
// import { ListHoodProps } from "@/app/containers/types";
import DeleteChatIcon from "@/app/icons/deleteChatIcon.svg";
import { getTime } from "@/app/utils";
import DeleteIcon from "@/app/icons/deleteIcon.svg";
import LogIcon from "@/app/icons/logIcon.svg";
import MenuLayout, {
MenuWrapperInspectProps,
} from "@/app/components/MenuLayout";
import MenuLayout from "@/app/components/MenuLayout";
import Panel from "./ChatPanel";
import Popover from "@/app/components/Popover";
export function SessionItem(props: {
onClick?: () => void;
@ -90,17 +90,30 @@ export function SessionItem(props: {
{Locale.ChatItem.ChatItemCount(props.count)}
</div>
</div>
<Popover
content={
<div
className={`absolute top-[50%] translate-y-[-50%] right-3 pointer-events-none opacity-0 group-hover:pointer-events-auto group-hover:opacity-100`}
className={`flex items-center gap-3 p-3 rounded-action-btn leading-6`}
onClickCapture={(e) => {
props.onDelete?.();
e.preventDefault();
e.stopPropagation();
}}
>
<DeleteChatIcon />
<div className="flex-1 font-common text-actions-popover-menu-item">
{Locale.Chat.Actions.Delete}
</div>
</div>
}
className=""
>
<div
className={`absolute top-[50%] translate-y-[-50%] right-3 pointer-events-none opacity-0 group-hover:pointer-events-auto group-hover:opacity-100`}
>
<DeleteIcon />
</div>
</Popover>
</div>
)}
</Draggable>
@ -145,26 +158,28 @@ export default MenuLayout(function SessionList(props) {
moveSession(source.index, destination.index);
};
let layoutClassName = "flex flex-col px-0";
let titleClassName = "py-7";
if (isMobileScreen) {
layoutClassName = "flex flex-col pb-chat-panel-mobile ";
titleClassName = "py-6 box-content h-0";
}
return (
<div className={`h-[100%] ${layoutClassName}`}>
<div
className={`
h-[100%] flex flex-col
max-md:pb-chat-panel-mobile
md:px-0
`}
>
<div data-tauri-drag-region>
<div
className={`flex items-center justify-between ${titleClassName}`}
className={`
flex items-center justify-between
py-6 max-md:box-content max-md:h-0
md:py-7
`}
data-tauri-drag-region
>
<div className="">
<NextChatTitle />
</div>
<div
className=""
className=" cursor-pointer"
onClick={() => {
if (config.dontShowMaskSplashScreen) {
chatStore.newSession();

View File

@ -57,9 +57,7 @@ export default function AppSetting(props: AppSettingProps) {
return (
<List
widgetStyle={{
selectClassName: isMobileScreen
? "min-w-select-mobile"
: "min-w-select",
selectClassName: "min-w-select-mobile md:min-w-select",
rangeNextLine: isMobileScreen,
}}
>

View File

@ -10,37 +10,35 @@ export interface ChatHeaderProps {
export default function SettingHeader(props: ChatHeaderProps) {
const { isMobileScreen, goback } = props;
const navigate = useNavigate();
let containerClassName = "";
let titleClassName = "mr-4";
let mainTitleClassName = "";
let subTitleClassName = "";
if (isMobileScreen) {
containerClassName = "h-menu-title-mobile bg-settings-header-mobile";
titleClassName = "flex flex-col items-center justify-center gap-0.5 text";
mainTitleClassName = "text-sm-title h-[19px] leading-5";
subTitleClassName = "text-sm-mobile-tab leading-4";
}
return (
<div
className={`relative flex flex-0 justify-between items-center px-6 py-4 gap-chat-header-gap border-b border-settings-header ${containerClassName}`}
className={`
relative flex flex-0 justify-between items-center px-6 py-4 gap-chat-header-gap border-b border-settings-header
max-md:h-menu-title-mobile max-md:bg-settings-header-mobile
`}
data-tauri-drag-region
>
{isMobileScreen ? (
<div
className="absolute left-4 top-[50%] translate-y-[-50%]"
className="absolute left-4 top-[50%] translate-y-[-50%] cursor-pointer"
onClick={() => goback()}
>
<GobackIcon />
</div>
) : null}
<div className={`flex-1 ${titleClassName}`}>
<div
className={`line-clamp-1 cursor-pointer text-text-settings-panel-header-title text-chat-header-title font-common ${mainTitleClassName}`}
className={`
flex-1
max-md:flex max-md:flex-col max-md:items-center max-md:justify-center max-md:gap-0.5 max-md:text
md:mr-4
`}
>
<div
className={`
line-clamp-1 cursor-pointer text-text-settings-panel-header-title text-chat-header-title font-common
max-md:text-sm-title max-md:h-chat-header-title-mobile max-md:leading-5
`}
>
{Locale.Settings.Title}
</div>

View File

@ -47,26 +47,27 @@ export default function Settings(props: MenuWrapperInspectProps) {
const clientConfig = useMemo(() => getClientConfig(), []);
let containerClassName =
"h-[calc(100%-1.25rem)] my-2.5 ml-1 mr-2.5 rounded-md";
let listBodyClassName = "px-6 py-8";
let cardClassName = "max-w-setting-list mb-8";
if (isMobileScreen) {
containerClassName = "h-setting-panel-mobile";
listBodyClassName = "gap-5 w-[100%] px-4 py-5";
cardClassName = "mb-6";
}
const cardClassName = "mb-6 md:max-w-setting-list md:mb-8";
return (
<div
className={`flex flex-col overflow-hidden bg-settings-panel ${containerClassName}`}
className={`
flex flex-col overflow-hidden bg-settings-panel
h-setting-panel-mobile
md:h-[calc(100%-1.25rem)] md:my-2.5 md:ml-1 md:mr-2.5 md:rounded-md
`}
>
<SettingHeader
isMobileScreen={isMobileScreen}
goback={() => setShowPanel?.(false)}
/>
<div className={`!overflow-x-hidden ${listBodyClassName}`}>
<div
className={`
!overflow-x-hidden
max-md:gap-5 max-md:w-[100%] px-4 py-5
md:px-6 md:py-8
`}
>
<Card className={cardClassName} title={Locale.Settings.Basic.Title}>
<AppSetting />
</Card>

View File

@ -12,21 +12,21 @@ export default MenuLayout(function SettingList(props) {
const { isMobileScreen } = config;
let layoutClassName = "pt-7 px-4";
let titleClassName = "pb-5";
let itemClassName = "";
if (isMobileScreen) {
layoutClassName = "h-[100%] mx-[-1.5rem] px-6 py-6 bg-settings-menu-mobile";
titleClassName = "h-menu-title-mobile";
itemClassName = "p-4 bg-settings-menu-item-mobile";
}
return (
<div className={` ${layoutClassName}`}>
<div
className={`
px-6
max-md:h-[100%] max-md:mx-[-1.5rem] max-md:py-6 max-md:bg-settings-menu-mobile
md:pt-7 md:px-4
`}
>
<div data-tauri-drag-region>
<div
className={`flex items-center justify-between ${titleClassName}`}
className={`
flex items-center justify-between
max-md:h-menu-title-mobile
md:pb-5
`}
data-tauri-drag-region
>
<div className="text-setting-title text-text-settings-menu-title font-common font-setting-title">
@ -39,10 +39,13 @@ export default MenuLayout(function SettingList(props) {
className={`flex flex-col overflow-y-auto overflow-x-hidden w-[100%]`}
>
<div
className={`p-4 font-common text-setting-items font-normal text-text-settings-menu-item-title
border border-settings-menu-item-selected border-opacity-0 rounded-md
hover:border-opacity-100 hover:font-semibold hover:bg-settings-menu-item-selected ${itemClassName}
className={`
p-4 font-common text-setting-items font-normal text-text-settings-menu-item-title
border
border-opacity-0 rounded-md
hover:border-opacity-100 hover:font-semibold hover:bg-settings-menu-item-selected
flex justify-between items-center
max-md:bg-settings-menu-item-mobile
`}
onClick={() => {
setShowPanel?.(true);

View File

@ -41,17 +41,14 @@ export function SideBar(props: { className?: string }) {
selectedTab = Path.Home;
}
let containerClassName = "relative flex h-[100%]";
let tabActionsClassName = "2xl:px-5 xl:px-4 px-2 py-6";
if (isMobileScreen) {
containerClassName = "flex flex-col-reverse w-[100%] h-[100%]";
tabActionsClassName =
"bg-sidebar-mobile rounded-tl-md rounded-tr-md h-mobile";
}
return (
<div className={`${containerClassName}`}>
<div
className={`
flex h-[100%]
max-md:flex-col-reverse max-md:w-[100%]
md:relative
`}
>
<ActionsBar
inMobile={isMobileScreen}
actionsShema={[
@ -113,9 +110,10 @@ export function SideBar(props: { className?: string }) {
mobile: [[Path.Home, Path.Masks, Path.Settings]],
}}
selected={selectedTab}
className={`${
isMobileScreen ? "justify-around" : "flex-col"
} ${tabActionsClassName}`}
className={`
max-md:bg-sidebar-mobile max-md:rounded-tl-md max-md:rounded-tr-md max-md:h-mobile max-md:justify-around
2xl:px-5 xl:px-4 md:px-2 md:py-6 md:flex-col
`}
/>
</div>
);

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" rx="8" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.77152 1.59431C6.13008 1.05646 6.73373 0.733398 7.38015 0.733398H8.61966C9.26607 0.733398 9.86972 1.05646 10.2283 1.59431L10.9877 2.7334H13.9999C14.3313 2.7334 14.5999 3.00203 14.5999 3.3334C14.5999 3.66477 14.3313 3.9334 13.9999 3.9334H1.9999C1.66853 3.9334 1.3999 3.66477 1.3999 3.3334C1.3999 3.00203 1.66853 2.7334 1.9999 2.7334H5.01213L5.77152 1.59431ZM6.45435 2.7334H9.54546L9.22983 2.25995C9.09382 2.05594 8.86485 1.9334 8.61966 1.9334H7.38015C7.13496 1.9334 6.90599 2.05594 6.76998 2.25995L6.45435 2.7334ZM3.33324 4.7334C3.66461 4.7334 3.93324 5.00203 3.93324 5.3334V12.0001C3.93324 13.1415 4.85851 14.0667 5.9999 14.0667H9.9999C11.1413 14.0667 12.0666 13.1415 12.0666 12.0001V5.3334C12.0666 5.00203 12.3352 4.7334 12.6666 4.7334C12.9979 4.7334 13.2666 5.00203 13.2666 5.3334V12.0001C13.2666 13.8042 11.804 15.2667 9.9999 15.2667H5.9999C4.19577 15.2667 2.73324 13.8042 2.73324 12.0001V5.3334C2.73324 5.00203 3.00186 4.7334 3.33324 4.7334ZM6.66657 6.7334C6.99794 6.7334 7.26657 7.00203 7.26657 7.3334V11.3334C7.26657 11.6648 6.99794 11.9334 6.66657 11.9334C6.3352 11.9334 6.06657 11.6648 6.06657 11.3334V7.3334C6.06657 7.00203 6.3352 6.7334 6.66657 6.7334ZM9.33324 6.7334C9.66461 6.7334 9.93324 7.00203 9.93324 7.3334V11.3334C9.93324 11.6648 9.66461 11.9334 9.33324 11.9334C9.00186 11.9334 8.73324 11.6648 8.73324 11.3334V7.3334C8.73324 7.00203 9.00186 6.7334 9.33324 6.7334Z" fill="#FF5454"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -22,6 +22,8 @@ body {
height: 100%;
width: 100%;
overflow: hidden;
font-family: PingFang, "SF Pro Text", "SF Pro Icons", "Helvetica Neue",
Helvetica, Arial, sans-serif;
}
*:focus-visible {

View File

@ -43,6 +43,7 @@
"sass": "^1.59.2",
"spark-md5": "^3.0.2",
"use-debounce": "^9.0.4",
"use-resize-observer": "^9.1.0",
"zustand": "^4.3.8"
},
"devDependencies": {

View File

@ -4,6 +4,13 @@ module.exports = {
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
screens: {
sm: '480px',
md: '768px',
lg: '1120px',
xl: '1440px',
'2xl': '1980px'
},
fontSize: {
sm: '0.75rem',
'sm-mobile': '0.875rem',
@ -19,16 +26,9 @@ module.exports = {
'setting-card-title': '600',
},
fontFamily: {
'common': ['Satoshi Variable', 'Variable'],
'time': ['Hind', 'Variable'],
'setting-card-title': ['PingFang HK', 'PingFang']
},
screens: {
sm: '480px',
md: '768px',
lg: '1120px',
xl: '1440px',
'2xl': '1980px'
'common': ['Satoshi Variable', "SF Pro Text", "SF Pro Icons", "Helvetica Neue", 'Helvetica', 'Arial', 'sans-serif'],
'time': ['Hind', "SF Pro Text", "SF Pro Icons", "Helvetica Neue", 'Helvetica', 'Arial', 'sans-serif'],
'setting-card-title': ['PingFang HK', 'PingFang', "SF Pro Text", "SF Pro Icons", "Helvetica Neue", 'Helvetica', 'Arial', 'sans-serif']
},
extend: {
lineHeight: {
@ -53,6 +53,7 @@ module.exports = {
'setting-panel-mobile': 'calc(100vh - var(--siderbar-mobile-height))',
'slide-btn': '18px',
'switch': '1rem',
'chat-header-title-mobile': '19px',
},
minWidth: {
'select-mobile-lg': '200px',

View File

@ -1242,6 +1242,11 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
"@juggle/resize-observer@^3.3.1":
version "3.4.0"
resolved "https://registry.npmmirror.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60"
integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==
"@next/env@13.4.9":
version "13.4.9"
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.9.tgz#b77759514dd56bfa9791770755a2482f4d6ca93e"
@ -6532,6 +6537,13 @@ use-memo-one@^1.1.3:
resolved "https://registry.npmmirror.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"
integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==
use-resize-observer@^9.1.0:
version "9.1.0"
resolved "https://registry.npmmirror.com/use-resize-observer/-/use-resize-observer-9.1.0.tgz#14735235cf3268569c1ea468f8a90c5789fc5c6c"
integrity sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==
dependencies:
"@juggle/resize-observer" "^3.3.1"
use-sync-external-store@1.2.0, use-sync-external-store@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"