Add MindSpace page live edit, chat skills, and H5 deploy tooling.
Introduce page edit sessions with draft preview and patch API, chat skill picker, user memory profile, h5ApiBase resolution, voice WAV transport, and scripts for 105/g2 deployment and Plaza local dev. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+91
-22
@@ -1,14 +1,17 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useChat } from '../context/ChatProvider';
|
||||
import { INSUFFICIENT_BALANCE_NOTICE } from '../hooks/useTKMindChat';
|
||||
import type { CapabilityMap, PortalUser } from '../types';
|
||||
import { useNetworkStatus } from '../hooks/useNetworkStatus';
|
||||
import { getSessionDisplayName } from '../utils/sessions';
|
||||
import { openAvatarPicker } from '../utils/userAvatar';
|
||||
import { BalanceRing } from './BalanceRing';
|
||||
import { HistorySidebar } from './HistorySidebar';
|
||||
import { TKMindAvatar } from './TKMindAvatar';
|
||||
import { ChatPanel } from './ChatPanel';
|
||||
import { WechatBindPrompt } from './WechatBindPrompt';
|
||||
import { WechatAccountButton } from './WechatAccountButton';
|
||||
import { ChatHeaderMoreMenu } from './ChatHeaderMoreMenu';
|
||||
import type { MindSpaceSaveCategory } from '../types';
|
||||
|
||||
export function ChatView({
|
||||
@@ -46,12 +49,14 @@ export function ChatView({
|
||||
rememberCurrentContext,
|
||||
refreshProjectMemory,
|
||||
switchSession,
|
||||
deleteSession,
|
||||
retryConnect,
|
||||
dismissNotice,
|
||||
onSidebarOpen,
|
||||
workingDir,
|
||||
balanceCents,
|
||||
totalCreditCents,
|
||||
tokensUsed,
|
||||
openRecharge,
|
||||
} = useChat();
|
||||
const online = useNetworkStatus();
|
||||
@@ -63,7 +68,7 @@ export function ChatView({
|
||||
}
|
||||
}, [sidebarOpen, onSidebarOpen]);
|
||||
|
||||
const busy = chatState === 'streaming' || chatState === 'loading';
|
||||
const busy = chatState === 'streaming' || chatState === 'loading' || chatState === 'connecting';
|
||||
|
||||
const handleSelectSession = (sessionId: string) => {
|
||||
void switchSession(sessionId);
|
||||
@@ -75,7 +80,37 @@ export function ChatView({
|
||||
setSidebarOpen(false);
|
||||
};
|
||||
|
||||
const sessionTitle = session ? getSessionDisplayName(session) : '连接中…';
|
||||
const sessionTitle = session
|
||||
? getSessionDisplayName(session)
|
||||
: chatState === 'connecting'
|
||||
? '新对话'
|
||||
: '连接中…';
|
||||
|
||||
const moreMenuItems = [
|
||||
{
|
||||
id: 'new-session',
|
||||
label: '新会话',
|
||||
onClick: () => void handleNewSession(),
|
||||
},
|
||||
{
|
||||
id: 'remember',
|
||||
label: '记住本轮',
|
||||
onClick: () => void rememberCurrentContext(),
|
||||
disabled: busy || memoryLoading || messages.length === 0,
|
||||
},
|
||||
{
|
||||
id: 'refresh-memory',
|
||||
label: memoryLoading ? '同步中…' : '刷新记忆',
|
||||
onClick: () => void refreshProjectMemory(),
|
||||
disabled: busy || memoryLoading,
|
||||
},
|
||||
...(onOpenAdmin
|
||||
? [{ id: 'admin', label: '管理', onClick: () => onOpenAdmin() }]
|
||||
: []),
|
||||
...(onLogout
|
||||
? [{ id: 'logout', label: '登出', onClick: () => onLogout(), danger: true }]
|
||||
: []),
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="app-shell">
|
||||
@@ -87,6 +122,7 @@ export function ChatView({
|
||||
onClose={() => setSidebarOpen(false)}
|
||||
onSelect={handleSelectSession}
|
||||
onNew={handleNewSession}
|
||||
onDelete={(sessionId) => void deleteSession(sessionId)}
|
||||
/>
|
||||
|
||||
<div className="app">
|
||||
@@ -106,11 +142,12 @@ export function ChatView({
|
||||
<div className="header-sub">{sessionTitle}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="header-actions">
|
||||
<div className="header-actions header-actions-desktop">
|
||||
{typeof balanceCents === 'number' && (
|
||||
<BalanceRing
|
||||
balanceCents={balanceCents}
|
||||
totalCreditCents={totalCreditCents}
|
||||
tokensUsed={tokensUsed}
|
||||
onRecharge={openRecharge}
|
||||
/>
|
||||
)}
|
||||
@@ -152,6 +189,41 @@ export function ChatView({
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="header-actions header-actions-mobile">
|
||||
{typeof balanceCents === 'number' && (
|
||||
<BalanceRing
|
||||
balanceCents={balanceCents}
|
||||
totalCreditCents={totalCreditCents}
|
||||
tokensUsed={tokensUsed}
|
||||
onRecharge={openRecharge}
|
||||
/>
|
||||
)}
|
||||
{onOpenSpace && (
|
||||
<button
|
||||
type="button"
|
||||
className="header-icon-btn"
|
||||
aria-label="我的空间"
|
||||
title="我的空间"
|
||||
onClick={() => onOpenSpace?.()}
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path
|
||||
d="M4 8.5V19a1 1 0 001 1h14a1 1 0 001-1V8.5M4 8.5l8-4.5 8 4.5M4 8.5h16M9 21v-6h6v6"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.75"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
<ChatHeaderMoreMenu
|
||||
items={moreMenuItems}
|
||||
returnTo={window.location.pathname}
|
||||
showWechat={Boolean(user)}
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{!online && (
|
||||
@@ -163,7 +235,7 @@ export function ChatView({
|
||||
{typeof balanceCents === 'number' && balanceCents <= 0 && (
|
||||
<div className="banner banner-error">
|
||||
<span>余额不足,请充值后继续使用</span>
|
||||
<button type="button" className="banner-action" onClick={() => openRecharge(true)}>
|
||||
<button type="button" className="banner-action" onClick={() => openRecharge(false)}>
|
||||
去充值
|
||||
</button>
|
||||
</div>
|
||||
@@ -177,26 +249,23 @@ export function ChatView({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{user?.publishUrl && (
|
||||
<div className="banner banner-info publish-banner">
|
||||
<span>
|
||||
你的发布目录:
|
||||
<a href={user.publishUrl} target="_blank" rel="noreferrer">
|
||||
{user.publishUrl}
|
||||
</a>
|
||||
{Boolean(capabilities?.static_publish) || (grantedSkills?.includes(user.publishSkillName ?? 'static-page-publish') ?? false)
|
||||
? '(已开通静态页面技能,生成后可直接访问)'
|
||||
: '(需管理员在「技能配置」中勾选 static-page-publish)'}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{notice && (
|
||||
<div className="banner banner-info">
|
||||
<div className={`banner${notice === INSUFFICIENT_BALANCE_NOTICE ? ' banner-warning' : ' banner-info'}`}>
|
||||
<span>{notice}</span>
|
||||
<button type="button" className="banner-dismiss" onClick={dismissNotice}>
|
||||
知道了
|
||||
</button>
|
||||
{notice === INSUFFICIENT_BALANCE_NOTICE ? (
|
||||
<>
|
||||
<button type="button" className="banner-action" onClick={() => openRecharge(false)}>
|
||||
去充值
|
||||
</button>
|
||||
<button type="button" className="banner-dismiss" onClick={dismissNotice}>
|
||||
知道了
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<button type="button" className="banner-dismiss" onClick={dismissNotice}>
|
||||
知道了
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user