Commit 1488c6e6 authored by Andy-bubu's avatar Andy-bubu

fix: refine ai chat assistant cards and model controls

parent aa343ada
import { import {
BulbOutlined,
CloseOutlined,
CopyOutlined, CopyOutlined,
DownOutlined,
PaperClipOutlined, PaperClipOutlined,
ReloadOutlined, ReloadOutlined,
SearchOutlined,
SettingOutlined, SettingOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { import {
...@@ -14,7 +18,28 @@ import { ChatMessage } from '@heroui-pro/react/chat-message'; ...@@ -14,7 +18,28 @@ import { ChatMessage } from '@heroui-pro/react/chat-message';
import { Markdown, StreamMarkdown } from '@heroui-pro/react/markdown'; import { Markdown, StreamMarkdown } from '@heroui-pro/react/markdown';
import { PromptInput } from '@heroui-pro/react/prompt-input'; import { PromptInput } from '@heroui-pro/react/prompt-input';
import { PromptSuggestion } from '@heroui-pro/react/prompt-suggestion'; import { PromptSuggestion } from '@heroui-pro/react/prompt-suggestion';
import { Disclosure, Spinner, Surface, toast } from '@heroui/react'; import {
Autocomplete,
Chip,
Disclosure,
ListBox,
SearchField,
Spinner,
Surface,
toast,
} from '@heroui/react';
function filterModel(textValue, inputValue) {
const query = String(inputValue || '')
.trim()
.toLocaleLowerCase();
return (
!query ||
String(textValue || '')
.toLocaleLowerCase()
.includes(query)
);
}
function MessageAttachments({ files = [] }) { function MessageAttachments({ files = [] }) {
if (!files.length) return null; if (!files.length) return null;
...@@ -162,7 +187,7 @@ function PromptGroups({ groups = [], onSelect }) { ...@@ -162,7 +187,7 @@ function PromptGroups({ groups = [], onSelect }) {
return ( return (
<PromptSuggestion className="rc-chat-suggestions" variant="card"> <PromptSuggestion className="rc-chat-suggestions" variant="card">
{groups.map((group) => ( {groups.map((group) => (
<PromptSuggestion.Group key={group.key} label={group.label}> <PromptSuggestion.Group key={group.key}>
<PromptSuggestion.Items <PromptSuggestion.Items
className={`rc-chat-suggestion-items${ className={`rc-chat-suggestion-items${
group.children?.length === 1 group.children?.length === 1
...@@ -183,6 +208,14 @@ function PromptGroups({ groups = [], onSelect }) { ...@@ -183,6 +208,14 @@ function PromptGroups({ groups = [], onSelect }) {
} }
}} }}
> >
{item.icon ? (
<Surface
className="rc-chat-agent-icon grid size-14 place-items-center rounded-xl border border-border"
variant="default"
>
{item.icon}
</Surface>
) : null}
<PromptSuggestion.ItemTitle> <PromptSuggestion.ItemTitle>
{item.label || item.description || item.title} {item.label || item.description || item.title}
</PromptSuggestion.ItemTitle> </PromptSuggestion.ItemTitle>
...@@ -201,6 +234,7 @@ function PromptGroups({ groups = [], onSelect }) { ...@@ -201,6 +234,7 @@ function PromptGroups({ groups = [], onSelect }) {
} }
export default function ChatWorkspace({ export default function ChatWorkspace({
activeMode,
canConfigure, canConfigure,
children, children,
description, description,
...@@ -209,9 +243,12 @@ export default function ChatWorkspace({ ...@@ -209,9 +243,12 @@ export default function ChatWorkspace({
isLoading, isLoading,
knowledgeBases = [], knowledgeBases = [],
messages = [], messages = [],
modelListLoading = false,
models = [],
onConfigure, onConfigure,
onFilesSelected, onFilesSelected,
onInputChange, onInputChange,
onModelChange,
onQuestion, onQuestion,
onRegenerate, onRegenerate,
onRemoveFile, onRemoveFile,
...@@ -221,6 +258,7 @@ export default function ChatWorkspace({ ...@@ -221,6 +258,7 @@ export default function ChatWorkspace({
questions = [], questions = [],
renderAssistantExtras, renderAssistantExtras,
renderAssistantContent, renderAssistantContent,
selectedModel,
suggestionGroups = [], suggestionGroups = [],
title, title,
userInfo, userInfo,
...@@ -228,6 +266,10 @@ export default function ChatWorkspace({ ...@@ -228,6 +266,10 @@ export default function ChatWorkspace({
const visibleMessages = messages.filter( const visibleMessages = messages.filter(
(item) => !item.message?.metadata?.hidden, (item) => !item.message?.metadata?.hidden,
); );
const selectedModelLabel =
models.find((model) => String(model.id) === String(selectedModel))?.label ||
selectedModel ||
(modelListLoading ? '正在加载模型' : '选择模型');
return ( return (
<div className="rc-chat-workspace"> <div className="rc-chat-workspace">
...@@ -273,7 +315,24 @@ export default function ChatWorkspace({ ...@@ -273,7 +315,24 @@ export default function ChatWorkspace({
); );
})} })}
{!visibleMessages.length ? ( {!visibleMessages.length ? (
<>
{!title ? (
<section
aria-labelledby="chat-welcome-title"
className="rc-chat-empty-state flex flex-col items-center text-center"
>
<Surface
className="grid size-16 place-items-center rounded-full text-accent"
variant="tertiary"
>
<BulbOutlined className="text-2xl" />
</Surface>
<h1 id="chat-welcome-title">今天想处理什么?</h1>
<p>选择一个助手,或直接在下方输入问题。</p>
</section>
) : null}
<PromptGroups groups={suggestionGroups} onSelect={onSuggestion} /> <PromptGroups groups={suggestionGroups} onSelect={onSuggestion} />
</>
) : null} ) : null}
{children} {children}
<ChatConversation.ScrollAnchor /> <ChatConversation.ScrollAnchor />
...@@ -329,7 +388,7 @@ export default function ChatWorkspace({ ...@@ -329,7 +388,7 @@ export default function ChatWorkspace({
</PromptInput.Attachments> </PromptInput.Attachments>
) : null} ) : null}
<PromptInput.Content> <PromptInput.Content>
<PromptInput.TextArea placeholder="请输入消息内容" /> <PromptInput.TextArea placeholder="你想了解什么?" />
</PromptInput.Content> </PromptInput.Content>
<PromptInput.Toolbar> <PromptInput.Toolbar>
<PromptInput.ToolbarStart> <PromptInput.ToolbarStart>
...@@ -354,6 +413,77 @@ export default function ChatWorkspace({ ...@@ -354,6 +413,77 @@ export default function ChatWorkspace({
)} )}
/> />
</ChatAttachmentInput> </ChatAttachmentInput>
<Chip
className="rc-chat-agent-chip"
size="md"
variant="secondary"
>
{activeMode || '通用问答'}
</Chip>
<Autocomplete
aria-label="选择模型"
className="rc-chat-model-select"
isDisabled={
isLoading || modelListLoading || models.length === 0
}
selectedKey={selectedModel || null}
variant="secondary"
onSelectionChange={onModelChange}
>
<Autocomplete.Trigger title={selectedModelLabel}>
<Autocomplete.Value>
<span className="block min-w-0 truncate">
{selectedModelLabel}
</span>
</Autocomplete.Value>
<Autocomplete.Indicator>
<DownOutlined />
</Autocomplete.Indicator>
</Autocomplete.Trigger>
<Autocomplete.Popover placement="top start">
<Autocomplete.Filter filter={filterModel}>
<SearchField
aria-label="搜索模型"
fullWidth
variant="secondary"
>
<SearchField.Group>
<SearchField.SearchIcon>
<SearchOutlined />
</SearchField.SearchIcon>
<SearchField.Input
autoFocus
placeholder="输入模型名称搜索"
/>
<SearchField.ClearButton aria-label="清空搜索">
<CloseOutlined />
</SearchField.ClearButton>
</SearchField.Group>
</SearchField>
<ListBox
aria-label="可用模型"
items={models}
renderEmptyState={() => (
<div className="grid min-h-20 place-items-center px-3 py-4 text-sm text-muted">
暂无可用模型
</div>
)}
>
{(model) => (
<ListBox.Item id={model.id} textValue={model.label}>
<span className="min-w-0 flex-1 truncate">
{model.label}
</span>
{model.isDefault ? (
<span className="text-xs text-muted">默认</span>
) : null}
<ListBox.ItemIndicator />
</ListBox.Item>
)}
</ListBox>
</Autocomplete.Filter>
</Autocomplete.Popover>
</Autocomplete>
</PromptInput.ToolbarStart> </PromptInput.ToolbarStart>
<PromptInput.ToolbarEnd> <PromptInput.ToolbarEnd>
{files.some((file) => file.status === 'uploading') ? ( {files.some((file) => file.status === 'uploading') ? (
......
...@@ -2,9 +2,9 @@ import { useNavbarActions } from '@/components/AppShell'; ...@@ -2,9 +2,9 @@ import { useNavbarActions } from '@/components/AppShell';
import ChatWorkspace from '@/components/ChatWorkspace'; import ChatWorkspace from '@/components/ChatWorkspace';
import { createSseStream, useChat, useChatAgent } from '@/utils/chatRuntime'; import { createSseStream, useChat, useChatAgent } from '@/utils/chatRuntime';
import { import {
CommentOutlined,
DatabaseOutlined, DatabaseOutlined,
FileDoneOutlined, FileDoneOutlined,
FileSearchOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { toast } from '@heroui/react'; import { toast } from '@heroui/react';
import { useQuery } from '@umijs/max'; import { useQuery } from '@umijs/max';
...@@ -47,6 +47,7 @@ import { ...@@ -47,6 +47,7 @@ import {
getApplicationRuntimeConfig, getApplicationRuntimeConfig,
imageAnalyze, imageAnalyze,
llmChat, llmChat,
llmList,
queryMcps, queryMcps,
queryRecords, queryRecords,
queryTopics, queryTopics,
...@@ -63,7 +64,17 @@ import InvoiceAssistant from './invoice'; ...@@ -63,7 +64,17 @@ import InvoiceAssistant from './invoice';
const SYSTEM_PROMPT = `你是一个有用的的智能助手。 const SYSTEM_PROMPT = `你是一个有用的的智能助手。
如果用户提供了上下文,请根据上下文回答问题,不要试图创造一个答案。如果你不知道答案,请回答你只能根据上下文的内容回答问题。`; 如果用户提供了上下文,请根据上下文回答问题,不要试图创造一个答案。如果你不知道答案,请回答你只能根据上下文的内容回答问题。`;
const SELECTED_MODEL_STORAGE_KEY = 'ai_chat.selected_model';
const AGENT_DEFINITIONS = [ const AGENT_DEFINITIONS = [
{
key: 'general-chat',
icon: <CommentOutlined />,
label: '通用问答',
description: '自由交流、分析问题或协助写作,不读取业务知识库。',
prompt:
'你是通用 AI 助手。根据用户的问题直接回答,不引用或假设任何未提供的业务知识库内容。',
},
{ {
key: 'knowledge-copilot', key: 'knowledge-copilot',
icon: <DatabaseOutlined />, icon: <DatabaseOutlined />,
...@@ -75,7 +86,7 @@ const AGENT_DEFINITIONS = [ ...@@ -75,7 +86,7 @@ const AGENT_DEFINITIONS = [
{ {
key: 'audit-checklist', key: 'audit-checklist',
icon: <FileDoneOutlined />, icon: <FileDoneOutlined />,
label: '审计检查清单生成', label: '审计检查清单',
description: '基于审计风险点知识库,生成可执行的审计检查清单。', description: '基于审计风险点知识库,生成可执行的审计检查清单。',
prompt: prompt:
'你是审计检查清单生成智能体。只能依据系统提供的审计风险点数据库原文组织检查清单,不得添加原文没有的风险、法规、事实或建议;知识库依据不足时必须明确说明。', '你是审计检查清单生成智能体。只能依据系统提供的审计风险点数据库原文组织检查清单,不得添加原文没有的风险、法规、事实或建议;知识库依据不足时必须明确说明。',
...@@ -120,7 +131,7 @@ function normalizeTopic(topic) { ...@@ -120,7 +131,7 @@ function normalizeTopic(topic) {
function buildAgentPromptGroup(topics = []) { function buildAgentPromptGroup(topics = []) {
return { return {
key: 'agents', key: 'agents',
label: '智能体', label: '选择功能',
children: AGENT_DEFINITIONS.map((agent) => { children: AGENT_DEFINITIONS.map((agent) => {
const topic = const topic =
topics.find((item) => item.name === agent.topicName) || topics.find((item) => item.name === agent.topicName) ||
...@@ -128,34 +139,12 @@ function buildAgentPromptGroup(topics = []) { ...@@ -128,34 +139,12 @@ function buildAgentPromptGroup(topics = []) {
return { return {
...agent, ...agent,
initialContent:
agent.topicName === AUDIT_TOPIC.name ? undefined : agent.description,
topic, topic,
}; };
}), }),
}; };
} }
function buildTopicPromptGroup(topics = []) {
const normalizedTopics = topics.length > 0 ? topics : FALLBACK_TOPICS;
const firstTopic = normalizedTopics[0];
return {
key: 'topics',
label: '业务话题',
children: firstTopic
? [
{
key: firstTopic.name,
description: firstTopic.title,
icon: <FileSearchOutlined />,
topic: firstTopic,
},
]
: [],
};
}
function getKnowledgeBaseId(kb) { function getKnowledgeBaseId(kb) {
return kb?._id || kb?.id || kb?.name; return kb?._id || kb?.id || kb?.name;
} }
...@@ -225,6 +214,14 @@ function getConversationTopic(metadata = {}) { ...@@ -225,6 +214,14 @@ function getConversationTopic(metadata = {}) {
return null; return null;
} }
function getStoredLlmModel() {
try {
return localStorage.getItem(SELECTED_MODEL_STORAGE_KEY) || '';
} catch {
return '';
}
}
export default forwardRef((props, ref) => { export default forwardRef((props, ref) => {
const { userInfo, conversation, onAddConversation } = props; const { userInfo, conversation, onAddConversation } = props;
const conversationAgent = useMemo( const conversationAgent = useMemo(
...@@ -261,6 +258,7 @@ export default forwardRef((props, ref) => { ...@@ -261,6 +258,7 @@ export default forwardRef((props, ref) => {
const [attachedFiles, setAttachedFiles] = useState([]); const [attachedFiles, setAttachedFiles] = useState([]);
const [inputValue, setInputValue] = useState(''); const [inputValue, setInputValue] = useState('');
const [selectedModel, setSelectedModel] = useState(getStoredLlmModel);
const [auditKnowledgeOpen, setAuditKnowledgeOpen] = useState(false); const [auditKnowledgeOpen, setAuditKnowledgeOpen] = useState(false);
const [auditPromptOpen, setAuditPromptOpen] = useState(false); const [auditPromptOpen, setAuditPromptOpen] = useState(false);
const [auditKnowledgeLoading, setAuditKnowledgeLoading] = useState(false); const [auditKnowledgeLoading, setAuditKnowledgeLoading] = useState(false);
...@@ -280,11 +278,54 @@ export default forwardRef((props, ref) => { ...@@ -280,11 +278,54 @@ export default forwardRef((props, ref) => {
[configuredTopics], [configuredTopics],
); );
const promptGroups = useMemo(() => { const promptGroups = useMemo(() => {
return [buildAgentPromptGroup(configuredTopics)];
}, [configuredTopics]);
const { data: availableModels = [], isLoading: modelListLoading } = useQuery({
queryKey: ['llm-models'],
enabled: Boolean(userInfo),
staleTime: 5 * 60 * 1000,
queryFn: async () => {
try {
return await llmList();
} catch (error) {
const fallbackModel = await resolveLlmModel({ thinking: true });
if (!fallbackModel) throw error;
return [ return [
buildTopicPromptGroup(configuredTopics), {
buildAgentPromptGroup(configuredTopics), id: fallbackModel,
label: fallbackModel,
isDefault: true,
unavailable: true,
},
]; ];
}, [configuredTopics]); }
},
});
useEffect(() => {
if (!availableModels.length) return;
setSelectedModel((current) => {
const next = availableModels.some((model) => model.id === current)
? current
: availableModels.find((model) => model.isDefault)?.id ||
availableModels[0].id;
try {
localStorage.setItem(SELECTED_MODEL_STORAGE_KEY, next);
} catch {}
return next;
});
}, [availableModels]);
const selectModel = (key) => {
if (key === null || key === undefined) return;
const next = String(key);
setSelectedModel(next);
try {
localStorage.setItem(SELECTED_MODEL_STORAGE_KEY, next);
} catch {}
};
const loadAuditRecords = useCallback( const loadAuditRecords = useCallback(
async (force = false) => { async (force = false) => {
...@@ -787,10 +828,12 @@ export default forwardRef((props, ref) => { ...@@ -787,10 +828,12 @@ export default forwardRef((props, ref) => {
const content = JSON.stringify(requestMessages); const content = JSON.stringify(requestMessages);
const long = content.length > 900000; const long = content.length > 900000;
const model = await resolveLlmModel({ const model =
selectedModel ||
(await resolveLlmModel({
long, long,
thinking: true, thinking: true,
}); }));
const enable_search = conversationTopic?.metadata?.enable_search; const enable_search = conversationTopic?.metadata?.enable_search;
const enable_thinking = true; const enable_thinking = true;
const requestContext = { const requestContext = {
...@@ -1030,9 +1073,20 @@ export default forwardRef((props, ref) => { ...@@ -1030,9 +1073,20 @@ export default forwardRef((props, ref) => {
console.error(error); console.error(error);
toast.danger(error.message || '应用模型配置读取失败'); toast.danger(error.message || '应用模型配置读取失败');
}); });
const usesAuditKnowledge =
conversationAgent?.key === 'knowledge-copilot' ||
conversationAgent?.key === 'audit-checklist' ||
conversationTopic?.name === AUDIT_TOPIC.name;
if (usesAuditKnowledge) {
loadAuditRecords().catch(() => {}); loadAuditRecords().catch(() => {});
} }
}, [loadAuditRecords, userInfo]); }
}, [
conversationAgent?.key,
conversationTopic?.name,
loadAuditRecords,
userInfo,
]);
const navbarActions = useMemo( const navbarActions = useMemo(
() => () =>
...@@ -1241,6 +1295,7 @@ export default forwardRef((props, ref) => { ...@@ -1241,6 +1295,7 @@ export default forwardRef((props, ref) => {
return ( return (
<> <>
<ChatWorkspace <ChatWorkspace
activeMode={conversationAgent?.label || DEFAULT_AGENT.label}
canConfigure={ canConfigure={
Boolean(conversation) && Boolean(conversation) &&
(conversationAgent?.key === 'audit-checklist' || (conversationAgent?.key === 'audit-checklist' ||
...@@ -1256,6 +1311,8 @@ export default forwardRef((props, ref) => { ...@@ -1256,6 +1311,8 @@ export default forwardRef((props, ref) => {
isLoading={loading} isLoading={loading}
knowledgeBases={conversation?.metadata?.knowledgeBases || []} knowledgeBases={conversation?.metadata?.knowledgeBases || []}
messages={messages} messages={messages}
modelListLoading={modelListLoading}
models={availableModels}
questions={conversationTopic?.metadata?.questions || []} questions={conversationTopic?.metadata?.questions || []}
renderAssistantExtras={renderAssistantExtras} renderAssistantExtras={renderAssistantExtras}
suggestionGroups={!conversation ? promptGroups : []} suggestionGroups={!conversation ? promptGroups : []}
...@@ -1264,6 +1321,7 @@ export default forwardRef((props, ref) => { ...@@ -1264,6 +1321,7 @@ export default forwardRef((props, ref) => {
? conversationAgent?.label || conversationTopic?.title || '智能助手' ? conversationAgent?.label || conversationTopic?.title || '智能助手'
: undefined : undefined
} }
selectedModel={selectedModel}
userInfo={userInfo} userInfo={userInfo}
onConfigure={() => setAuditPromptOpen(true)} onConfigure={() => setAuditPromptOpen(true)}
onFilesSelected={handleFilesSelected} onFilesSelected={handleFilesSelected}
...@@ -1275,6 +1333,7 @@ export default forwardRef((props, ref) => { ...@@ -1275,6 +1333,7 @@ export default forwardRef((props, ref) => {
current.filter((item) => item.uid !== file.uid), current.filter((item) => item.uid !== file.uid),
) )
} }
onModelChange={selectModel}
onStop={() => abortController.current?.abort()} onStop={() => abortController.current?.abort()}
onSubmit={() => { onSubmit={() => {
const content = inputValue.trim(); const content = inputValue.trim();
......
...@@ -340,6 +340,50 @@ export async function getApplicationRuntimeConfig({ force = false } = {}) { ...@@ -340,6 +340,50 @@ export async function getApplicationRuntimeConfig({ force = false } = {}) {
return applicationConfigPromise; return applicationConfigPromise;
} }
function normalizeLlmList(payload) {
const candidates = [
payload,
payload?.data,
payload?.models,
payload?.records,
payload?.result,
payload?.result?.data,
];
const items = candidates.find(Array.isArray) || [];
const models = items
.map((item) => {
if (typeof item === 'string' && item.trim()) {
return { id: item.trim(), label: item.trim() };
}
if (!item || typeof item !== 'object') return null;
const id = firstNonEmptyString(
item.id,
item.model,
item.name,
item.value,
item.key,
);
if (!id) return null;
return {
...item,
id,
label:
firstNonEmptyString(
item.display_name,
item.displayName,
item.label,
item.title,
item.name,
) || id,
};
})
.filter(Boolean);
return [...new Map(models.map((model) => [model.id, model])).values()];
}
function resolveLlmUrl(llmConfig = {}) { function resolveLlmUrl(llmConfig = {}) {
const envBaseUrl = firstNonEmptyString( const envBaseUrl = firstNonEmptyString(
process.env.UMI_APP_LLM_API_BASE, process.env.UMI_APP_LLM_API_BASE,
...@@ -747,6 +791,23 @@ export async function llmChat(data, signal = undefined) { ...@@ -747,6 +791,23 @@ export async function llmChat(data, signal = undefined) {
return response; return response;
} }
export async function llmList(signal = undefined) {
const payload = await request('/api/llm/list', {
method: 'get',
signal,
});
const models = normalizeLlmList(payload);
if (!models.length) {
throw new Error('模型列表为空');
}
const defaultModel = await resolveLlmModel().catch(() => null);
return models.map((model) => ({
...model,
isDefault: model.id === defaultModel,
}));
}
export async function resolveLlmModel(options = {}) { export async function resolveLlmModel(options = {}) {
const runtimeConfig = await getApplicationRuntimeConfig(); const runtimeConfig = await getApplicationRuntimeConfig();
const models = runtimeConfig.llm?.models || {}; const models = runtimeConfig.llm?.models || {};
......
...@@ -522,14 +522,16 @@ body [data-theme='dark'] .login-page { ...@@ -522,14 +522,16 @@ body [data-theme='dark'] .login-page {
} }
.rc-chat-conversation-content { .rc-chat-conversation-content {
width: min(100%, 56rem); width: min(100%, 64rem);
margin-inline: auto; margin-inline: auto;
gap: 1rem; gap: 1rem;
padding-block: 2rem 1rem; padding-block: 2rem 1rem;
} }
.rc-chat-conversation-content--empty { .rc-chat-conversation-content--empty {
width: min(100%, 48rem); width: min(100%, 64rem);
gap: 2rem;
padding-block-start: 0.75rem;
} }
.rc-chat-context { .rc-chat-context {
...@@ -547,12 +549,19 @@ body [data-theme='dark'] .login-page { ...@@ -547,12 +549,19 @@ body [data-theme='dark'] .login-page {
} }
.rc-chat-suggestions { .rc-chat-suggestions {
width: 100%; width: min(100%, 62rem);
margin-inline: auto;
} }
.rc-chat-suggestion-items { .rc-chat-suggestion-items {
width: 100%; width: 100%;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 1.25rem;
}
.rc-chat-suggestion-items [data-slot='prompt-suggestion-item'] {
min-height: 12rem;
align-content: start;
} }
.rc-chat-suggestion-items--single { .rc-chat-suggestion-items--single {
...@@ -561,13 +570,51 @@ body [data-theme='dark'] .login-page { ...@@ -561,13 +570,51 @@ body [data-theme='dark'] .login-page {
.rc-chat-composer-stack { .rc-chat-composer-stack {
display: grid; display: grid;
width: min(100%, 48rem); width: min(100%, 64rem);
min-height: 0; min-height: 0;
flex: none; flex: none;
margin-inline: auto; margin-inline: auto;
gap: 0.75rem; gap: 0.75rem;
} }
.rc-chat-empty-state {
gap: 0.75rem;
}
.rc-chat-empty-state h1 {
margin: 0.5rem 0 0;
font-size: 1.75rem;
font-weight: 600;
line-height: 2.25rem;
}
.rc-chat-empty-state p {
margin: 0;
color: var(--muted);
font-size: 1rem;
line-height: 1.5rem;
}
.rc-chat-agent-icon {
color: var(--accent);
}
.rc-chat-agent-icon .anticon {
display: inline-flex;
font-size: 1.5rem;
line-height: 1;
}
.rc-chat-model-select {
width: 12rem;
}
.rc-chat-agent-chip {
height: 100%;
min-height: 2.25rem;
align-self: center;
}
.rc-invoice-workspace { .rc-invoice-workspace {
display: grid; display: grid;
height: 100%; height: 100%;
...@@ -602,4 +649,12 @@ body [data-theme='dark'] .login-page { ...@@ -602,4 +649,12 @@ body [data-theme='dark'] .login-page {
.rc-chat-suggestion-items { .rc-chat-suggestion-items {
grid-template-columns: minmax(0, 1fr); grid-template-columns: minmax(0, 1fr);
} }
.rc-chat-agent-chip {
min-height: 2.5rem;
}
.rc-chat-conversation-content--empty {
padding-block-start: 2rem;
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment