diff --git a/.cursorignore b/.cursorignore
index dc58d54..1ab54d2 100644
--- a/.cursorignore
+++ b/.cursorignore
@@ -3,4 +3,4 @@ biome.json
index.html
pnpm-lock.yaml
.trae/
-.cursor/
\ No newline at end of file
+.cursor/
diff --git a/index.html b/index.html
index e4b78ea..1ece71b 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
-
Vite + React + TS
+ 幕间自定义界面模版 React版
diff --git a/package.json b/package.json
index db232f3..a550bea 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,9 @@
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@vitejs/plugin-react": "^5.0.0",
+ "clsx": "^2.1.1",
"globals": "^16.3.0",
+ "tailwind-merge": "^3.4.0",
"typescript": "~5.8.3",
"vite": "^7.1.2"
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 170483e..d05fe2e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -66,9 +66,15 @@ importers:
'@vitejs/plugin-react':
specifier: ^5.0.0
version: 5.0.2(vite@7.1.3(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1))
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
globals:
specifier: ^16.3.0
version: 16.3.0
+ tailwind-merge:
+ specifier: ^3.4.0
+ version: 3.4.0
typescript:
specifier: ~5.8.3
version: 5.8.3
@@ -685,6 +691,10 @@ packages:
resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
engines: {node: '>=6'}
+ clsx@2.1.1:
+ resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
+ engines: {node: '>=6'}
+
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@@ -1275,6 +1285,9 @@ packages:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
+ tailwind-merge@3.4.0:
+ resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==}
+
tailwindcss@4.1.12:
resolution: {integrity: sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==}
@@ -1972,6 +1985,8 @@ snapshots:
cli-spinners@2.9.2: {}
+ clsx@2.1.1: {}
+
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
@@ -2408,6 +2423,8 @@ snapshots:
dependencies:
has-flag: 4.0.0
+ tailwind-merge@3.4.0: {}
+
tailwindcss@4.1.12: {}
tapable@2.2.3: {}
diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx
index 7784b4f..fb5d330 100644
--- a/src/components/Button/index.tsx
+++ b/src/components/Button/index.tsx
@@ -4,16 +4,17 @@ interface ButtonProps extends React.ButtonHTMLAttributes {
children: React.ReactNode;
}
-export const Button: React.FC = ({ children, className = '', ...props }) => {
+export const Button: React.FC = ({
+ children,
+ className = '',
+ ...props
+}) => {
return (
);
};
-
-
-
diff --git a/src/pages/about/index.tsx b/src/pages/about/index.tsx
deleted file mode 100644
index 76752c0..0000000
--- a/src/pages/about/index.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Button } from '@/components';
-import './index.css';
-import { useNavigate } from 'react-router';
-
-// import { useGlobalStore } from "@/store/global";
-
-function About() {
- const navigate = useNavigate();
- // const { count, increment } = useGlobalStore((state) => state);
-
- return (
-
-
这里是第二个页面
-
-
- );
-}
-
-export default About;
diff --git a/src/pages/about/components/.gitkeep b/src/pages/chat/components/.gitkeep
similarity index 100%
rename from src/pages/about/components/.gitkeep
rename to src/pages/chat/components/.gitkeep
diff --git a/src/pages/chat/index.tsx b/src/pages/chat/index.tsx
new file mode 100644
index 0000000..7e42bdb
--- /dev/null
+++ b/src/pages/chat/index.tsx
@@ -0,0 +1,133 @@
+import { useChat, useMujian } from '@mujian/js-sdk/react';
+import { useEffect, useRef, useState } from 'react';
+import { useNavigate } from 'react-router';
+import { Button } from '@/components';
+import { useGlobalStore } from '@/store/global';
+import { cn } from '@/utils/cn';
+
+// import { useGlobalStore } from "@/store/global";
+
+function Chat() {
+ const { init } = useGlobalStore();
+ const mujian = useMujian();
+ const navigate = useNavigate();
+ const [inputValue, setInputValue] = useState('');
+ const messagesContainerRef = useRef(null);
+
+ useEffect(() => {
+ init(mujian);
+ }, []);
+
+ // const { count, increment } = useGlobalStore((state) => state);
+
+ // 调用消息 SDK,获取消息列表、状态、错误信息、添加消息、停止消息
+ const { messages, status, error, append, stop } = useChat({
+ onError: (e) => {
+ console.error(e);
+ },
+ });
+
+ useEffect(() => {
+ if (messagesContainerRef.current) {
+ messagesContainerRef.current?.scrollTo({
+ top: messagesContainerRef.current.scrollHeight,
+ behavior: 'smooth',
+ });
+ }
+ }, [messages]);
+
+ // 发送按钮
+ const handleSend = () => {
+ append(inputValue);
+ setInputValue('');
+ };
+
+ // 停止按钮
+ const handleStop = () => {
+ stop();
+ };
+
+ // 输入框变化
+ const handleInputChange = (e: React.ChangeEvent) => {
+ setInputValue(e.target.value);
+ };
+
+ return (
+
+
+ 这里是首页{' '}
+
+
+
+
+ {messages.map((message) =>
+ message.role === 'user' ? (
+
+ User: {message.content}
+
+ ) : (
+
+ Assistant: {message.content || '思考中...'}
+
+ ),
+ )}
+
+
+
+
+
+ 当前状态: {status}
+
+
+
+
+
+
+
+ );
+}
+
+export default Chat;
diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx
deleted file mode 100644
index b450709..0000000
--- a/src/pages/home/index.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import './index.css';
-import { useNavigate } from 'react-router';
-import { Button } from '@/components';
-
-function Home() {
- const navigate = useNavigate();
- return (
-
-
这里是首页
-
-
- );
-}
-
-export default Home;
diff --git a/src/pages/home/components/.gitkeep b/src/pages/second/components/.gitkeep
similarity index 100%
rename from src/pages/home/components/.gitkeep
rename to src/pages/second/components/.gitkeep
diff --git a/src/pages/home/index.css b/src/pages/second/index.css
similarity index 100%
rename from src/pages/home/index.css
rename to src/pages/second/index.css
diff --git a/src/pages/second/index.tsx b/src/pages/second/index.tsx
new file mode 100644
index 0000000..2ef5e68
--- /dev/null
+++ b/src/pages/second/index.tsx
@@ -0,0 +1,30 @@
+import './index.css';
+import { useNavigate } from 'react-router';
+import { Button } from '@/components';
+import { useGlobalStore } from '@/store/global';
+
+function Second() {
+ const navigate = useNavigate();
+
+ const { projectInfo, activePersona } = useGlobalStore();
+ return (
+
+
这里是第二个页面
+
+
+
{JSON.stringify(projectInfo, null, 2)}
+
+
+
+
{JSON.stringify(activePersona, null, 2)}
+
+
+ );
+}
+
+export default Second;
diff --git a/src/providers/RouterProvider.tsx b/src/providers/RouterProvider.tsx
index ead1e77..6326b90 100644
--- a/src/providers/RouterProvider.tsx
+++ b/src/providers/RouterProvider.tsx
@@ -1,16 +1,16 @@
import { createBrowserRouter } from 'react-router';
import { RouterProvider } from 'react-router/dom';
-import About from '@/pages/about';
-import Home from '@/pages/home';
+import Chat from '@/pages/chat';
+import Second from '@/pages/second';
const router = createBrowserRouter([
{
path: '/',
- element: ,
+ element: ,
},
{
- path: '/about',
- element: ,
+ path: '/second',
+ element: ,
},
]);
diff --git a/src/store/global.tsx b/src/store/global.tsx
index c4ae7f0..4e93482 100644
--- a/src/store/global.tsx
+++ b/src/store/global.tsx
@@ -1,14 +1,23 @@
+import type { MujianSdk } from '@mujian/js-sdk';
+import type { PersonaInfo, ProjectInfo } from '@mujian/js-sdk/types';
import { create } from 'zustand';
type GlobalState = {
- count: number;
- increment: () => void;
+ projectInfo: ProjectInfo | null;
+ activePersona: PersonaInfo | null;
+ init: (mujian: MujianSdk) => Promise;
};
export const useGlobalStore = create((set) => ({
count: 0,
- increment: () => {
- console.log('increment');
- set((state) => ({ count: state.count + 1 }));
+ projectInfo: null,
+ activePersona: null,
+
+ init: async (mujian: MujianSdk) => {
+ const [projectInfo, persona] = await Promise.all([
+ mujian.ai.chat.project.getInfo(),
+ mujian.ai.chat.settings.persona.getActive(),
+ ]);
+ set({ projectInfo, activePersona: persona });
},
}));
diff --git a/src/utils/cn.ts b/src/utils/cn.ts
new file mode 100644
index 0000000..bff778e
--- /dev/null
+++ b/src/utils/cn.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from 'clsx';
+import { twMerge } from 'tailwind-merge';
+
+export const cn = (...inputs: ClassValue[]) => {
+ return twMerge(clsx(inputs));
+};
diff --git a/vite.config.ts b/vite.config.ts
index 0c2aac5..de4e543 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -5,10 +5,13 @@ import { defineConfig } from 'vite';
// https://vite.dev/config/
export default defineConfig({
- plugins: [react(), tailwindcss()],
- resolve: {
- alias: {
- '@': path.resolve(__dirname, './src'),
- },
- },
+ plugins: [react(), tailwindcss()],
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src'),
+ },
+ },
+ server: {
+ cors: true,
+ },
});