Browse Source

feat: 添加AI创作,修改mermaid导入

liaojiaxing 1 month ago
parent
commit
8862b483f6

+ 62 - 0
apps/designer/src/api/ai.ts

@@ -0,0 +1,62 @@
+import { request } from "umi";
+
+/**
+ * 获取会话列表
+ * @param app_name 应用名称
+ * @param page_index 页码
+ */
+export const GetSessionList = (params: {
+  app_name: string;
+  page_index: number;
+}) =>
+  request("/api/ai/chat-session/list", {
+    method: "get",
+    params,
+  });
+
+/**
+ * 获取会话消息列表
+ * @param app_name 应用名称
+ * @param session_id 会话id
+ * @param page_index 页码
+ */
+export const GetSessionMessageList = (params: {
+  app_name: string;
+  session_id: string;
+  page_index: number;
+}) =>
+  request("/api/ai/chat-message/list", {
+    method: "get",
+    params,
+  });
+
+
+/**
+ * 修改会话名称
+ * @param app_name 应用名称
+ * @param session_id 会话id
+ * @param page_index 页码
+ */
+export const ChangeSessionName = (data: {
+  app_name: string;
+  session_id: string;
+  new_name: string;
+}) =>
+  request("/api/ai/chat-session/rename", {
+    method: "post",
+    data,
+  });
+
+  /**
+ * 删除会话
+ * @param app_name 应用名称
+ * @param session_id 会话id
+ */
+export const DeleteSession = (data: {
+  app_name: string;
+  session_id: string;
+}) =>
+  request("/api/ai/chat-session/delete", {
+    method: "post",
+    data,
+  });

+ 24 - 24
apps/designer/src/app.ts

@@ -1,45 +1,45 @@
-import { message } from 'antd';
-import type { RequestConfig } from 'umi';
+import { message } from "antd";
+import type { RequestConfig } from "umi";
 
 export const request: RequestConfig = {
   timeout: 10000,
   // other axios options you want
   errorConfig: {
-    errorHandler(err){
+    errorHandler(err) {
       console.log(err);
-
     },
-    errorThrower(){
-    }
+    errorThrower() {},
   },
   requestInterceptors: [
     (url, options) => {
-      const baseUrl = process.env.NODE_ENV === 'production' ? '' : '/api'//'http://ab.dev.jbpm.shalu.com' // https://edesign.shalu.com'
-      const enterpriseCode = sessionStorage.getItem('enterpriseCode');
-      const token = localStorage.getItem('token_' + enterpriseCode);
- 
-      if(token) {
-        if(!options.headers) {
-          options.headers = {}
+      const baseUrl = process.env.NODE_ENV === "production" ? "" : "/api"; //'http://ab.dev.jbpm.shalu.com' // https://edesign.shalu.com'
+      const enterpriseCode = sessionStorage.getItem("enterpriseCode");
+      const token = localStorage.getItem("token_" + enterpriseCode);
+
+      if (token) {
+        if (!options.headers) {
+          options.headers = {};
+        }
+        if(!options.headers?.Authorization) {
+          options.headers.Authorization = token;
         }
-        options.headers.Authorization = token
       }
 
       return {
-        url: baseUrl + url,
-        options
-      }
-    }
+        url: url?.includes("//:") ? url : baseUrl + url,
+        options,
+      };
+    },
   ],
   responseInterceptors: [
     (response) => {
-      const {data = {} as any, config} = response;
-      if(data?.error) {
+      const { data = {} as any, config } = response;
+      if (data?.error) {
         message.error(data.error);
         return Promise.reject(data.error);
       }
-      
+
       return response;
-    }
-  ]
-};
+    },
+  ],
+};

+ 80 - 42
apps/designer/src/components/ai/AIChat.tsx

@@ -18,10 +18,11 @@ import {
   Dropdown,
   MenuProps,
 } from "antd";
-import { useChat, Message } from "ai/react";
+import { Message } from "ai/react";
 import MarkdownViewer from "./MarkdownViewer";
 import { uuid } from "@repo/utils";
 import { useLocalStorageState } from "ahooks";
+import { useModel } from "umi";
 
 // 用户消息
 function UserMessage({ message }: { message: Message }) {
@@ -74,9 +75,7 @@ function MessageInfo({ message }: { message: Message }) {
       {message.role === "assistant" && (
         <AssistantMessage message={message}></AssistantMessage>
       )}
-      {message.role === "user" && (
-        <UserMessage message={message}></UserMessage>
-      )}
+      {message.role === "user" && <UserMessage message={message}></UserMessage>}
     </div>
   );
 }
@@ -144,42 +143,22 @@ function groupDataByDate(data: ChatHistoryItem[]): DateGroups {
 }
 
 export default function AIChat(props: { onClose?: () => void }) {
-  const [focused, setFocused] = React.useState(false);
-  const [chatStarted, setChatStarted] = React.useState(false);
-  const [scrollHeight, setScrollHeight] = React.useState(0);
-  const scrollAreaRef = React.useRef<HTMLDivElement>(null);
+  const [focused, setFocused] = useState(false);
+  const [chatStarted, setChatStarted] = useState(false);
+  const [scrollHeight, setScrollHeight] = useState(0);
+  const scrollAreaRef = useRef<HTMLDivElement>(null);
   const observer = useRef<ResizeObserver | null>(null);
+  const [messages, setMessages] = useState<Message[]>([]);
+  const [inputVal, setInputVal] = useState("");
   // 对话历史
   const [history, setHistory] = useLocalStorageState<ChatHistoryItem[]>(
     "chat-history",
     { defaultValue: [] }
   );
+  const { loading, agent, cancel } = useModel("aiModel");
 
   const [chatId, setChatId] = React.useState(uuid());
 
-  const {
-    messages,
-    input,
-    handleInputChange,
-    handleSubmit,
-    isLoading,
-    stop,
-    error,
-    reload,
-    setMessages,
-  } = useChat({
-    api: "http://localhost:3000/ai/chat",
-    keepLastMessageOnError: true,
-    id: chatId,
-    initialMessages: [
-      {
-        id: uuid(),
-        role: "system",
-        content: "当用户提出一个流程图设计需求是,以mermaid 语法返回流程图代码",
-      },
-    ],
-  });
-
   useEffect(() => {
     // 判断messages是否存在消息
     if (!messages.length) {
@@ -216,12 +195,63 @@ export default function AIChat(props: { onClose?: () => void }) {
     }
   }, [messages, chatId]);
 
+  // 发起请求
+  const onRequest = () => {
+    agent.request(
+      {
+        // 应用名称
+        app_name: "app1",
+        // 会话内容
+        chat_query: inputVal,
+        // 会话名称 第一次
+        chat_name: "新会话",
+        // 会话id 后续会话带入
+        // conversation_id: ;
+      },
+      {
+        onSuccess: (msg) => {
+          console.log("success", msg);
+          setMessages((messages) => {
+            const arr = [...messages];
+            return arr;
+          });
+        },
+        onError: (error) => {
+          console.log("err:", error);
+        },
+        onUpdate: (msg) => {
+          console.log("update", msg);
+          setMessages((messages) => {
+            const arr = [...messages];
+            arr[messages.length - 1].content += msg.answer;
+            arr[messages.length - 1].id = msg.message_id;
+            setChatId(msg.conversation_id);
+            return arr;
+          });
+        },
+      }
+    );
+    setInputVal("");
+  };
+
   // 处理提交
   const onSubmit = () => {
-    if (input.trim()) {
-      handleSubmit();
+    if (inputVal.trim()) {
       if (!chatStarted) setChatStarted(true);
     }
+    setMessages((arr) => {
+      const index = arr.length;
+      return [
+        ...arr,
+        { id: index + "", content: inputVal, role: "user" },
+        {
+          id: index + 1 + "",
+          content: "",
+          role: "assistant",
+        },
+      ];
+    });
+    onRequest();
   };
 
   // 开启新的对话
@@ -230,6 +260,10 @@ export default function AIChat(props: { onClose?: () => void }) {
     setChatStarted(false);
   };
 
+  const stop = () => {
+    cancel();
+  };
+
   React.useEffect(() => {
     return () => {
       // 取消所有进行中的请求
@@ -427,6 +461,10 @@ export default function AIChat(props: { onClose?: () => void }) {
     ] as MenuProps["items"];
   }, [messages, chatId]);
 
+  const handleInputChange = (str: string) => {
+    setInputVal(str);
+  };
+
   return (
     <div className="flex-1 h-full flex flex-col">
       <div className="chat-head w-full h-40px px-10px color-#333 flex items-center justify-between">
@@ -484,17 +522,17 @@ export default function AIChat(props: { onClose?: () => void }) {
             <div className="overflow-y-auto h-full">
               <MessageList messages={messages}></MessageList>
               <div className="flex justify-center items-center h-40px">
-                {isLoading && (
+                {loading && (
                   <Button
                     type="primary"
                     icon={<LoadingOutlined />}
-                    loading={isLoading}
+                    loading={loading}
                   >
                     思考中...
                   </Button>
                 )}
               </div>
-              {error && (
+              {/* {error && (
                 <div>
                   <div className="text-center">请求失败:{error.message}</div>
                   <div className="flex justify-center items-center h-40px">
@@ -503,7 +541,7 @@ export default function AIChat(props: { onClose?: () => void }) {
                     </Button>
                   </div>
                 </div>
-              )}
+              )} */}
             </div>
           )}
         </div>
@@ -523,13 +561,13 @@ export default function AIChat(props: { onClose?: () => void }) {
             variant="borderless"
             onFocus={() => setFocused(true)}
             onBlur={() => setFocused(false)}
-            value={input}
-            onChange={handleInputChange}
-            disabled={isLoading}
+            value={inputVal}
+            onChange={(e) => handleInputChange(e.target.value)}
+            disabled={loading}
             onPressEnter={onSubmit}
           />
           <div className="float-right p-10px">
-            {isLoading ? (
+            {loading ? (
               <Tooltip title="停止生成">
                 <Button
                   type="primary"
@@ -542,7 +580,7 @@ export default function AIChat(props: { onClose?: () => void }) {
               <Button
                 type="primary"
                 icon={<SendOutlined />}
-                disabled={!input.trim()}
+                disabled={!inputVal.trim()}
                 htmlType="submit"
               >
                 发送

+ 20 - 1
apps/designer/src/components/ai/MermaidModal.tsx

@@ -4,6 +4,7 @@ import {
   forwardRef,
   useEffect,
   useRef,
+  useCallback
 } from "react";
 import { Modal, Input, Button } from "antd";
 import "./mermaid.less";
@@ -19,6 +20,8 @@ import Circle from "@/components/flowchart/onPageReference";
 import Diamond from "@/components/flowchart/decision";
 import { isMermaidFlowchart } from "@/utils";
 import mermaid from "mermaid";
+import { useModel } from "umi";
+import { getDefaultDataByTheme } from "@/config/data";
 
 export enum VERTEX_TYPE {
   RECTANGLE = "rectangle", // 矩形
@@ -43,6 +46,12 @@ export default forwardRef(function MermaidModal(
   const [open, setOpen] = useState(false);
   const [mermaidCode, setMermaidCode] = useState("");
   const resultRef = useRef<MermaidResult>();
+  const { pageState } = useModel("appModel");
+  const theme = useRef(pageState.theme);
+
+  useEffect(() => {
+    theme.current = pageState.theme;
+  }, [pageState.theme]);
 
   useImperativeHandle(ref, () => ({
     open: () => {
@@ -63,6 +72,8 @@ export default forwardRef(function MermaidModal(
     const cells: any[] = [];
     let comp = Rectangle;
     const idMap: Record<string, string> = {};
+    const commonData = getDefaultDataByTheme(theme.current || '1');
+    const { nodeDefaultData, edgeDefaultData, ports } = commonData;
     list.forEach((item) => {
       // 节点处理
       if (item.type !== "arrow" && item.type !== "line") {
@@ -93,6 +104,7 @@ export default forwardRef(function MermaidModal(
           }
         }
         const id = uuid();
+        
         const node = {
           ...comp.node,
           id,
@@ -104,11 +116,17 @@ export default forwardRef(function MermaidModal(
           height: parseInt(item.height),
           data: {
             ...comp.node.data,
+            ...nodeDefaultData,
             text: {
-              ...comp.node.data.text,
+              ...(comp.node.data?.text || {}),
+              ...nodeDefaultData.text,
               fontSize: item.label.fontSize,
             },
             label: item.label.text,
+          },
+          ports: {
+            groups: comp.node?.ports?.groups || ports.groups,
+            items: comp.node?.ports?.items || ports.items,
           }
         };
         idMap[item.id] = id;
@@ -117,6 +135,7 @@ export default forwardRef(function MermaidModal(
       if (item.type === "arrow") {
         // 连线处理
         const edge = {
+          ...edgeDefaultData,
           source: { cell: idMap[item.start?.id] },
           target: { cell: idMap[item.end?.id] },
           labels: item?.label

+ 314 - 0
apps/designer/src/hooks/useChat.ts

@@ -0,0 +1,314 @@
+import { useXAgent, XStream } from "@ant-design/x";
+import { useEffect, useRef, useState } from "react";
+import { useSessionStorageState } from "ahooks";
+import { GetSessionList, GetSessionMessageList } from "@/api/ai";
+
+import type { ConversationsProps } from "@ant-design/x";
+import type { ReactNode } from "react";
+
+// 消息格式
+type MessageItem = {
+  id: string;
+  content: string | ReactNode;
+  role: "user" | "assistant" | "system";
+  status: "loading" | "done" | "error" | "stop";
+  loading?: boolean;
+  footer?: ReactNode;
+};
+
+// 后端返回格式
+type ResponseMessageItem = {
+  answer: string;
+  conversation_id: string;
+  created_at: number;
+  event: "message" | "message_end" | "message_error" | "ping";
+  message_id: string;
+  task_id: string;
+};
+
+type ChatParams = {
+  // 应用名称
+  app_name: string;
+  // 会话内容
+  chat_query: string;
+  // 会话名称 第一次
+  chat_name?: string;
+  // 会话id 后续会话带入
+  conversation_id?: string;
+};
+
+type ChatProps = {
+  // 应用名称
+  app_name: string;
+  // 会话id 后续会话带入
+  conversation_id?: string;
+  // 开始流式传输内容
+  onStart?: (data?: ResponseMessageItem) => void;
+  // 成功获取会话内容
+  onSuccess?: (data: ResponseMessageItem) => void;
+  // 更新流式消息内容
+  onUpdate: (data: ResponseMessageItem) => void;
+  // 异常
+  onError?: (error: Error) => void;
+};
+
+const defaultConversation = {
+  // 会话id
+  key: "1",
+  label: "新的对话",
+};
+
+export function useChat({ app_name, onStart, onSuccess, onUpdate, onError }: ChatProps) {
+  /**
+   * 发送消息加载状态
+   */
+  const [loading, setLoading] = useState(false);
+
+  /**
+   * 加载会话记录列表
+   */
+  const [loadingSession, setLoadingSession] = useState(false);
+
+  /**
+   * 加载消息列表
+   */
+  const [loadingMessages, setLoadingMessages] = useState(false);
+
+  // 用于停止对话
+  const abortController = useRef<AbortController | null>(null);
+
+  /**
+   * 消息列表
+   */
+  const [messages, setMessages] = useState<Array<MessageItem>>([]);
+
+  // 会话列表
+  const [conversationList, setConversationList] = useState<
+    ConversationsProps["items"]
+  >([{ ...defaultConversation }]);
+
+  // 活动对话
+  const [activeConversation, setActiveConversation] = useState("1");
+
+  // 当前智能体对象
+  const [currentAgent, setCurrentAgent] = useSessionStorageState("agent-map");
+
+  useEffect(() => {
+    setLoadingSession(true);
+    GetSessionList({
+      app_name,
+      page_index: 1,
+    })
+      .then((res) => {
+        setConversationList([
+          { ...defaultConversation },
+          ...(res?.result?.model || []).map((item: any) => ({
+            ...item,
+            key: item.sessionId,
+            label: item.name,
+          })),
+        ]);
+      })
+      .finally(() => {
+        setLoadingSession(false);
+      });
+  }, [app_name]);
+
+  /**
+   * 切换会话
+   * @param key 会话id
+   * @returns 
+   */
+  const changeConversation = async (key: string) => {
+    setActiveConversation(key);
+    if (key === "1") {
+      setMessages([]);
+      return;
+    }
+    setLoadingMessages(true);
+    // 获取会话内容
+    try {
+      const res = await GetSessionMessageList({
+        app_name,
+        session_id: key,
+        page_index: 1,
+      });
+
+      const list: MessageItem[] = [];
+      (res?.result?.model || []).forEach((item: any) => {
+        list.push(
+          {
+            id: item.id + "_query",
+            content: item.query,
+            role: "user",
+            status: "done",
+          },
+          {
+            id: item.id + "_query",
+            content: item.answer,
+            role: "assistant",
+            status: "done",
+          }
+        );
+      });
+      setMessages(list);
+    } finally {
+      setLoadingMessages(false);
+    }
+  };
+
+  /**
+   * 封装智能体
+   */
+  const [agent] = useXAgent<ResponseMessageItem>({
+    request: async (message, { onError, onSuccess, onUpdate }) => {
+      abortController.current = new AbortController();
+      const signal = abortController.current.signal;
+      try {
+        setLoading(true);
+        const response = await fetch(
+          "https://design.shalu.com/api/ai/chat-message",
+          {
+            method: "POST",
+            body: JSON.stringify(message),
+            headers: {
+              Authorization: localStorage.getItem("token_a") || "",
+              "Content-Type": "application/json",
+            },
+            signal,
+          }
+        );
+
+        // 判断当前是否流式返回
+        if(response.headers.get('content-type')?.includes('text/event-stream')) {
+          if (response.body) {
+            for await (const chunk of XStream({
+              readableStream: response.body,
+            })) {
+              const data = JSON.parse(chunk.data);
+              if (data?.event === "message") {
+                onUpdate(data);
+              }
+              if (data?.event === "message_end") {
+                onSuccess(data);
+              }
+              if (data?.event === "message_error") {
+                onError(data);
+              }
+              if (data?.event === "ping") {
+                console.log(">>>> stream start <<<<");
+                onStart?.(data);
+              }
+            }
+          }
+        } else {
+          // 接口异常处理
+          response.json().then(res => {
+            if(res.code === 0 ) {
+              onError?.(Error(res?.error || '请求失败'));
+              cancel();
+            }
+          });
+        }
+      } catch (error) {
+        // 判断是不是 abort 错误
+        if (signal.aborted) {
+          return;
+        }
+        onError(error as Error);
+      } finally {
+        setLoading(false);
+      }
+    },
+  });
+
+  /**
+   * 发起请求
+   * @param chat_query 对话内容
+   */
+  const onRequest = (chat_query: string) => {
+    setConversationList((list) => {
+      return list?.map((item) => {
+        return {
+          ...item,
+          label: item.key === "1" ? chat_query : item.label,
+        };
+      });
+    });
+    agent.request(
+      {
+        app_name,
+        chat_query,
+        chat_name: activeConversation === "1" ? chat_query : undefined,
+        conversation_id:
+          activeConversation === "1" ? undefined : activeConversation,
+      },
+      {
+        onSuccess: (data) => {
+          onSuccess?.(data);
+        },
+        onUpdate: (data) => {
+          onUpdate(data);
+          // 更新会话相关信息
+          if (activeConversation === "1") {
+            setConversationList((list) => {
+              return list?.map((item) => {
+                return {
+                  ...item,
+                  // 更新当前会话id
+                  key: item.key === "1" ? data.conversation_id : item.key,
+                };
+              });
+            });
+            setActiveConversation(data.conversation_id);
+          }
+        },
+        onError: (error) => {
+          console.log("error", error);
+          onError?.(error);
+        },
+      }
+    );
+  };
+
+  /**
+   * 停止对话
+   */
+  const cancel = () => {
+    abortController.current?.abort();
+  };
+
+  /**
+   * 新增会话
+   */
+  const addConversation = () => {
+    setMessages([]);
+    setActiveConversation("1");
+    // 还没产生对话时 直接清除当前对话
+    if (!conversationList?.find((item) => item.key === "1")) {
+      setConversationList([
+        {
+          ...defaultConversation,
+        },
+        ...(conversationList || []),
+      ]);
+    }
+  };
+
+  return {
+    agent,
+    loading,
+    loadingMessages,
+    loadingSession,
+    cancel,
+    messages,
+    setMessages,
+    conversationList,
+    setConversationList,
+    activeConversation,
+    setActiveConversation,
+    onRequest,
+    addConversation,
+    changeConversation,
+  };
+}

+ 78 - 0
apps/designer/src/models/aiModel.ts

@@ -0,0 +1,78 @@
+import { useXAgent, XStream } from "@ant-design/x";
+import { useRef, useState } from "react";
+
+type MessageItem = {
+  answer: string;
+  conversation_id: string;
+  created_at: number;
+  event: "message" | "message_end" | "message_error" | "ping";
+  message_id: string;
+  task_id: string;
+};
+
+export default function aiModel() {
+  const [loading, setLoading] = useState(false);
+  const abortController = useRef<AbortController | null>(null);
+
+  // 封装智能体
+  const [agent] = useXAgent<MessageItem>({
+    request: async (message, { onError, onSuccess, onUpdate }) => {
+      abortController.current = new AbortController();
+      const signal = abortController.current.signal;
+      try {
+        setLoading(true);
+        const response = await fetch(
+          "https://design.shalu.com/api/ai/chat-message",
+          {
+            method: "POST",
+            body: JSON.stringify(message),
+            headers: {
+              Authorization: localStorage.getItem("token_a") || "",
+              "Content-Type": "application/json",
+            },
+            signal
+          }
+        );
+
+        if (response.body) {
+          for await (const chunk of XStream({
+            readableStream: response.body,
+          })) {
+            const data = JSON.parse(chunk.data);
+            if (data?.event === "message") {
+              onUpdate(data);
+            }
+            if (data?.event === "message_end") {
+              onSuccess(data);
+            }
+            if (data?.event === "message_error") {
+              onError(data);
+            }
+            if (data?.event === "ping") {
+              console.log("start");
+            }
+          }
+        }
+      } catch (error) {
+        if(signal.aborted) {
+          return;
+        }
+        onError(error as Error);
+      } finally {
+        setLoading(false);
+      }
+    },
+  });
+
+  // 停止对话
+  const cancel = () => {
+    abortController.current?.abort();
+  };
+
+  return {
+    agent,
+    loading,
+    setLoading,
+    cancel,
+  };
+}

+ 47 - 5
apps/designer/src/models/flowchartModel.ts

@@ -15,7 +15,7 @@ import { pageMenu, nodeMenu } from "@/utils/contentMenu";
 import { bindKeys } from "@/utils/fastKey";
 import { handleSetEdgeStyle } from "@/utils";
 import { getDefaultDataByTheme } from "@/config/data";
-import { getFlowNodeMetaByBasic } from "@/utils/flow";
+import { getFlowNodeMetaByBasic, getFlowNodeMetaByAi } from "@/utils/flow";
 export default function flowchartModel() {
   const [graph, setGraph] = useState<Graph>();
   const [dnd, setDnd] = useState<Dnd>();
@@ -70,8 +70,10 @@ export default function flowchartModel() {
     if (graphRef.current) {
       const defaultDataByTheme = getDefaultDataByTheme(theme);
       // 添加节点,可能节点数据不完整,需要合并默认数据
-      const list = (cells || []).map((item) => getFlowNodeMetaByBasic(item, defaultDataByTheme));
-      console.log(list);
+      const list = (cells || []).map((item) =>
+        getFlowNodeMetaByBasic(item, defaultDataByTheme)
+      );
+      console.log('=====init cells:=======\n');
       graphRef.current.fromJSON({ cells: list as any });
       addPageNode();
       handleGraphApiEvent(graphRef.current);
@@ -197,8 +199,11 @@ export default function flowchartModel() {
   };
 
   /**组件库拖拽生成 */
-  const startDrag: any = useCallback(
-    (e: React.MouseEvent<HTMLDivElement, MouseEvent>, node: Node.Metadata) => {
+  const startDrag: (
+    e: React.MouseEvent<HTMLDivElement, MouseEvent>,
+    node: Node.Metadata
+  ) => void = useCallback(
+    (e, node) => {
       if (!dndRef.current || !graphRef.current) return;
       // 查找对应主题的默认数据
       const defaultDataByTheme = getDefaultDataByTheme(pageState.theme || "1");
@@ -234,6 +239,42 @@ export default function flowchartModel() {
     graphRef.current?.redo();
   };
 
+  // 添加AI的生成节点
+  const handleAddNodeByAi: (data: any) => void = useCallback(
+    (data) => {
+      if(!graphRef.current || !data) return;
+
+      // 查找对应主题的默认数据
+      const defaultDataByTheme = getDefaultDataByTheme(pageState.theme || "1");
+      // 数组
+      if(Array.isArray(data)) {
+        const list = (data || []).map((item) =>
+          getFlowNodeMetaByAi(item, defaultDataByTheme)
+        );
+        console.log('=====ai creator cells:=======\n', list);
+        list.forEach((item) => {
+          if(item.shape !== 'edge') {
+            graphRef.current?.addNode(item);
+          }
+          if(item.shape === 'edge') {
+            graphRef.current?.addEdge(item);
+          }
+        });
+      }
+      // 对象
+      if(data instanceof Object) {
+        const cell = getFlowNodeMetaByAi(data, defaultDataByTheme);
+        if(cell.shape !== 'edge') {
+          graphRef.current?.addNode(cell);
+        }
+        if(cell.shape === 'edge') {
+          graphRef.current?.addEdge(cell);
+        }
+      }
+    },
+    [pageState.theme, graphRef.current]
+  );
+
   return {
     graph,
     dnd,
@@ -248,5 +289,6 @@ export default function flowchartModel() {
     initCells,
     updateKey,
     setUpdateKey,
+    handleAddNodeByAi,
   };
 }

+ 292 - 0
apps/designer/src/pages/flow/components/Config/AiCreator.tsx

@@ -0,0 +1,292 @@
+import React, { useEffect, useMemo, useRef, useState } from "react";
+import {
+  CloseOutlined,
+  FieldTimeOutlined,
+  SendOutlined,
+  EditOutlined,
+  DeleteOutlined,
+  CaretDownOutlined,
+} from "@ant-design/icons";
+import { Button, Tooltip, Input, Form, Dropdown, message } from "antd";
+import type { DropDownProps } from "antd";
+import { useChat } from "@/hooks/useChat";
+
+
+interface ChatHistoryItem {
+  id: string;
+  messages: any[];
+  createdAt: number;
+  updatedAt: number;
+  title: string;
+}
+
+const items = [
+  { key: "1", label: "流程图" },
+  { key: "2", label: "泳道图" },
+  { key: "3", label: "ER实体图" },
+  { key: "4", label: "组织图" },
+  { key: "5", label: "时序图" },
+];
+export default function AICreator(props: { 
+  type: 'mindmap' | 'flow',
+  onClose?: () => void,
+  onChange?: (data: any) => void,
+  onError?: (err: Error) => void,
+}) {
+  const [focused, setFocused] = React.useState(false);
+  const [input, setInput] = useState("");
+  const [messageApi, contextHolder] = message.useMessage();
+  const messageKey = 'ailoading';
+  const createContent = useRef<string>('');
+
+  const {
+    loading,
+    onRequest
+  } = useChat({
+    app_name: "system_design",
+    onStart: () => {
+      messageApi.open({
+        key: messageKey,
+        type: 'loading',
+        content: "AI创作中...",
+        duration: 0,
+        style: {
+          marginTop: 300
+        }
+      })
+    },
+    onUpdate: (msg) => {
+      createContent.current += msg.answer;
+    },
+    onSuccess: (msg) => {
+      console.log('加载完毕!', createContent.current);
+      messageApi.open({
+        key: messageKey,
+        type: 'success',
+        content: "AI创作完成",
+        duration: 2,
+        style: {
+          marginTop: 300
+        }
+      });
+      handleParse();
+    },
+  });
+
+  const handleParse = () => {
+    try {
+      const json = JSON.parse(createContent.current);
+      console.log('解析结果:', json);
+      props.onChange?.(json);
+    } catch (error) {
+      messageApi.open({
+        key: messageKey,
+        type: 'error',
+        content: "AI创作失败",
+        duration: 2,
+        style: {
+          marginTop: 300
+        }
+      });
+      console.error(error);
+      props.onError?.(new Error('AI创作失败'));
+    }
+  };
+
+  const [graphType, setGraphType] = useState<string>('流程图');
+  const dropDownMenu: DropDownProps['menu'] = {
+    items,
+    onClick: (info) => {
+      console.log(info);
+      const type = items.find(item => item.key === info.key);
+      setGraphType(type?.label || '流程图');
+    },
+  }
+
+  // 处理提交
+  const onSubmit = () => {
+    if (input.trim()) {
+      onRequest(`设计一个${graphType}, 返回图形json数据, 具体需求描述:${input}`);
+      setInput("");
+    }
+  };
+
+  React.useEffect(() => {
+    return () => {
+      // 取消所有进行中的请求
+      const controller = new AbortController();
+      controller.abort();
+    };
+  }, []);
+
+  const [hoverId, setHoverId] = useState<string | null>(null);
+
+    // 获取items
+    const getItems = (list: ChatHistoryItem[]) => {
+      return (list || []).map((item) => {
+        return {
+          key: item.id,
+          label: (
+            <div className="w-180px relative">
+              <div className="w-full flex">
+                <span
+                  className="truncate"
+                  style={{
+                    overflow: "hidden",
+                    whiteSpace: "nowrap",
+                    textOverflow: "ellipsis",
+                  }}
+                >
+                  <Tooltip title={item.title}>{item.title}</Tooltip>
+                </span>
+              </div>
+              {/* TODO: 添加编辑删除按钮 */}
+              {hoverId === item.id && (
+                <div className="h-full w-50px text-right absolute right-0 top-0 bg-#fff">
+                  <EditOutlined
+                    onClick={(e) => {
+                      e.stopPropagation();
+                    }}
+                  />
+                  <DeleteOutlined />
+                </div>
+              )}
+            </div>
+          ),
+          onClick: () => {
+            
+          },
+          onMouseOver: () => {
+            
+          },
+        };
+      });
+    };
+
+  const handleList = [
+    {
+      key: "1",
+      label: "风格美化",
+      icon: "icon-yijianmeihua",
+      color: "#a171f2",
+    },
+    {
+      key: "2",
+      label: "语法修复",
+      icon: "icon-tubiao_yufajiucuo",
+      color: "#00c4ad",
+    },
+    {
+      key: "3",
+      label: "翻译为英文",
+      icon: "icon-fanyiweiyingwen",
+      color: "#8c4ff0",
+    },
+    {
+      key: "4",
+      label: "翻译为中文",
+      icon: "icon-fanyiweizhongwen",
+      color: "#3d72fb",
+    },
+  ];
+
+  return (
+    <div className="flex-1 h-full">
+      {contextHolder}
+      <div className="chat-head w-full h-40px px-10px color-#333 flex items-center justify-between">
+        <i className="iconfont icon-AIchuangzuo"></i>
+        <span>
+          {/* <Dropdown
+            menu={{  }}
+            trigger={["click"]}
+            placement="bottomLeft"
+            arrow
+          >
+            <Tooltip title="历史记录">
+              <Button
+                type="text"
+                size="small"
+                icon={<FieldTimeOutlined />}
+              ></Button>
+            </Tooltip>
+          </Dropdown> */}
+          <Button
+            type="text"
+            size="small"
+            icon={<CloseOutlined />}
+            onClick={() => props.onClose?.()}
+          ></Button>
+        </span>
+      </div>
+
+      <div className="text-14px pl-12px text-#333">绘制图形</div>
+      <div
+        className="chat-content bg-#f5f5f5 px-10px overflow-y-auto mt-12px"
+      >
+        <div
+          style={{
+            borderColor: focused ? "#1890ff" : "#ddd",
+          }}
+          className="chat-foot bg-#fff rounded-10px border border-solid border-1px shadow-sm"
+        >
+          <Dropdown menu={dropDownMenu} placement="bottomLeft">
+            <div className="text-12px pl-10px pt-10px cursor-pointer">
+              帮我绘制-{graphType}
+              <CaretDownOutlined />
+            </div>
+          </Dropdown>
+          <Form onFinish={onSubmit}>
+            <Input.TextArea
+              rows={3}
+              autoSize={{ maxRows: 3, minRows: 3 }}
+              placeholder="你可以这样问:用户登陆流程图"
+              variant="borderless"
+              onFocus={() => setFocused(true)}
+              onBlur={() => setFocused(false)}
+              value={input}
+              onChange={(e) => setInput(e.target.value)}
+              disabled={loading}
+              onPressEnter={onSubmit}
+            />
+            <div className="text-right p-10px">
+              {loading ? (
+                <Tooltip title="停止生成">
+                  <Button
+                    type="primary"
+                    shape="circle"
+                    icon={<i className="iconfont icon-stopcircle" />}
+                    onClick={stop}
+                  ></Button>
+                </Tooltip>
+              ) : (
+                <Button
+                  type="text"
+                  icon={<SendOutlined />}
+                  disabled={!input.trim()}
+                  htmlType="submit"
+                ></Button>
+              )}
+            </div>
+          </Form>
+        </div>
+      </div>
+
+      <div className="text-14px pl-12px text-#333 mt-32px">图形处理</div>
+      <div className="flex flex-wrap gap-10px p-10px">
+        {handleList.map((item) => (
+          <div
+            key={item.key}
+            className="flex-[40%] h-50px bg-#fff rounded-10px shadow-sm flex items-center pl-10px text-12px cursor-pointer"
+            style={{}}
+          >
+            <i
+              className={`iconfont ${item.icon} text-16px`}
+              style={{ color: item.color }}
+            ></i>
+            <span className="ml-10px">{item.label}</span>
+          </div>
+        ))}
+      </div>
+    </div>
+  );
+}

+ 4 - 4
apps/designer/src/pages/flow/components/Config/index.tsx

@@ -6,7 +6,7 @@ import NodeAttrs from "@/components/NodeAttrs";
 import { useModel } from "umi";
 import InsetCss from "insert-css";
 import AIChat from "@/components/ai/AIChat";
-import AICreator from "@/components/ai/AiCreator";
+import AICreator from "./AiCreator";
 import { useDragResize } from "@/hooks/useDragResize";
 
 InsetCss(`
@@ -21,13 +21,13 @@ InsetCss(`
   }
   `);
 export default function Config() {
-  const { selectedCell } = useModel("flowchartModel");
+  const { selectedCell, handleAddNodeByAi } = useModel("flowchartModel");
   const {
     rightPanelTabActiveKey,
     setRightPanelTabActiveKey,
     activeAI,
     setActiveAI,
-    setFlowRightPanelWidth
+    setFlowRightPanelWidth,
   } = useModel("appModel");
 
   const { dragging, handleMouseDown } = useDragResize(setFlowRightPanelWidth);
@@ -106,7 +106,7 @@ export default function Config() {
           <AIChat onClose={() => setActiveAI(undefined)} />
         )}
         {activeAI === "creator" && (
-          <AICreator type="flow" onClose={() => setActiveAI(undefined)} />
+          <AICreator type="flow" onClose={() => setActiveAI(undefined)} onChange={handleAddNodeByAi}/>
         )}
         {activeAI === undefined && (
           <Tabs

+ 8 - 7
apps/designer/src/pages/flow/components/ToolBar/index.tsx

@@ -33,7 +33,7 @@ import FindReplaceModal from "@/components/FindReplaceModal";
 import { useFindReplace } from "@/hooks/useFindReplace";
 import MermaidModal, { MermaidResult } from "@/components/ai/MermaidModal";
 import { nodeMenu, edgeMenu } from "@/utils/contentMenu";
-import BaseNode from "@/components/base";
+import ImageNode from "@/components/ImageNode";
 import { Cell } from "@antv/x6";
 
 export default function ToolBar() {
@@ -169,11 +169,11 @@ export default function ToolBar() {
     if (res) {
       if (res.type === "image") {
         const node = graph?.addNode({
-          ...BaseNode,
+          ...ImageNode,
           data: {
-            ...BaseNode.data,
+            ...ImageNode.data,
             fill: {
-              ...BaseNode.data.fill,
+              ...ImageNode.data.fill,
               fillType: "image",
               imageUrl: res.data,
             },
@@ -775,7 +775,7 @@ export default function ToolBar() {
         <MermaidModal ref={mermaidModelRef} onChange={handleInsertMermaid} />
         <div>
           <Tooltip placement="bottom" title="AI助手">
-            <Dropdown
+            {/* <Dropdown
               menu={{
                 items: [
                   {
@@ -804,14 +804,15 @@ export default function ToolBar() {
                   },
                 ],
               }}
-            >
+            > */}
               <Button
                 type="text"
                 icon={<i className="iconfont icon-AI" />}
                 className={activeAI ? "active" : ""}
                 style={{ marginRight: 16 }}
+                onClick={() => setActiveAI("creator")}
               />
-            </Dropdown>
+            {/* </Dropdown> */}
           </Tooltip>
           <Tooltip placement="bottom" title="替换">
             <Button

+ 31 - 223
apps/designer/src/components/ai/AiCreator.tsx

@@ -3,152 +3,70 @@ import {
   CloseOutlined,
   FieldTimeOutlined,
   SendOutlined,
-  LoadingOutlined,
   EditOutlined,
   DeleteOutlined,
   CaretDownOutlined,
 } from "@ant-design/icons";
 import { Button, Tooltip, Input, Form, Dropdown, MenuProps } from "antd";
 import type { DropDownProps } from "antd";
-import { useChat, Message } from "ai/react";
-import { uuid } from "@repo/utils";
-import { useLocalStorageState } from "ahooks";
+import { useChat } from "@/hooks/useChat";
+
 
 interface ChatHistoryItem {
   id: string;
-  messages: Message[];
+  messages: any[];
   createdAt: number;
   updatedAt: number;
   title: string;
 }
 
-interface DateGroups {
-  today: ChatHistoryItem[];
-  yesterday: ChatHistoryItem[];
-  last7Days: ChatHistoryItem[];
-  last30Days: ChatHistoryItem[];
-  older: ChatHistoryItem[];
-}
-
-// 对历史记录进行分组
-function groupDataByDate(data: ChatHistoryItem[]): DateGroups {
-  const normalizeDate = (date: Date | string): Date => {
-    const d = new Date(date);
-    d.setHours(0, 0, 0, 0);
-    return d;
-  };
-
-  const now = normalizeDate(new Date()); // 当前日期归一化
-
-  const groups: DateGroups = {
-    today: [],
-    yesterday: [],
-    last7Days: [],
-    last30Days: [],
-    older: [],
-  };
-
-  data.forEach((item: ChatHistoryItem) => {
-    const itemDate = normalizeDate(new Date(item.updatedAt));
-    const diffTime = now.getTime() - itemDate.getTime();
-    const diffDays = Math.floor(diffTime / (1000 * 3600 * 24));
-
-    if (diffDays === 0) {
-      groups.today.push(item);
-    } else if (diffDays === 1) {
-      groups.yesterday.push(item);
-    } else if (diffDays <= 6) {
-      groups.last7Days.push(item);
-    } else if (diffDays <= 29) {
-      groups.last30Days.push(item);
-    } else {
-      groups.older.push(item);
-    }
-  });
-
-  return groups;
-}
-
 const dropDownMenu: DropDownProps['menu'] = {
   items: [
     { key: "1", label: "流程图" },
     { key: "2", label: "泳道图" },
     { key: "3", label: "ER实体图" },
-    { key: "4", label: "结构图" },
+    { key: "4", label: "组织图" },
+    { key: "5", label: "时序图" },
   ]
 }
 
 export default function AICreator(props: { 
   type: 'mindmap' | 'flow',
-  onClose?: () => void 
+  onClose?: () => void,
+  onChange?: (data: any) => void,
 }) {
   const [focused, setFocused] = React.useState(false);
   const [chatStarted, setChatStarted] = React.useState(false);
   const scrollAreaRef = React.useRef<HTMLDivElement>(null);
-  // 对话历史
-  const [history, setHistory] = useLocalStorageState<ChatHistoryItem[]>(
-    "chat-history",
-    { defaultValue: [] }
-  );
-
-  const [chatId, setChatId] = React.useState(uuid());
+  const [input, setInput] = useState("");
 
   const {
     messages,
-    input,
-    handleInputChange,
-    handleSubmit,
-    isLoading,
-    stop,
-    error,
-    reload,
     setMessages,
+    loading,
+    onRequest
   } = useChat({
-    api: "http://localhost:3000/ai/chat",
-    keepLastMessageOnError: true,
-    id: chatId,
-  });
+    app_name: "app1",
+    onStart: () => {
 
-  useEffect(() => {
-    // 判断messages是否存在消息
-    if (!messages.length) {
-      return;
-    }
-    // 再判断历史记录是否存在该聊天,存在则更新记录
-    if (history?.find((item) => item.id === chatId)) {
-      setHistory(
-        history?.map((item) => {
-          if (item.id === chatId) {
-            return {
-              ...item,
-              messages,
-              updatedAt: Date.now(),
-            };
-          }
-          return item;
-        })
-      );
-    } else {
-      setHistory((history) => {
-        const title =
-          messages.find((message) => message.role === "user")?.content ||
-          "新对话";
-        const newData = {
-          id: chatId,
-          messages,
-          createdAt: Date.now(),
-          updatedAt: Date.now(),
-          title,
-        };
-        return history ? [newData, ...history] : [newData];
+    },
+    onUpdate: (msg) => {
+      setMessages((messages) => {
+        const arr = [...messages];
+        arr[messages.length - 1].content += msg.answer;
+        arr[messages.length - 1].id = msg.message_id;
+        return arr;
       });
-    }
-  }, [messages, chatId]);
+    },
+    onSuccess: (msg) => {
+      console.log('加载完毕!');
+    },
+  })
 
   // 处理提交
   const onSubmit = () => {
     if (input.trim()) {
-      handleSubmit();
+
       if (!chatStarted) setChatStarted(true);
     }
   };
@@ -163,25 +81,6 @@ export default function AICreator(props: {
 
   const [hoverId, setHoverId] = useState<string | null>(null);
 
-  const items = useMemo(() => {
-    const hasCurrentChat = history?.find((item) => item.id === chatId);
-    const groups = groupDataByDate(
-      hasCurrentChat
-        ? history || []
-        : [
-            {
-              id: chatId,
-              messages,
-              createdAt: Date.now(),
-              updatedAt: Date.now(),
-              title:
-                messages.find((message) => message.role === "user")?.content ||
-                "新对话",
-            },
-            ...(history || []),
-          ]
-    );
-
     // 获取items
     const getItems = (list: ChatHistoryItem[]) => {
       return (list || []).map((item) => {
@@ -200,11 +99,6 @@ export default function AICreator(props: {
                 >
                   <Tooltip title={item.title}>{item.title}</Tooltip>
                 </span>
-                {item.id === chatId ? (
-                  <span className="text-12px color-#999 flex-shrink-0">
-                    (当前)
-                  </span>
-                ) : null}
               </div>
               {/* TODO: 添加编辑删除按钮 */}
               {hoverId === item.id && (
@@ -220,101 +114,15 @@ export default function AICreator(props: {
             </div>
           ),
           onClick: () => {
-            if (item.id === chatId) return;
-
-            setChatId(item.id);
+            
           },
           onMouseOver: () => {
-            setHoverId(item.id);
+            
           },
         };
       });
     };
 
-    const today = groups.today.length
-      ? [
-          {
-            key: "today",
-            label: "今天",
-            disabled: true,
-          },
-          {
-            key: "today-divider",
-            type: "divider",
-          },
-          ...getItems(groups.today),
-        ]
-      : [];
-
-    const yesterday = groups.yesterday.length
-      ? [
-          {
-            key: "yesterday",
-            label: "昨天",
-            disabled: true,
-          },
-          {
-            key: "yesterday-divider",
-            type: "divider",
-          },
-          ...getItems(groups.yesterday),
-        ]
-      : [];
-
-    const last7Days = groups.last7Days.length
-      ? [
-          {
-            key: "last7Days",
-            label: "最近7天",
-            disabled: true,
-          },
-          {
-            key: "last7Days-divider",
-            type: "divider",
-          },
-          ...getItems(groups.last7Days),
-        ]
-      : [];
-
-    const last30Days = groups.last30Days.length
-      ? [
-          {
-            key: "last30Days",
-            label: "最近30天",
-            disabled: true,
-          },
-          {
-            key: "last30Days-divider",
-            type: "divider",
-          },
-          ...getItems(groups.last30Days),
-        ]
-      : [];
-
-    const older = groups.older.length
-      ? [
-          {
-            key: "older",
-            label: "更早",
-            disabled: true,
-          },
-          {
-            key: "older-divider",
-            type: "divider",
-          },
-          ...getItems(groups.older),
-        ]
-      : [];
-
-    return [
-      ...today,
-      ...yesterday,
-      ...last7Days,
-      ...last30Days,
-      ...older,
-    ] as MenuProps["items"];
-  }, [messages, chatId]);
-
   const handleList = [
     {
       key: "1",
@@ -348,7 +156,7 @@ export default function AICreator(props: {
         <i className="iconfont icon-AIchuangzuo"></i>
         <span>
           <Dropdown
-            menu={{ items }}
+            menu={{  }}
             trigger={["click"]}
             placement="bottomLeft"
             arrow
@@ -396,12 +204,12 @@ export default function AICreator(props: {
               onFocus={() => setFocused(true)}
               onBlur={() => setFocused(false)}
               value={input}
-              onChange={handleInputChange}
-              disabled={isLoading}
+              onChange={(e) => setInput(e.target.value)}
+              disabled={loading}
               onPressEnter={onSubmit}
             />
             <div className="text-right p-10px">
-              {isLoading ? (
+              {loading ? (
                 <Tooltip title="停止生成">
                   <Button
                     type="primary"

+ 1 - 1
apps/designer/src/pages/mindmap/components/Config/index.tsx

@@ -13,7 +13,7 @@ import IconConfig from "./IconConfig";
 import Remark from "./Remark";
 import NodeAttrs from "@/components/NodeAttrs";
 import AIChat from "@/components/ai/AIChat";
-import AICreator from "@/components/ai/AiCreator";
+import AICreator from "./AiCreator";
 
 InsertCss(`
   .shalu-tabs-nav {

+ 51 - 0
apps/designer/src/utils/flow.ts

@@ -2,6 +2,10 @@ import { Cell } from "@antv/x6";
 import { merge, cloneDeep } from "lodash-es";
 import { getCompMap } from "@/components/flowchartNode";
 import { nodeMenu, edgeMenu } from "@/utils/contentMenu";
+
+/**
+ * 根据元素基本信息获取完整元素信息
+ */
 export const getFlowNodeMetaByBasic = (
   item: Cell.Properties,
   defaultDataByTheme: any
@@ -60,3 +64,50 @@ export const getFlowNodeMetaByBasic = (
     return merge(cloneDeep(defaultDataByTheme.edgeDefaultData), newEdge);
   }
 };
+
+/**
+ * 根据ai生成的数据获取完整元素信息
+ */
+export const getFlowNodeMetaByAi = (
+  item: Cell.Properties,
+  defaultDataByTheme: any
+) => {
+  if (item.shape !== "edge") {
+    const nodeMeta = getCompMap()[item.shape as string] || {};
+    // ports 先取自身 再判断节点本身是否带有 最后取通用的ports
+    const ports: { items: any[]; groups: any[] } = nodeMeta?.ports || defaultDataByTheme.ports;
+    const newNode = {
+      ...item,
+      tools: [
+        {
+          name: "contextmenu",
+          args: {
+            menu: nodeMenu,
+          },
+        },
+      ],
+      ports,
+    };
+    return merge(
+      nodeMeta,
+      {
+        data: defaultDataByTheme.nodeDefaultData,
+        ports: defaultDataByTheme.ports,
+      },
+      newNode
+    );
+  } else {
+    const newEdge = {
+      ...item,
+      tools: [
+        {
+          name: "contextmenu",
+          args: {
+            menu: edgeMenu,
+          },
+        },
+      ],
+    };
+    return merge(cloneDeep(defaultDataByTheme.edgeDefaultData), newEdge);
+  }
+};

+ 0 - 1
apps/er-designer/src/type.d.ts

@@ -58,7 +58,6 @@ export interface ViewTable {
   schemaName: string;
   type: number;
   updateTime: string;
-  langName: string;
   langNameList?: Record<string, string>[];
   langDescriptionList?: Record<string, string>[];
   cnName?: string;

+ 1 - 0
package.json

@@ -20,6 +20,7 @@
   "dependencies": {
     "@ant-design/icons": "^5.6.1",
     "@ant-design/pro-components": "^2.8.6",
+    "@ant-design/x": "^1.0.5",
     "@antv/hierarchy": "^0.6.14",
     "@antv/x6": "^2.18.1",
     "@antv/x6-plugin-clipboard": "^2.1.6",

+ 50 - 202
pnpm-lock.yaml

@@ -14,6 +14,9 @@ importers:
       '@ant-design/pro-components':
         specifier: ^2.8.6
         version: 2.8.6(antd@5.24.1)(rc-field-form@2.7.0)(react-dom@18.3.1)(react@18.3.1)
+      '@ant-design/x':
+        specifier: ^1.0.5
+        version: 1.0.5(antd@5.24.1)(react-dom@18.3.1)(react@18.3.1)
       '@antv/hierarchy':
         specifier: ^0.6.14
         version: 0.6.14
@@ -157,7 +160,7 @@ importers:
         version: 4.4.6(react-dom@18.3.1)(react@18.3.1)
       react-markdown:
         specifier: ^9.0.3
-        version: 9.0.3(@types/react@19.0.10)(react@18.3.1)
+        version: 9.0.3(@types/react@18.3.18)(react@18.3.1)
       react-syntax-highlighter:
         specifier: ^15.6.1
         version: 15.6.1(react@18.3.1)
@@ -172,7 +175,7 @@ importers:
         version: 2.0.1(@codemirror/language@6.10.8)(@codemirror/state@6.5.2)(@codemirror/view@6.36.3)
       umi:
         specifier: ^4.4.5
-        version: 4.4.5(@babel/core@7.26.9)(@types/react@19.0.10)(eslint@8.57.1)(prettier@3.5.1)(react-dom@18.3.1)(react@18.3.1)(stylelint@14.16.1)(typescript@5.7.3)(webpack@5.98.0)
+        version: 4.4.5(@babel/core@7.26.9)(@types/react@18.3.18)(eslint@8.57.1)(prettier@3.5.1)(react-dom@18.3.1)(react@18.3.1)(stylelint@14.16.1)(typescript@5.7.3)(webpack@5.98.0)
       unocss:
         specifier: ^0.62.4
         version: 0.62.4(postcss@8.5.3)(vite@5.4.14)
@@ -182,7 +185,7 @@ importers:
     devDependencies:
       '@umijs/plugins':
         specifier: ^4.4.5
-        version: 4.4.5(@babel/core@7.26.9)(@types/react@19.0.10)(antd@5.24.1)(dva@2.5.0-beta.2)(rc-field-form@2.7.0)(react-dom@18.3.1)(react@18.3.1)
+        version: 4.4.5(@babel/core@7.26.9)(@types/react@18.3.18)(antd@5.24.1)(dva@2.5.0-beta.2)(rc-field-form@2.7.0)(react-dom@18.3.1)(react@18.3.1)
       prettier:
         specifier: ^3.5.1
         version: 3.5.1
@@ -800,6 +803,27 @@ packages:
       resize-observer-polyfill: 1.5.1
       throttle-debounce: 5.0.2
 
+  /@ant-design/x@1.0.5(antd@5.24.1)(react-dom@18.3.1)(react@18.3.1):
+    resolution: {integrity: sha512-St+JqVa6VNDTDjsn79O+GN6hG2rxZ1Skksjbfzn9JFkHNXXBQ+7SK+Vh2lDaZi48IzLPhaiBzFu+D8/4mgw2Ww==}
+    peerDependencies:
+      antd: '>=5.0.0'
+      react: '>=18.0.0'
+      react-dom: '>=18.0.0'
+    dependencies:
+      '@ant-design/colors': 7.2.0
+      '@ant-design/cssinjs': 1.23.0(react-dom@18.3.1)(react@18.3.1)
+      '@ant-design/cssinjs-utils': 1.1.3(react-dom@18.3.1)(react@18.3.1)
+      '@ant-design/fast-color': 2.0.6
+      '@ant-design/icons': 5.6.1(react-dom@18.3.1)(react@18.3.1)
+      '@babel/runtime': 7.26.9
+      antd: 5.24.1(react-dom@18.3.1)(react@18.3.1)
+      classnames: 2.5.1
+      rc-motion: 2.9.5(react-dom@18.3.1)(react@18.3.1)
+      rc-util: 5.44.4(react-dom@18.3.1)(react@18.3.1)
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+    dev: false
+
   /@antfu/install-pkg@0.1.1:
     resolution: {integrity: sha512-LyB/8+bSfa0DFGC06zpCEfs89/XoWZwws5ygEa5D+Xsm3OfI+aXQ86VgVG7Acyef+rSZ5HE7J8rrxzrQeM3PjQ==}
     dependencies:
@@ -3039,20 +3063,6 @@ packages:
       - react-dom
     dev: false
 
-  /@floating-ui/react-dom-interactions@0.3.1(@types/react@19.0.10)(react-dom@18.3.1)(react@18.3.1):
-    resolution: {integrity: sha512-tP2KEh7EHJr5hokSBHcPGojb+AorDNUf0NYfZGg/M+FsMvCOOsSEeEF0O1NDfETIzDnpbHnCs0DuvCFhSMSStg==}
-    deprecated: Package renamed to @floating-ui/react
-    dependencies:
-      '@floating-ui/react-dom': 0.6.3(@types/react@19.0.10)(react-dom@18.3.1)(react@18.3.1)
-      aria-hidden: 1.2.4
-      point-in-polygon: 1.1.0
-      use-isomorphic-layout-effect: 1.2.0(@types/react@19.0.10)(react@18.3.1)
-    transitivePeerDependencies:
-      - '@types/react'
-      - react
-      - react-dom
-    dev: false
-
   /@floating-ui/react-dom@0.6.3(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1):
     resolution: {integrity: sha512-hC+pS5D6AgS2wWjbmSQ6UR6Kpy+drvWGJIri6e1EDGADTPsCaa4KzCgmCczHrQeInx9tqs81EyDmbKJYY2swKg==}
     peerDependencies:
@@ -3067,20 +3077,6 @@ packages:
       - '@types/react'
     dev: false
 
-  /@floating-ui/react-dom@0.6.3(@types/react@19.0.10)(react-dom@18.3.1)(react@18.3.1):
-    resolution: {integrity: sha512-hC+pS5D6AgS2wWjbmSQ6UR6Kpy+drvWGJIri6e1EDGADTPsCaa4KzCgmCczHrQeInx9tqs81EyDmbKJYY2swKg==}
-    peerDependencies:
-      react: '>=16.8.0'
-      react-dom: '>=16.8.0'
-    dependencies:
-      '@floating-ui/dom': 0.4.5
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-      use-isomorphic-layout-effect: 1.2.0(@types/react@19.0.10)(react@18.3.1)
-    transitivePeerDependencies:
-      - '@types/react'
-    dev: false
-
   /@formatjs/intl-displaynames@1.2.10:
     resolution: {integrity: sha512-GROA2RP6+7Ouu0WnHFF78O5XIU7pBfI19WM1qm93l6MFWibUk67nCfVCK3VAYJkLy8L8ZxjkYT11VIAfvSz8wg==}
     dependencies:
@@ -3405,7 +3401,7 @@ packages:
     peerDependencies:
       react: '>=16.3.0'
     dependencies:
-      '@babel/runtime': 7.23.6
+      '@babel/runtime': 7.26.9
       hoist-non-react-statics: 3.3.2
       react: 18.3.1
       react-is: 16.13.1
@@ -4557,7 +4553,7 @@ packages:
   /@types/eslint-scope@3.7.7:
     resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
     dependencies:
-      '@types/eslint': 9.6.1
+      '@types/eslint': 8.56.12
       '@types/estree': 1.0.6
     dev: false
 
@@ -4566,14 +4562,6 @@ packages:
     dependencies:
       '@types/estree': 1.0.6
       '@types/json-schema': 7.0.15
-    dev: true
-
-  /@types/eslint@9.6.1:
-    resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==}
-    dependencies:
-      '@types/estree': 1.0.6
-      '@types/json-schema': 7.0.15
-    dev: false
 
   /@types/estree-jsx@1.0.5:
     resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
@@ -4625,7 +4613,7 @@ packages:
     resolution: {integrity: sha512-hy8b7Y1J8OGe6LbAjj3xniQrj3v6lsivCcrmf4TzSgPzLkhIeKgc5IZnT7ReIqmEuodjfO8EYAuoFvIrHi/+jQ==}
     deprecated: This is a stub types definition. history provides its own type definitions, so you do not need this installed.
     dependencies:
-      history: 4.10.1
+      history: 5.3.0
     dev: true
 
   /@types/hoist-non-react-statics@3.3.6:
@@ -4722,7 +4710,6 @@ packages:
     resolution: {integrity: sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==}
     dependencies:
       undici-types: 6.19.8
-    dev: true
 
   /@types/node@22.13.4:
     resolution: {integrity: sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==}
@@ -4752,7 +4739,7 @@ packages:
     resolution: {integrity: sha512-eFajSUASYbPHg2BDM1G8Btx+YqGgvROPIg6sBhl3O4kbDdYXdFdfrgQFf/pcBuQVObjfT9AL/dd15jilR5DIEA==}
     dependencies:
       '@types/history': 5.0.0
-      '@types/react': 19.0.10
+      '@types/react': 18.3.18
       '@types/react-router': 5.1.20
     dev: true
 
@@ -4760,16 +4747,16 @@ packages:
     resolution: {integrity: sha512-qC5lbuP2K/kMR/HE3e5ltCJptyiQhmfV0wbklqcqWDbNdpJBDwUsBGP4f/0RDYJf09+OTbz43u6iG+8E0Zcwqw==}
     dependencies:
       '@types/history': 4.7.11
-      '@types/react': 19.0.10
+      '@types/react': 18.3.18
       '@types/react-router': 5.1.20
-      redux: 3.7.2
+      redux: 4.2.1
     dev: true
 
   /@types/react-router@5.1.20:
     resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==}
     dependencies:
       '@types/history': 4.7.11
-      '@types/react': 19.0.10
+      '@types/react': 18.3.18
     dev: true
 
   /@types/react@18.3.18:
@@ -4782,6 +4769,7 @@ packages:
     resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==}
     dependencies:
       csstype: 3.1.3
+    dev: true
 
   /@types/resolve@1.20.6:
     resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==}
@@ -5586,7 +5574,7 @@ packages:
   /@umijs/history@5.3.1:
     resolution: {integrity: sha512-/e0cEGrR2bIWQD7pRl3dl9dcyRGeC9hoW0OCvUTT/hjY0EfUrkd6G8ZanVghPMpDuY5usxq9GVcvrT8KNXLWvA==}
     dependencies:
-      '@babel/runtime': 7.23.6
+      '@babel/runtime': 7.26.9
       query-string: 6.14.1
     dev: false
 
@@ -5741,7 +5729,7 @@ packages:
       tsx: 3.12.2
     dev: false
 
-  /@umijs/plugins@4.4.5(@babel/core@7.26.9)(@types/react@19.0.10)(antd@5.24.1)(dva@2.5.0-beta.2)(rc-field-form@2.7.0)(react-dom@18.3.1)(react@18.3.1):
+  /@umijs/plugins@4.4.5(@babel/core@7.26.9)(@types/react@18.3.18)(antd@5.24.1)(dva@2.5.0-beta.2)(rc-field-form@2.7.0)(react-dom@18.3.1)(react@18.3.1):
     resolution: {integrity: sha512-MC2248NM4E+j3FzNz1luz4m55MkTHfe99DCKr43pAsKhOsKT3KOij+Eph3nNKYgSf4mcLAs99v4MLpxACv6qcw==}
     dependencies:
       '@ahooksjs/use-request': 2.8.15(react@18.3.1)
@@ -5753,7 +5741,7 @@ packages:
       '@tanstack/react-query': 4.36.1(react-dom@18.3.1)(react@18.3.1)
       '@tanstack/react-query-devtools': 4.36.1(@tanstack/react-query@4.36.1)(react-dom@18.3.1)(react@18.3.1)
       '@umijs/bundler-utils': 4.4.5
-      '@umijs/valtio': 1.0.4(@types/react@19.0.10)(react@18.3.1)
+      '@umijs/valtio': 1.0.4(@types/react@18.3.18)(react@18.3.1)
       antd-dayjs-webpack-plugin: 1.0.6(dayjs@1.11.13)
       axios: 0.27.2
       babel-plugin-import: 1.13.8
@@ -5769,7 +5757,7 @@ packages:
       moment: 2.30.1
       qiankun: 2.10.16
       react-intl: 3.12.1(react@18.3.1)
-      react-redux: 8.1.3(@types/react@19.0.10)(react-dom@18.3.1)(react@18.3.1)(redux@4.2.1)
+      react-redux: 8.1.3(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)(redux@4.2.1)
       redux: 4.2.1
       styled-components: 6.1.1(react-dom@18.3.1)(react@18.3.1)
       tslib: 2.8.1
@@ -5848,66 +5836,6 @@ packages:
       - webpack-plugin-serve
     dev: false
 
-  /@umijs/preset-umi@4.4.5(@types/react@19.0.10)(typescript@5.7.3)(webpack@5.98.0):
-    resolution: {integrity: sha512-ATy2a0Wny5fvvRmCumS3SEAOBVfccnTq6s5OP++EVUuLawx5Dt4duGix0KNhoRW4GwWPkF13HKDbHr0NfHwABg==}
-    dependencies:
-      '@iconify/utils': 2.1.1
-      '@svgr/core': 6.5.1
-      '@umijs/ast': 4.4.5
-      '@umijs/babel-preset-umi': 4.4.5
-      '@umijs/bundler-esbuild': 4.4.5
-      '@umijs/bundler-mako': 0.11.4
-      '@umijs/bundler-utils': 4.4.5
-      '@umijs/bundler-vite': 4.4.5(postcss@8.5.3)
-      '@umijs/bundler-webpack': 4.4.5(typescript@5.7.3)(webpack@5.98.0)
-      '@umijs/core': 4.4.5
-      '@umijs/did-you-know': 1.0.3
-      '@umijs/es-module-parser': 0.0.7
-      '@umijs/history': 5.3.1
-      '@umijs/mfsu': 4.4.5
-      '@umijs/plugin-run': 4.4.5
-      '@umijs/renderer-react': 4.4.5(react-dom@18.3.1)(react@18.3.1)
-      '@umijs/server': 4.4.5
-      '@umijs/ui': 3.0.1
-      '@umijs/utils': 4.4.5
-      '@umijs/zod2ts': 4.4.5
-      babel-plugin-dynamic-import-node: 2.3.3
-      babel-plugin-react-compiler: 0.0.0-experimental-c23de8d-20240515
-      click-to-react-component: 1.1.0(@types/react@19.0.10)(react-dom@18.3.1)(react@18.3.1)
-      core-js: 3.34.0
-      current-script-polyfill: 1.0.0
-      enhanced-resolve: 5.9.3
-      fast-glob: 3.2.12
-      html-webpack-plugin: 5.5.0(webpack@5.98.0)
-      less-plugin-resolve: 1.0.2
-      path-to-regexp: 1.7.0
-      postcss: 8.5.3
-      postcss-prefix-selector: 1.16.0(postcss@8.5.3)
-      react: 18.3.1
-      react-dom: 18.3.1(react@18.3.1)
-      react-router: 6.3.0(react@18.3.1)
-      react-router-dom: 6.3.0(react-dom@18.3.1)(react@18.3.1)
-      regenerator-runtime: 0.13.11
-    transitivePeerDependencies:
-      - '@types/node'
-      - '@types/react'
-      - '@types/webpack'
-      - lightningcss
-      - rollup
-      - sass
-      - sockjs-client
-      - stylus
-      - sugarss
-      - supports-color
-      - terser
-      - type-fest
-      - typescript
-      - webpack
-      - webpack-dev-server
-      - webpack-hot-middleware
-      - webpack-plugin-serve
-    dev: false
-
   /@umijs/react-refresh-webpack-plugin@0.5.11(react-refresh@0.14.0)(webpack@5.98.0):
     resolution: {integrity: sha512-RtFvB+/GmjRhpHcqNgnw8iWZpTlxOnmNxi8eDcecxMmxmSgeDj25LV0jr4Q6rOhv3GTIfVGBhkwz+khGT5tfmg==}
     engines: {node: '>= 10.13'}
@@ -6010,10 +5938,10 @@ packages:
       chokidar: 3.5.3
       pino: 7.11.0
 
-  /@umijs/valtio@1.0.4(@types/react@19.0.10)(react@18.3.1):
+  /@umijs/valtio@1.0.4(@types/react@18.3.18)(react@18.3.1):
     resolution: {integrity: sha512-2PmAU4rNQbBqrWpJ86Si9UGC23JapkYw8k7Hna6V8DHLaEYJENdp2e/IKLPHSPghzrdQtbUHSoOAUsBd4i4OzQ==}
     dependencies:
-      valtio: 1.11.2(@types/react@19.0.10)(react@18.3.1)
+      valtio: 1.11.2(@types/react@18.3.18)(react@18.3.1)
     transitivePeerDependencies:
       - '@types/react'
       - react
@@ -7523,20 +7451,6 @@ packages:
       - react-dom
     dev: false
 
-  /click-to-react-component@1.1.0(@types/react@19.0.10)(react-dom@18.3.1)(react@18.3.1):
-    resolution: {integrity: sha512-/DjZemufS1BkxyRgZL3r7HXVVOFRWVQi5Xd4EBnjxZMwrHEh0OlUVA2N9CjXkZ0x8zMf8dL1cKnnx+xUWUg4VA==}
-    peerDependencies:
-      react: '>=16.8.0'
-    dependencies:
-      '@floating-ui/react-dom-interactions': 0.3.1(@types/react@19.0.10)(react-dom@18.3.1)(react@18.3.1)
-      htm: 3.1.1
-      react: 18.3.1
-      react-merge-refs: 1.1.0
-    transitivePeerDependencies:
-      - '@types/react'
-      - react-dom
-    dev: false
-
   /client-only@0.0.1:
     resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
     dev: false
@@ -10646,8 +10560,7 @@ packages:
   /history@5.3.0:
     resolution: {integrity: sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==}
     dependencies:
-      '@babel/runtime': 7.23.6
-    dev: false
+      '@babel/runtime': 7.26.9
 
   /hmac-drbg@1.0.1:
     resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==}
@@ -11495,7 +11408,7 @@ packages:
     resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
     engines: {node: '>= 10.13.0'}
     dependencies:
-      '@types/node': 22.13.4
+      '@types/node': 20.17.19
       merge-stream: 2.0.0
       supports-color: 8.1.1
     dev: false
@@ -14993,7 +14906,7 @@ packages:
       react: ^16.6.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0
     dependencies:
-      '@babel/runtime': 7.23.6
+      '@babel/runtime': 7.26.9
       invariant: 2.2.4
       prop-types: 15.8.1
       react: 18.3.1
@@ -15031,14 +14944,14 @@ packages:
   /react-lifecycles-compat@3.0.4:
     resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==}
 
-  /react-markdown@9.0.3(@types/react@19.0.10)(react@18.3.1):
+  /react-markdown@9.0.3(@types/react@18.3.18)(react@18.3.1):
     resolution: {integrity: sha512-Yk7Z94dbgYTOrdk41Z74GoKA7rThnsbbqBTRYuxoe08qvfQ9tJVhmAKw6BJS/ZORG7kTy/s1QvYzSuaoBA1qfw==}
     peerDependencies:
       '@types/react': '>=18'
       react: '>=18'
     dependencies:
       '@types/hast': 3.0.4
-      '@types/react': 19.0.10
+      '@types/react': 18.3.18
       devlop: 1.1.0
       hast-util-to-jsx-runtime: 2.3.3
       html-url-attributes: 3.0.1
@@ -15074,7 +14987,7 @@ packages:
       redux: 3.7.2
     dev: true
 
-  /react-redux@8.1.3(@types/react@19.0.10)(react-dom@18.3.1)(react@18.3.1)(redux@4.2.1):
+  /react-redux@8.1.3(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1)(redux@4.2.1):
     resolution: {integrity: sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==}
     peerDependencies:
       '@types/react': ^16.8 || ^17.0 || ^18.0
@@ -15097,7 +15010,7 @@ packages:
     dependencies:
       '@babel/runtime': 7.26.9
       '@types/hoist-non-react-statics': 3.3.6
-      '@types/react': 19.0.10
+      '@types/react': 18.3.18
       '@types/use-sync-external-store': 0.0.3
       hoist-non-react-statics: 3.3.2
       react: 18.3.1
@@ -16976,57 +16889,6 @@ packages:
       - webpack-plugin-serve
     dev: false
 
-  /umi@4.4.5(@babel/core@7.26.9)(@types/react@19.0.10)(eslint@8.57.1)(prettier@3.5.1)(react-dom@18.3.1)(react@18.3.1)(stylelint@14.16.1)(typescript@5.7.3)(webpack@5.98.0):
-    resolution: {integrity: sha512-LtfJEnepls7+MCIj5LdKLo+beLoKUrVQjzDPAVbN/OzfiewvC4JG3X/8uQ84pEDDa5vwJoq9m9daGMS9YuVVRg==}
-    engines: {node: '>=14'}
-    hasBin: true
-    dependencies:
-      '@babel/runtime': 7.23.6
-      '@umijs/bundler-utils': 4.4.5
-      '@umijs/bundler-webpack': 4.4.5(typescript@5.7.3)(webpack@5.98.0)
-      '@umijs/core': 4.4.5
-      '@umijs/lint': 4.4.5(eslint@8.57.1)(stylelint@14.16.1)(typescript@5.7.3)
-      '@umijs/preset-umi': 4.4.5(@types/react@19.0.10)(typescript@5.7.3)(webpack@5.98.0)
-      '@umijs/renderer-react': 4.4.5(react-dom@18.3.1)(react@18.3.1)
-      '@umijs/server': 4.4.5
-      '@umijs/test': 4.4.5(@babel/core@7.26.9)
-      '@umijs/utils': 4.4.5
-      prettier-plugin-organize-imports: 3.2.4(prettier@3.5.1)(typescript@5.7.3)
-      prettier-plugin-packagejson: 2.4.3(prettier@3.5.1)
-    transitivePeerDependencies:
-      - '@babel/core'
-      - '@types/node'
-      - '@types/react'
-      - '@types/webpack'
-      - '@volar/vue-language-plugin-pug'
-      - '@volar/vue-typescript'
-      - eslint
-      - jest
-      - lightningcss
-      - postcss-html
-      - postcss-jsx
-      - postcss-less
-      - postcss-markdown
-      - postcss-scss
-      - prettier
-      - react
-      - react-dom
-      - rollup
-      - sass
-      - sockjs-client
-      - stylelint
-      - stylus
-      - sugarss
-      - supports-color
-      - terser
-      - type-fest
-      - typescript
-      - webpack
-      - webpack-dev-server
-      - webpack-hot-middleware
-      - webpack-plugin-serve
-    dev: false
-
   /unbox-primitive@1.1.0:
     resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
     engines: {node: '>= 0.4'}
@@ -17048,7 +16910,6 @@ packages:
 
   /undici-types@6.19.8:
     resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
-    dev: true
 
   /undici-types@6.20.0:
     resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
@@ -17219,19 +17080,6 @@ packages:
       react: 18.3.1
     dev: false
 
-  /use-isomorphic-layout-effect@1.2.0(@types/react@19.0.10)(react@18.3.1):
-    resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==}
-    peerDependencies:
-      '@types/react': '*'
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
-    peerDependenciesMeta:
-      '@types/react':
-        optional: true
-    dependencies:
-      '@types/react': 19.0.10
-      react: 18.3.1
-    dev: false
-
   /use-sync-external-store@1.2.0(react@18.3.1):
     resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
     peerDependencies:
@@ -17323,7 +17171,7 @@ packages:
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dev: true
 
-  /valtio@1.11.2(@types/react@19.0.10)(react@18.3.1):
+  /valtio@1.11.2(@types/react@18.3.18)(react@18.3.1):
     resolution: {integrity: sha512-1XfIxnUXzyswPAPXo1P3Pdx2mq/pIqZICkWN60Hby0d9Iqb+MEIpqgYVlbflvHdrp2YR/q3jyKWRPJJ100yxaw==}
     engines: {node: '>=12.20.0'}
     peerDependencies:
@@ -17335,7 +17183,7 @@ packages:
       react:
         optional: true
     dependencies:
-      '@types/react': 19.0.10
+      '@types/react': 18.3.18
       proxy-compare: 2.5.1
       react: 18.3.1
       use-sync-external-store: 1.2.0(react@18.3.1)