mirror of
				https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
				synced 2025-11-04 16:23:41 +08:00 
			
		
		
		
	Merge branch 'feat-redesign-ui' into v3
This commit is contained in:
		@@ -42,7 +42,7 @@ export default function Btn(props: BtnProps) {
 | 
			
		||||
      } text-text-btn-primary `;
 | 
			
		||||
      break;
 | 
			
		||||
    case "danger":
 | 
			
		||||
      btnClassName = `bg-danger-btn text-text-btn-danger hover:bg-hovered-btn`;
 | 
			
		||||
      btnClassName = `bg-danger-btn text-text-btn-danger hover:bg-hovered-danger-btn`;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      btnClassName = `bg-default-btn text-text-btn-default hover:bg-hovered-btn`;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ import {
 | 
			
		||||
  DetailedHTMLProps,
 | 
			
		||||
  InputHTMLAttributes,
 | 
			
		||||
  useContext,
 | 
			
		||||
  useEffect,
 | 
			
		||||
  useLayoutEffect,
 | 
			
		||||
  useState,
 | 
			
		||||
} from "react";
 | 
			
		||||
@@ -17,6 +16,7 @@ export interface CommonInputProps
 | 
			
		||||
  > {
 | 
			
		||||
  className?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface NumberInputProps {
 | 
			
		||||
  onChange?: (v: number) => void;
 | 
			
		||||
  type?: "number";
 | 
			
		||||
@@ -49,12 +49,16 @@ export default function Input(props: CommonInputProps & InputProps) {
 | 
			
		||||
 | 
			
		||||
  const internalType = (show && "text") || type;
 | 
			
		||||
 | 
			
		||||
  const { update } = useContext(List.ListContext);
 | 
			
		||||
  const { update, handleValidate } = useContext(List.ListContext);
 | 
			
		||||
 | 
			
		||||
  useLayoutEffect(() => {
 | 
			
		||||
    update?.({ type: "input" });
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  useLayoutEffect(() => {
 | 
			
		||||
    handleValidate?.(value);
 | 
			
		||||
  }, [value]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      className={` group/input w-[100%] rounded-chat-input bg-input transition-colors duration-300 ease-in-out flex gap-3 items-center px-3 py-2 ${className} hover:bg-select-hover ${inputClassName}`}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ interface WidgetStyle {
 | 
			
		||||
 | 
			
		||||
interface ChildrenMeta {
 | 
			
		||||
  type?: "unknown" | "input" | "range";
 | 
			
		||||
  error?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ListProps {
 | 
			
		||||
@@ -27,6 +28,15 @@ export interface ListProps {
 | 
			
		||||
  widgetStyle?: WidgetStyle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Error =
 | 
			
		||||
  | {
 | 
			
		||||
      error: true;
 | 
			
		||||
      message: string;
 | 
			
		||||
    }
 | 
			
		||||
  | {
 | 
			
		||||
      error: false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
export interface ListItemProps {
 | 
			
		||||
  title: string;
 | 
			
		||||
  subTitle?: string;
 | 
			
		||||
@@ -34,10 +44,15 @@ export interface ListItemProps {
 | 
			
		||||
  className?: string;
 | 
			
		||||
  onClick?: () => void;
 | 
			
		||||
  nextline?: boolean;
 | 
			
		||||
  validator?: (v: any) => Error | Promise<Error>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ListContext = createContext<
 | 
			
		||||
  { isMobileScreen?: boolean; update?: (m: ChildrenMeta) => void } & WidgetStyle
 | 
			
		||||
  {
 | 
			
		||||
    isMobileScreen?: boolean;
 | 
			
		||||
    update?: (m: ChildrenMeta) => void;
 | 
			
		||||
    handleValidate?: (v: any) => void;
 | 
			
		||||
  } & WidgetStyle
 | 
			
		||||
>({ isMobileScreen: false });
 | 
			
		||||
 | 
			
		||||
export function ListItem(props: ListItemProps) {
 | 
			
		||||
@@ -48,6 +63,7 @@ export function ListItem(props: ListItemProps) {
 | 
			
		||||
    subTitle,
 | 
			
		||||
    children,
 | 
			
		||||
    nextline,
 | 
			
		||||
    validator,
 | 
			
		||||
  } = props;
 | 
			
		||||
 | 
			
		||||
  const context = useContext(ListContext);
 | 
			
		||||
@@ -56,9 +72,11 @@ export function ListItem(props: ListItemProps) {
 | 
			
		||||
 | 
			
		||||
  const { inputNextLine, rangeNextLine } = context;
 | 
			
		||||
 | 
			
		||||
  const { type, error } = childrenMeta;
 | 
			
		||||
 | 
			
		||||
  let internalNextLine;
 | 
			
		||||
 | 
			
		||||
  switch (childrenMeta.type) {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case "input":
 | 
			
		||||
      internalNextLine = !!(nextline || inputNextLine);
 | 
			
		||||
      break;
 | 
			
		||||
@@ -70,7 +88,22 @@ export function ListItem(props: ListItemProps) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const update = useCallback((m: ChildrenMeta) => {
 | 
			
		||||
    setMeta(m);
 | 
			
		||||
    setMeta((pre) => ({ ...pre, ...m }));
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  const handleValidate = useCallback((v: any) => {
 | 
			
		||||
    const insideValidator = validator || (() => {});
 | 
			
		||||
 | 
			
		||||
    Promise.resolve(insideValidator(v)).then((result) => {
 | 
			
		||||
      if (result && result.error) {
 | 
			
		||||
        return update({
 | 
			
		||||
          error: result.message,
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      update({
 | 
			
		||||
        error: undefined,
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
@@ -88,13 +121,18 @@ 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, handleValidate }}>
 | 
			
		||||
        <div
 | 
			
		||||
          className={`${
 | 
			
		||||
            internalNextLine ? "mt-[0.625rem]" : "max-w-[70%]"
 | 
			
		||||
          } flex items-center justify-center`}
 | 
			
		||||
          } flex flex-col items-center justify-center`}
 | 
			
		||||
        >
 | 
			
		||||
          {children}
 | 
			
		||||
          <div>{children}</div>
 | 
			
		||||
          {!!error && (
 | 
			
		||||
            <div className="text-text-btn-danger text-sm-mobile-tab mt-[0.3125rem] flex items-start w-[100%]">
 | 
			
		||||
              <div className="">{error}</div>
 | 
			
		||||
            </div>
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
      </ListContext.Provider>
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -61,6 +61,7 @@ export default function MenuLayout<
 | 
			
		||||
      <div
 | 
			
		||||
        className={`
 | 
			
		||||
          w-[100%] relative bg-center
 | 
			
		||||
          max-md:h-[100%]
 | 
			
		||||
          md:flex md:my-2.5
 | 
			
		||||
        `}
 | 
			
		||||
      >
 | 
			
		||||
 
 | 
			
		||||
@@ -96,7 +96,7 @@ const Select = <Value extends number | string>(props: SearchProps<Value>) => {
 | 
			
		||||
      className={selectClassName}
 | 
			
		||||
    >
 | 
			
		||||
      <div
 | 
			
		||||
        className={`flex items-center gap-3 py-2 px-3 bg-select rounded-action-btn font-time text-sm-title  cursor-pointer hover:bg-select-hover transition duration-300 ease-in-out`}
 | 
			
		||||
        className={`flex items-center gap-3 py-2 px-3 bg-select rounded-action-btn font-common text-sm-title  cursor-pointer hover:bg-select-hover transition duration-300 ease-in-out`}
 | 
			
		||||
        ref={contentRef}
 | 
			
		||||
      >
 | 
			
		||||
        <div
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,9 @@
 | 
			
		||||
  &-body {
 | 
			
		||||
    margin-top: 20px;
 | 
			
		||||
  }
 | 
			
		||||
  div:not(.no-dark) > svg {
 | 
			
		||||
    filter: invert(0.5);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.export-content {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,10 @@
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
 | 
			
		||||
  div:not(.no-dark) > svg {
 | 
			
		||||
    filter: invert(0.5);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .mask-page-body {
 | 
			
		||||
    padding: 20px;
 | 
			
		||||
    overflow-y: auto;
 | 
			
		||||
 
 | 
			
		||||
@@ -398,7 +398,7 @@ export function ContextPrompts(props: {
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function MaskPage() {
 | 
			
		||||
export function MaskPage(props: { className?: string }) {
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
 | 
			
		||||
  const maskStore = useMaskStore();
 | 
			
		||||
@@ -465,14 +465,13 @@ export function MaskPage() {
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const isMobileScreen = useMobileScreen();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <div
 | 
			
		||||
        className={`${styles["mask-page"]} !bg-gray-50 ${
 | 
			
		||||
          isMobileScreen ? "pb-chat-panel-mobile" : ""
 | 
			
		||||
        }`}
 | 
			
		||||
        className={`
 | 
			
		||||
          ${styles["mask-page"]} 
 | 
			
		||||
          ${props.className}
 | 
			
		||||
          `}
 | 
			
		||||
      >
 | 
			
		||||
        <div className="window-header">
 | 
			
		||||
          <div className="window-header-title">
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,10 @@
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
 | 
			
		||||
  div:not(.no-dark) > svg {
 | 
			
		||||
    filter: invert(0.5);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .mask-header {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: space-between;
 | 
			
		||||
 
 | 
			
		||||
@@ -72,7 +72,7 @@ function useMaskGroup(masks: Mask[]) {
 | 
			
		||||
  return groups;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function NewChat() {
 | 
			
		||||
export function NewChat(props: { className?: string }) {
 | 
			
		||||
  const chatStore = useChatStore();
 | 
			
		||||
  const maskStore = useMaskStore();
 | 
			
		||||
 | 
			
		||||
@@ -115,9 +115,10 @@ export function NewChat() {
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      className={`${styles["new-chat"]} !bg-gray-50 px-1 ${
 | 
			
		||||
        isMobileScreen ? "pb-chat-panel-mobile" : ""
 | 
			
		||||
      }`}
 | 
			
		||||
      className={`
 | 
			
		||||
      ${styles["new-chat"]}
 | 
			
		||||
      ${props.className}
 | 
			
		||||
      `}
 | 
			
		||||
    >
 | 
			
		||||
      <div className={styles["mask-header"]}>
 | 
			
		||||
        <IconButton
 | 
			
		||||
 
 | 
			
		||||
@@ -301,7 +301,7 @@ export default forwardRef<ChatInputPanelInstance, ChatInputPanelProps>(
 | 
			
		||||
            {!isMobileScreen && (
 | 
			
		||||
              <div className="flex items-center justify-center text-sm gap-3">
 | 
			
		||||
                <div className="flex-1"> </div>
 | 
			
		||||
                <div className="text-text-chat-input-placeholder text-time line-clamp-1">
 | 
			
		||||
                <div className="text-text-chat-input-placeholder font-common line-clamp-1">
 | 
			
		||||
                  {Locale.Chat.Input(submitKey)}
 | 
			
		||||
                </div>
 | 
			
		||||
                <Btn
 | 
			
		||||
 
 | 
			
		||||
@@ -185,7 +185,7 @@ export default function ChatMessagePanel(props: ChatMessagePanelProps) {
 | 
			
		||||
                }`}
 | 
			
		||||
              >
 | 
			
		||||
                <div
 | 
			
		||||
                  className={` pointer-events-none  text-text-chat-message-date text-right text-time whitespace-nowrap transition-all duration-500 text-sm absolute z-1 ${
 | 
			
		||||
                  className={` pointer-events-none  text-text-chat-message-date text-right font-common whitespace-nowrap transition-all duration-500 text-sm absolute z-1 ${
 | 
			
		||||
                    isUser ? "right-0" : "left-0"
 | 
			
		||||
                  } bottom-[100%] hidden group-hover:block`}
 | 
			
		||||
                >
 | 
			
		||||
 
 | 
			
		||||
@@ -124,13 +124,17 @@ const ModelSelect = () => {
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Popover
 | 
			
		||||
      content={content({ close: () => {} })}
 | 
			
		||||
      content={
 | 
			
		||||
        <div className="max-h-chat-actions-select-model-popover overflow-y-auto">
 | 
			
		||||
          {content({ close: () => {} })}
 | 
			
		||||
        </div>
 | 
			
		||||
      }
 | 
			
		||||
      trigger="click"
 | 
			
		||||
      noArrow
 | 
			
		||||
      placement={
 | 
			
		||||
        position?.poi.relativePosition[1] !== Orientation.bottom ? "lb" : "lt"
 | 
			
		||||
      }
 | 
			
		||||
      popoverClassName="border border-select-popover rounded-lg shadow-select-popover-shadow w-actions-popover  bg-model-select-popover-panel max-h-chat-actions-select-model-popover  w-[280px]"
 | 
			
		||||
      popoverClassName="border border-select-popover rounded-lg shadow-select-popover-shadow w-actions-popover  bg-model-select-popover-panel w-[280px]"
 | 
			
		||||
      onShow={(e) => {
 | 
			
		||||
        if (e) {
 | 
			
		||||
          autoScrollToSelectedModal();
 | 
			
		||||
 
 | 
			
		||||
@@ -67,15 +67,14 @@ export default function PromptHints(props: {
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      className={`
 | 
			
		||||
      ${styles["prompt-hints"]} 
 | 
			
		||||
      transition-all duration-300 shadow-prompt-hint-container rounded-none w-[100%] flex flex-col-reverse overflow-auto
 | 
			
		||||
      ${
 | 
			
		||||
        notShowPrompt
 | 
			
		||||
          ? "max-h-[0vh] border-none"
 | 
			
		||||
          : "border-b pt-2.5 max-h-[50vh]"
 | 
			
		||||
      } 
 | 
			
		||||
      ${props.className}
 | 
			
		||||
    `}
 | 
			
		||||
        transition-all duration-300 shadow-prompt-hint-container rounded-none  flex flex-col-reverse overflow-x-hidden
 | 
			
		||||
        ${
 | 
			
		||||
          notShowPrompt
 | 
			
		||||
            ? "max-h-[0vh] border-none"
 | 
			
		||||
            : "border-b pt-2.5 max-h-[50vh]"
 | 
			
		||||
        } 
 | 
			
		||||
        ${props.className}
 | 
			
		||||
      `}
 | 
			
		||||
    >
 | 
			
		||||
      {internalPrompts.map((prompt, i) => (
 | 
			
		||||
        <div
 | 
			
		||||
 
 | 
			
		||||
@@ -525,38 +525,33 @@
 | 
			
		||||
  text-overflow: ellipsis;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.prompt-hints {
 | 
			
		||||
  .prompt-hint {
 | 
			
		||||
    color:var(--btn-default-text);
 | 
			
		||||
    padding: 6px 10px;
 | 
			
		||||
    // animation: slide-in ease 0.3s;
 | 
			
		||||
    // cursor: pointer;
 | 
			
		||||
    // transition: all ease 0.3s;
 | 
			
		||||
    border: transparent 1px solid;
 | 
			
		||||
    margin: 4px;
 | 
			
		||||
    border-radius: 8px;
 | 
			
		||||
.prompt-hint {
 | 
			
		||||
  color:var(--btn-default-text);
 | 
			
		||||
  padding: 6px 10px;
 | 
			
		||||
  border: transparent 1px solid;
 | 
			
		||||
  margin: 4px;
 | 
			
		||||
  border-radius: 8px;
 | 
			
		||||
 | 
			
		||||
    &:not(:last-child) {
 | 
			
		||||
      margin-top: 0;
 | 
			
		||||
    }
 | 
			
		||||
  &:not(:last-child) {
 | 
			
		||||
    margin-top: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    .hint-title {
 | 
			
		||||
      font-size: 12px;
 | 
			
		||||
      font-weight: bolder;
 | 
			
		||||
  .hint-title {
 | 
			
		||||
    font-size: 12px;
 | 
			
		||||
    font-weight: bolder;
 | 
			
		||||
 | 
			
		||||
      @include single-line();
 | 
			
		||||
    }
 | 
			
		||||
    @include single-line();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    .hint-content {
 | 
			
		||||
      font-size: 12px;
 | 
			
		||||
  .hint-content {
 | 
			
		||||
    font-size: 12px;
 | 
			
		||||
 | 
			
		||||
      @include single-line();
 | 
			
		||||
    }
 | 
			
		||||
    @include single-line();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    &-selected,
 | 
			
		||||
    &:hover {
 | 
			
		||||
      border-color: var(--primary);
 | 
			
		||||
    }
 | 
			
		||||
  &-selected,
 | 
			
		||||
  &:hover {
 | 
			
		||||
    border-color: var(--primary);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -153,6 +153,16 @@ export default function ModelSetting(props: {
 | 
			
		||||
            title={Locale.Settings.InputTemplate.Title}
 | 
			
		||||
            subTitle={Locale.Settings.InputTemplate.SubTitle}
 | 
			
		||||
            nextline={isMobileScreen}
 | 
			
		||||
            validator={(v: string) => {
 | 
			
		||||
              if (!v.includes("{{input}}")) {
 | 
			
		||||
                return {
 | 
			
		||||
                  error: true,
 | 
			
		||||
                  message: Locale.Settings.InputTemplate.Error,
 | 
			
		||||
                };
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              return { error: false };
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <Input
 | 
			
		||||
              type="text"
 | 
			
		||||
@@ -160,7 +170,6 @@ export default function ModelSetting(props: {
 | 
			
		||||
              onChange={(e = "") =>
 | 
			
		||||
                props.updateConfig((config) => (config.template = e))
 | 
			
		||||
              }
 | 
			
		||||
              className="text-center"
 | 
			
		||||
            ></Input>
 | 
			
		||||
          </ListItem>
 | 
			
		||||
        </>
 | 
			
		||||
 
 | 
			
		||||
@@ -71,15 +71,13 @@ export default MenuLayout(function SettingList(props) {
 | 
			
		||||
              cursor-pointer
 | 
			
		||||
              border 
 | 
			
		||||
              rounded-md
 | 
			
		||||
 | 
			
		||||
              bg-chat-menu-session-unselected border-chat-menu-session-unselected
 | 
			
		||||
              border-transparent
 | 
			
		||||
              ${
 | 
			
		||||
                selected === i.id && !isMobileScreen
 | 
			
		||||
                  ? `!bg-chat-menu-session-selected !border-chat-menu-session-selected !font-medium`
 | 
			
		||||
                  : `hover:bg-chat-menu-session-hovered hover:chat-menu-session-hovered`
 | 
			
		||||
                  : `hover:bg-chat-menu-session-unselected hover:border-chat-menu-session-unselected`
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              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
 | 
			
		||||
            `}
 | 
			
		||||
 
 | 
			
		||||
@@ -111,8 +111,30 @@ export default function Home() {
 | 
			
		||||
          <ErrorBoundary>
 | 
			
		||||
            <Routes>
 | 
			
		||||
              <Route path={Path.Home} element={<Chat />} />
 | 
			
		||||
              <Route path={Path.NewChat} element={<NewChat />} />
 | 
			
		||||
              <Route path={Path.Masks} element={<MaskPage />} />
 | 
			
		||||
              <Route
 | 
			
		||||
                path={Path.NewChat}
 | 
			
		||||
                element={
 | 
			
		||||
                  <NewChat
 | 
			
		||||
                    className={`
 | 
			
		||||
              md:w-[100%] px-1
 | 
			
		||||
              ${config.theme === "dark" ? "bg-[var(--white)]" : "bg-gray-50"}
 | 
			
		||||
              ${config.isMobileScreen ? "pb-chat-panel-mobile" : ""}
 | 
			
		||||
              `}
 | 
			
		||||
                  />
 | 
			
		||||
                }
 | 
			
		||||
              />
 | 
			
		||||
              <Route
 | 
			
		||||
                path={Path.Masks}
 | 
			
		||||
                element={
 | 
			
		||||
                  <MaskPage
 | 
			
		||||
                    className={`
 | 
			
		||||
                md:w-[100%]
 | 
			
		||||
                ${config.theme === "dark" ? "bg-[var(--white)]" : "bg-gray-50"}
 | 
			
		||||
                ${config.isMobileScreen ? "pb-chat-panel-mobile" : ""}
 | 
			
		||||
              `}
 | 
			
		||||
                  />
 | 
			
		||||
                }
 | 
			
		||||
              />
 | 
			
		||||
              <Route path={Path.Chat} element={<Chat />} />
 | 
			
		||||
              <Route path={Path.Settings} element={<Settings />} />
 | 
			
		||||
            </Routes>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								app/fonts/Satoshi-Variable.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/fonts/Satoshi-Variable.ttf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/fonts/Satoshi-Variable.woff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/fonts/Satoshi-Variable.woff
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/fonts/Satoshi-Variable.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/fonts/Satoshi-Variable.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -1,5 +1,6 @@
 | 
			
		||||
import { useLayoutEffect } from "react";
 | 
			
		||||
import { Theme, useAppConfig } from "@/app/store/config";
 | 
			
		||||
import { getCSSVar } from "../utils";
 | 
			
		||||
 | 
			
		||||
const DARK_CLASS = "dark-new";
 | 
			
		||||
const LIGHT_CLASS = "light-new";
 | 
			
		||||
@@ -17,4 +18,31 @@ export function useSwitchTheme() {
 | 
			
		||||
      document.body.classList.add(LIGHT_CLASS);
 | 
			
		||||
    }
 | 
			
		||||
  }, [config.theme]);
 | 
			
		||||
 | 
			
		||||
  useLayoutEffect(() => {
 | 
			
		||||
    document.body.classList.remove("light");
 | 
			
		||||
    document.body.classList.remove("dark");
 | 
			
		||||
 | 
			
		||||
    if (config.theme === "dark") {
 | 
			
		||||
      document.body.classList.add("dark");
 | 
			
		||||
    } else if (config.theme === "light") {
 | 
			
		||||
      document.body.classList.add("light");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const metaDescriptionDark = document.querySelector(
 | 
			
		||||
      'meta[name="theme-color"][media*="dark"]',
 | 
			
		||||
    );
 | 
			
		||||
    const metaDescriptionLight = document.querySelector(
 | 
			
		||||
      'meta[name="theme-color"][media*="light"]',
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (config.theme === "auto") {
 | 
			
		||||
      metaDescriptionDark?.setAttribute("content", "#151515");
 | 
			
		||||
      metaDescriptionLight?.setAttribute("content", "#fafafa");
 | 
			
		||||
    } else {
 | 
			
		||||
      const themeColor = getCSSVar("--theme-color");
 | 
			
		||||
      metaDescriptionDark?.setAttribute("content", themeColor);
 | 
			
		||||
      metaDescriptionLight?.setAttribute("content", themeColor);
 | 
			
		||||
    }
 | 
			
		||||
  }, [config.theme]);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -170,6 +170,7 @@ const cn = {
 | 
			
		||||
    InputTemplate: {
 | 
			
		||||
      Title: "用户输入预处理",
 | 
			
		||||
      SubTitle: "用户最新的一条消息会填充到此模板",
 | 
			
		||||
      Error: "模板中必须携带占位符{{input}}",
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    Update: {
 | 
			
		||||
 
 | 
			
		||||
@@ -174,6 +174,7 @@ const en: LocaleType = {
 | 
			
		||||
    InputTemplate: {
 | 
			
		||||
      Title: "Input Template",
 | 
			
		||||
      SubTitle: "Newest message will be filled to this template",
 | 
			
		||||
      Error: "Placeholder {{input}} must be included in the template",
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    Update: {
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,7 @@ body {
 | 
			
		||||
  --danger-btn-bg: #fff6f6;
 | 
			
		||||
  --default-btn-bg: #f7f7f8;
 | 
			
		||||
  --hovered-btn-bg: rgba(0, 0, 0, 0.05);
 | 
			
		||||
  --hovered-danger-btn-bg: #FFE7E7;
 | 
			
		||||
  --card-bg: #fff;
 | 
			
		||||
  --input-bg: #f7f7f8;
 | 
			
		||||
  --list-item-divider-bg: #f0f0f3;
 | 
			
		||||
@@ -191,6 +192,7 @@ body {
 | 
			
		||||
  --danger-btn-bg: #20131A;
 | 
			
		||||
  --default-btn-bg: #1D1D1D;
 | 
			
		||||
  --hovered-btn-bg: #303030;
 | 
			
		||||
  --hovered-danger-btn-bg:#303030;
 | 
			
		||||
  --card-bg: #111;
 | 
			
		||||
  --input-bg: #1D1D1D;
 | 
			
		||||
  --list-item-divider-bg: #303030;
 | 
			
		||||
@@ -216,7 +218,7 @@ body {
 | 
			
		||||
  --chat-panel-message-mobile-bg: #303030;
 | 
			
		||||
  --chat-message-actions-btn-hovered-bg: rgba(255, 255, 255, 0.05);
 | 
			
		||||
  --chat-panel-bg: #1D1D1D;
 | 
			
		||||
  --chat-panel-message-clear-divider-bg: #e2e2e6; //////////
 | 
			
		||||
  --chat-panel-message-clear-divider-bg: #a5a5b3;
 | 
			
		||||
  --chat-menu-session-selected-bg: #182455;
 | 
			
		||||
  --chat-menu-session-unselected-bg: #303030;
 | 
			
		||||
  --chat-menu-session-hovered-bg: #404040;
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,9 @@
 | 
			
		||||
    font-feature-settings:
 | 
			
		||||
      "clig" off,
 | 
			
		||||
      "liga" off;
 | 
			
		||||
    src: url(/fonts/Roboto.woff2) format("woff2");
 | 
			
		||||
    src:
 | 
			
		||||
      url("../fonts/Satoshi-Variable.woff2") format("woff2"),
 | 
			
		||||
      url("../fonts/Satoshi-Variable.woff") format("woff"),
 | 
			
		||||
      url("../fonts/Satoshi-Variable.ttf") format("truetype");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -38,10 +38,6 @@
 | 
			
		||||
  --border-in-light: 1px solid rgba(255, 255, 255, 0.192);
 | 
			
		||||
 | 
			
		||||
  --theme-color: var(--gray);
 | 
			
		||||
 | 
			
		||||
  div:not(.no-dark) > svg {
 | 
			
		||||
    filter: invert(0.5);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.light {
 | 
			
		||||
 
 | 
			
		||||
@@ -90,6 +90,7 @@ module.exports = {
 | 
			
		||||
        'danger-btn': 'var(--danger-btn-bg)',
 | 
			
		||||
        'default-btn': 'var(--default-btn-bg)',
 | 
			
		||||
        'hovered-btn': 'var(--hovered-btn-bg)',
 | 
			
		||||
        'hovered-danger-btn': 'var(--hovered-danger-btn-bg)',
 | 
			
		||||
        'card': 'var(--card-bg)',
 | 
			
		||||
        'input': 'var(--input-bg)',
 | 
			
		||||
        'list-item-divider': 'var(--list-item-divider-bg)',
 | 
			
		||||
@@ -186,6 +187,7 @@ module.exports = {
 | 
			
		||||
        'chat-menu-session-unselected-mobile': 'var(--chat-menu-session-unselected-mobile-border)',
 | 
			
		||||
        'chat-menu-session-hovered': 'var(--chat-menu-session-hovered-border)',
 | 
			
		||||
        'modal-header-bottom': 'var(--modal-header-bottom-border)',
 | 
			
		||||
        'transparent': 'transparent',
 | 
			
		||||
 | 
			
		||||
        'text-sidebar-tab-mobile-active': 'var(--sidebar-tab-mobile-active-text)',
 | 
			
		||||
        'text-sidebar-tab-mobile-inactive': 'var(--sidebar-tab-mobile-inactive-text)',
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user