0.6.1
This commit is contained in:
2
.npmrc
2
.npmrc
@@ -1 +1 @@
|
||||
registry=https://registry.npmmirror.com
|
||||
# registry=https://registry.npmmirror.com
|
||||
@@ -18,7 +18,7 @@
|
||||
"dependencies": {
|
||||
"@heroui/react": "2.8.3",
|
||||
"@heroui/theme": "2.4.21",
|
||||
"@mujian/js-sdk": "0.0.6-beta.65",
|
||||
"@mujian/js-sdk": "0.0.6-beta.mjv.68",
|
||||
"@tailwindcss/vite": "4.1.12",
|
||||
"ahooks": "3.9.5",
|
||||
"axios": "1.11.0",
|
||||
|
||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -15,8 +15,8 @@ importers:
|
||||
specifier: 2.4.21
|
||||
version: 2.4.21(tailwindcss@4.1.12)
|
||||
'@mujian/js-sdk':
|
||||
specifier: 0.0.6-beta.65
|
||||
version: 0.0.6-beta.65(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
specifier: 0.0.6-beta.mjv.68
|
||||
version: 0.0.6-beta.mjv.68(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
'@tailwindcss/vite':
|
||||
specifier: 4.1.12
|
||||
version: 4.1.12(vite@7.1.2(@types/node@22.13.9)(jiti@2.5.1)(lightningcss@1.30.1))
|
||||
@@ -996,8 +996,8 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.30':
|
||||
resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==}
|
||||
|
||||
'@mujian/js-sdk@0.0.6-beta.65':
|
||||
resolution: {integrity: sha512-QMAYn6wx4uLlGSBjkAH8O9gR4x0vgHUpIKupIJSlgIKfjIqgYOZEvvx3/4anH2l6ESXGTR521x2ri9uQj5cuyw==}
|
||||
'@mujian/js-sdk@0.0.6-beta.mjv.68':
|
||||
resolution: {integrity: sha512-bsuev++lvRmS+fD36XljHIw6DnUTFzVrqV69IXKMTb7Vr8W70qQ1rMrn9gN8Lva996JZrXKQ39f3jFrRo1o53g==}
|
||||
peerDependencies:
|
||||
react: ~19.1.1
|
||||
react-dom: ~19.1.1
|
||||
@@ -3942,7 +3942,7 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@mujian/js-sdk@0.0.6-beta.65(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
'@mujian/js-sdk@0.0.6-beta.mjv.68(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
|
||||
dependencies:
|
||||
'@adobe/css-tools': 4.4.4
|
||||
ahooks: 3.9.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
|
||||
@@ -22,6 +22,7 @@ export type LastMessageActionsProps = {
|
||||
isEditing: boolean;
|
||||
swipes: string[];
|
||||
activeSwipeId: number;
|
||||
panicked?: boolean;
|
||||
onRegenerate: (() => Promise<void>) | undefined;
|
||||
onContinue: (() => Promise<void>) | undefined;
|
||||
onEditButtonClick: () => void;
|
||||
@@ -116,6 +117,7 @@ export const MessageActions = ({
|
||||
item.className || '',
|
||||
)}
|
||||
onPress={item.onPress}
|
||||
isDisabled={item.disabled}
|
||||
classNames={{
|
||||
wrapper: mjChatCls(
|
||||
[
|
||||
|
||||
@@ -26,7 +26,7 @@ export type MessageBubbleProps = {
|
||||
};
|
||||
|
||||
export const MessageBubble = ({
|
||||
// message,
|
||||
message,
|
||||
isUser,
|
||||
isEditing,
|
||||
editedMessage,
|
||||
@@ -79,7 +79,13 @@ export const MessageBubble = ({
|
||||
)}
|
||||
role="presentation"
|
||||
>
|
||||
<MdRenderer content={currentMessage} />
|
||||
<MdRenderer
|
||||
content={currentMessage}
|
||||
extra={{
|
||||
messageId: message.id,
|
||||
swipeId: message.activeSwipeId,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu
|
||||
@@ -103,6 +109,7 @@ export const MessageBubble = ({
|
||||
item.className || '',
|
||||
)}
|
||||
onPress={item.onPress}
|
||||
isDisabled={item.disabled}
|
||||
>
|
||||
{item.label}
|
||||
</DropdownItem>
|
||||
@@ -111,7 +118,13 @@ export const MessageBubble = ({
|
||||
</Dropdown>
|
||||
) : (
|
||||
<div className={mjChatCls(['msg-content'], 'py-4')}>
|
||||
<MdRenderer content={currentMessage} />
|
||||
<MdRenderer
|
||||
content={currentMessage}
|
||||
extra={{
|
||||
messageId: message.id,
|
||||
swipeId: message.activeSwipeId,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { useContext } from 'react';
|
||||
import { PanicContext } from '../../PanicContext';
|
||||
|
||||
export type DropdownItem = {
|
||||
key: string;
|
||||
label: string;
|
||||
@@ -10,6 +13,7 @@ export type DropdownItem = {
|
||||
| 'warning'
|
||||
| 'danger';
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export type UseDropdownItemsProps = {
|
||||
@@ -29,11 +33,14 @@ export const useMessageActions = ({
|
||||
onContinue,
|
||||
onDelete,
|
||||
}: UseDropdownItemsProps): DropdownItem[] => {
|
||||
const { panicked } = useContext(PanicContext);
|
||||
|
||||
const dropdownItems: DropdownItem[] = [
|
||||
{
|
||||
key: 'edit',
|
||||
label: '编辑',
|
||||
onPress: onEditButtonClick,
|
||||
disabled: panicked,
|
||||
},
|
||||
{
|
||||
key: 'copy',
|
||||
@@ -46,11 +53,13 @@ export const useMessageActions = ({
|
||||
key: 'regenerate',
|
||||
label: '重说',
|
||||
onPress: async () => await onRegenerate?.(),
|
||||
disabled: panicked,
|
||||
},
|
||||
{
|
||||
key: 'continue',
|
||||
label: '继续',
|
||||
onPress: async () => await onContinue?.(),
|
||||
disabled: panicked,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
@@ -60,6 +69,7 @@ export const useMessageActions = ({
|
||||
onPress: onDelete,
|
||||
color: 'danger',
|
||||
className: 'text-danger',
|
||||
disabled: panicked,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -83,6 +83,8 @@ export const MessageItem = React.memo((props: MessageItemProps) => {
|
||||
swipes = [],
|
||||
activeSwipeId,
|
||||
|
||||
mjv,
|
||||
|
||||
// sendAt,
|
||||
} = message;
|
||||
const isUser = role === 'user';
|
||||
@@ -183,7 +185,7 @@ export const MessageItem = React.memo((props: MessageItemProps) => {
|
||||
// 按ctrl点击时打印ID
|
||||
if (event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
console.log('Official Chat Message ID:', id);
|
||||
console.log('Official Chat Message ID:', id, mjv);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Button, Textarea } from '@heroui/react';
|
||||
import { CircleStopIcon, SendIcon } from 'lucide-react';
|
||||
import { mjChatCls } from '@/utils/cls';
|
||||
import { useContext } from 'react';
|
||||
import { PanicContext } from './PanicContext';
|
||||
|
||||
interface Props {
|
||||
onSend: (query: string) => void;
|
||||
@@ -28,6 +30,8 @@ export const MsgSend = ({
|
||||
value,
|
||||
onChange,
|
||||
}: Props) => {
|
||||
const { panicked } = useContext(PanicContext);
|
||||
|
||||
const isEmptyInput = value.trim().length === 0;
|
||||
|
||||
const handleSend = () => {
|
||||
@@ -64,7 +68,7 @@ export const MsgSend = ({
|
||||
),
|
||||
mainWrapper: mjChatCls(['input-textarea-mainWrapper'], 'px-2'),
|
||||
}}
|
||||
disabled={running}
|
||||
disabled={running || panicked}
|
||||
endContent={
|
||||
running ? (
|
||||
<Button
|
||||
|
||||
3
src/pages/chat/PanicContext.tsx
Normal file
3
src/pages/chat/PanicContext.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const PanicContext = createContext({ panicked: false });
|
||||
@@ -1,19 +1,31 @@
|
||||
import { Alert, ScrollShadow, Spinner, ToastProvider } from '@heroui/react';
|
||||
import { useChat, useMujian } from '@mujian/js-sdk/react';
|
||||
import type { PersonaInfo, ProjectInfo } from '@mujian/js-sdk/types';
|
||||
import * as _ from 'lodash-es';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { mjChatCls } from '@/utils/cls';
|
||||
import { MessageList } from './MessageList';
|
||||
import { MsgSend } from './MsgSend';
|
||||
import { PanicContext } from './PanicContext';
|
||||
import { QuickReply } from './QuickReply';
|
||||
|
||||
let $mj_ai_chat_complete: ((message: string) => Promise<void>) | undefined;
|
||||
let $mj_ai_chat_project: ProjectInfo | undefined;
|
||||
let $mj_ai_chat_settings_persona: PersonaInfo | undefined;
|
||||
let $mj_ai_chat_setFirstMesIndex:
|
||||
| ((index: number) => Promise<void>)
|
||||
| undefined;
|
||||
let $mj_ai_chat_mjv_get:
|
||||
| ((messageId: string, swipeId: number, path: string) => unknown)
|
||||
| undefined;
|
||||
let $mj_ai_chat_mjv_getAll:
|
||||
| ((messageId: string, swipeId: number) => unknown)
|
||||
| undefined;
|
||||
|
||||
// 扩展Window接口以包含chat对象
|
||||
declare global {
|
||||
interface Window {
|
||||
$mj_engine: {
|
||||
ai: {
|
||||
chat: {
|
||||
interface MjEngineAiChat {
|
||||
complete?: (message: string) => Promise<void>;
|
||||
project?: ProjectInfo;
|
||||
settings: {
|
||||
@@ -23,21 +35,71 @@ declare global {
|
||||
* @param index 下标从1开始
|
||||
*/
|
||||
setFirstMesIndex?: (index: number) => Promise<void>;
|
||||
}
|
||||
|
||||
interface MjEngineAi {
|
||||
chat: MjEngineAiChat;
|
||||
}
|
||||
|
||||
interface MjEngine {
|
||||
ai: MjEngineAi;
|
||||
}
|
||||
|
||||
interface Window {
|
||||
$mj_engine: MjEngine & {
|
||||
bind: (extra: { messageId: string; swipeId: number }) => MjEngine & {
|
||||
ai: {
|
||||
chat: {
|
||||
mjv: {
|
||||
get: (path: string) => unknown;
|
||||
getAll: () => unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
window.$mj_engine = {
|
||||
const $mj_engine: MjEngine = {
|
||||
ai: {
|
||||
chat: {
|
||||
complete: undefined,
|
||||
project: undefined,
|
||||
get complete() {
|
||||
return $mj_ai_chat_complete;
|
||||
},
|
||||
get project() {
|
||||
return $mj_ai_chat_project;
|
||||
},
|
||||
settings: {
|
||||
persona: undefined,
|
||||
get persona() {
|
||||
return $mj_ai_chat_settings_persona;
|
||||
},
|
||||
},
|
||||
get setFirstMesIndex() {
|
||||
return $mj_ai_chat_setFirstMesIndex;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
window.$mj_engine = {
|
||||
...$mj_engine,
|
||||
bind({ messageId, swipeId }) {
|
||||
return {
|
||||
ai: {
|
||||
chat: {
|
||||
...$mj_engine.ai.chat,
|
||||
mjv: {
|
||||
get(path) {
|
||||
return $mj_ai_chat_mjv_get?.(messageId, swipeId, path);
|
||||
},
|
||||
getAll() {
|
||||
return $mj_ai_chat_mjv_getAll?.(messageId, swipeId) ?? {};
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -65,8 +127,8 @@ export const Chat = () => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
window.$mj_engine.ai.chat.project = projectInfo ?? undefined;
|
||||
window.$mj_engine.ai.chat.settings.persona = activePersona ?? undefined;
|
||||
$mj_ai_chat_project = projectInfo ?? undefined;
|
||||
$mj_ai_chat_settings_persona = activePersona ?? undefined;
|
||||
}, [projectInfo, activePersona]);
|
||||
|
||||
// 自定义删除消息函数
|
||||
@@ -130,10 +192,10 @@ export const Chat = () => {
|
||||
|
||||
// 将chat对象挂载到window上
|
||||
useEffect(() => {
|
||||
window.$mj_engine.ai.chat.complete = async (message) => {
|
||||
$mj_ai_chat_complete = async (message) => {
|
||||
await onSend(message);
|
||||
};
|
||||
window.$mj_engine.ai.chat.setFirstMesIndex = async (oneBasedIndex) => {
|
||||
$mj_ai_chat_setFirstMesIndex = async (oneBasedIndex) => {
|
||||
if (messages.length > 1) {
|
||||
throw new Error('已有新消息,不允许切换开场白');
|
||||
}
|
||||
@@ -147,6 +209,15 @@ export const Chat = () => {
|
||||
}
|
||||
setSwipe(messages[0].id, zeroBasedIndex);
|
||||
};
|
||||
$mj_ai_chat_mjv_getAll = (messageId, swipeId) =>
|
||||
messages.find((m) => m.id === messageId)?.swipeInfo[swipeId]?.mjv?.value;
|
||||
$mj_ai_chat_mjv_get = (messageId, swipeId, path) => {
|
||||
const mjv = $mj_ai_chat_mjv_getAll?.(messageId, swipeId);
|
||||
if (!mjv) {
|
||||
return;
|
||||
}
|
||||
return _.get(mjv, path);
|
||||
};
|
||||
}, [onSend, messages, setSwipe]);
|
||||
|
||||
const handleInputChange = (value: string) => {
|
||||
@@ -175,7 +246,14 @@ export const Chat = () => {
|
||||
margin-top: ${paddingTop}px;
|
||||
}`;
|
||||
|
||||
const saveFailed =
|
||||
status !== 'streaming' &&
|
||||
messages.some((m) => m.id.startsWith('not_saved'));
|
||||
|
||||
return (
|
||||
<PanicContext.Provider
|
||||
value={{ panicked: saveFailed || Boolean(errorMessage) }}
|
||||
>
|
||||
<div className={mjChatCls(['container'], 'size-full relative')}>
|
||||
<style> {toastStyle} </style>
|
||||
<ToastProvider
|
||||
@@ -228,8 +306,7 @@ export const Chat = () => {
|
||||
onRegenerate={regenerate}
|
||||
onNeedMore={loadMoreMessage}
|
||||
/>
|
||||
{status !== 'streaming' &&
|
||||
messages.some((m) => m.id.startsWith('not_saved')) && (
|
||||
{saveFailed && (
|
||||
<Alert
|
||||
hideIcon
|
||||
title="最近一次保存消息失败,请刷新页面后重试"
|
||||
@@ -250,15 +327,20 @@ export const Chat = () => {
|
||||
['error-message-mainWrapper'],
|
||||
'min-h-4 ml-0',
|
||||
),
|
||||
description: 'opacity-80 text-xs',
|
||||
}}
|
||||
color="danger"
|
||||
title={errorMessage}
|
||||
title="出错啦,请刷新页面后重试"
|
||||
description={errorMessage}
|
||||
variant="bordered"
|
||||
/>
|
||||
)}
|
||||
</ScrollShadow>
|
||||
<div
|
||||
className={mjChatCls(['input-container'], 'flex flex-col gap-4 mt-2')}
|
||||
className={mjChatCls(
|
||||
['input-container'],
|
||||
'flex flex-col gap-4 mt-2',
|
||||
)}
|
||||
>
|
||||
{projectInfo?.config?.qrConfig?.qrList &&
|
||||
projectInfo?.config?.qrConfig?.qrList.length > 0 && (
|
||||
@@ -277,5 +359,6 @@ export const Chat = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PanicContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user