refactor: merge agent workbench into chat

This commit is contained in:
boos
2026-06-29 10:56:07 +08:00
parent cd08ac931d
commit 9ae9224669
9 changed files with 32 additions and 112 deletions
@@ -2,19 +2,17 @@
## Scope ## Scope
This deliverable adds the desktop UI shell and local interactions for Agent Workbench, and connects Agent mode to the existing desktop Agent command path. It intentionally avoids new Rust commands, database tables, billing, and remote media generation until the page contract is stable. This deliverable adds the desktop Chat empty-state creator composer and local interactions for Agent Workbench, and connects Agent mode to the existing desktop Agent command path. It intentionally avoids new Rust commands, database tables, billing, and remote media generation until the page contract is stable.
## Frontend Boundaries ## Frontend Boundaries
- `types/index.ts` owns the `PageKey` addition. - `components/chat/AgentWorkbenchComposer.tsx` owns the creator composer, local attachment state, mode state, model selection, Agent handoff, and placeholder result/status for media modes.
- `components/layout/Sidebar.tsx` owns navigation entry placement. - `components/chat/ChatView.tsx` renders the composer in the Chat empty state and keeps the normal Chat input for existing conversations.
- `components/layout/ContentArea.tsx` owns routing from `PageKey` to page component.
- `pages/AgentWorkbenchPage.tsx` owns the creator composer, local attachment state, mode state, model selection, Agent handoff, and placeholder result/status for media modes.
- `stores/providerStore.ts` remains the source of provider/model data. The page reads `providers` and calls `fetchProviders`; it does not duplicate provider fetching logic. - `stores/providerStore.ts` remains the source of provider/model data. The page reads `providers` and calls `fetchProviders`; it does not duplicate provider fetching logic.
- `stores/conversationStore.ts` remains the source for conversation creation and Agent streaming. The Workbench reuses `createConversation`, `updateConversation`, and `sendAgentMessage`. - `stores/conversationStore.ts` remains the source for conversation creation and Agent streaming. The Workbench reuses `createConversation`, `updateConversation`, and `sendAgentMessage`.
- `stores/skillStore.ts` remains the source for skill installation/listing. The Workbench reuses `loadSkills`, `installSkill`, and `toggleSkill` for third-party skills. - `stores/skillStore.ts` remains the source for skill installation/listing. The Workbench reuses `loadSkills`, `installSkill`, and `toggleSkill` for third-party skills.
- `lib/browserMock.ts` mirrors skill install/list/toggle enough for browser preview. - `lib/browserMock.ts` mirrors skill install/list/toggle enough for browser preview.
- `i18n/locales/*.json` owns navigation labels. The first page UI can use Chinese literal copy to match current product direction, but nav should use locale keys. - The first composer UI can use Chinese literal copy to match current product direction; it does not add a standalone nav label.
## Data Flow ## Data Flow
@@ -1,7 +1,7 @@
# Implementation Plan # Implementation Plan
1. Add `agent-workbench` to desktop page types, sidebar nav, content routing, and nav locale files. 1. Integrate Agent Workbench into the existing Chat empty state instead of adding a standalone `agent-workbench` page/nav module.
2. Create `AgentWorkbenchPage.tsx` with: 2. Create `components/chat/AgentWorkbenchComposer.tsx` with:
- mode definitions - mode definitions
- provider-derived model options - provider-derived model options
- custom model fallback - custom model fallback
@@ -30,3 +30,4 @@
- `pnpm --dir desktop exec tsc --noEmit` passed after bundling built-in skills. - `pnpm --dir desktop exec tsc --noEmit` passed after bundling built-in skills.
- `pnpm --dir desktop run build` passed after bundling built-in skills. - `pnpm --dir desktop run build` passed after bundling built-in skills.
- `cargo check -p open-agent-sdk --manifest-path desktop/src-tauri/Cargo.toml` passed after bundling built-in skills. - `cargo check -p open-agent-sdk --manifest-path desktop/src-tauri/Cargo.toml` passed after bundling built-in skills.
- `pnpm --dir desktop exec tsc --noEmit` passed after merging Agent Workbench into Chat.
@@ -2,23 +2,23 @@
## Goal ## Goal
Add a first usable Agent Workbench entry to the desktop client so users can start from the same creator-style surface used in the BoosAPI web UI: choose a creation mode, select a model, attach references, and type a prompt. Merge the first usable Agent Workbench creator surface into the desktop Chat entry so users start from one conversation-first screen: choose a creation mode, select a model, attach references, and type a prompt.
## Requirements ## Requirements
- Add an Agent Workbench page to the desktop navigation without disrupting existing Chat, Drawing, Skills, Knowledge, Memory, Gateway, Files, or Settings flows. - Integrate the Agent Workbench composer into the existing Chat page instead of adding a separate Agent navigation module.
- The first screen should match the creator-composer direction: centered prompt, large input surface, upload affordance, creation mode selector, auto toggle, model selector, skills button, subject button, and submit button. - The first screen should match the creator-composer direction: centered prompt, large input surface, upload affordance, creation mode selector, auto toggle, model selector, skills button, subject button, and submit button.
- Creation modes must include at least Agent, image generation, video generation, music generation, voice generation, digital human, and action imitation. Unsupported modes may show “coming soon” copy but must remain selectable. - Creation modes must include at least Agent, image generation, video generation, music generation, voice generation, digital human, and action imitation. Unsupported modes may show “coming soon” copy but must remain selectable.
- Agent mode should expose a conversation model selector populated from existing enabled desktop provider models where possible, with a custom model fallback. - Agent mode should expose a conversation model selector populated from existing enabled desktop provider models where possible, with a custom model fallback.
- Upload affordance should accept local reference files, display removable attachment chips, and pass attachments into existing Agent chat flow when Agent mode submits. - Upload affordance should accept local reference files, display removable attachment chips, and pass attachments into existing Agent chat flow when Agent mode submits.
- The page must follow the desktop app’s existing React/Ant Design/Zustand conventions and be reachable from the sidebar. - The composer must follow the desktop app’s existing React/Ant Design/Zustand conventions and remain reachable through the Chat sidebar entry.
- The implementation must avoid new backend schema. Existing Tauri commands may be reused. - The implementation must avoid new backend schema. Existing Tauri commands may be reused.
- The Workbench skills menu should expose a JianYing/剪映 automation entry that installs/enables `luoluoluo22/jianying-editor-skill` through the existing Desktop skills system and primes the Agent prompt. - The Workbench skills menu should expose a JianYing/剪映 automation entry that installs/enables `luoluoluo22/jianying-editor-skill` through the existing Desktop skills system and primes the Agent prompt.
- Desktop should bundle the first-party curated skill pack in the project so JianYing, Seedance, DWG/CAD, and PPT skills are available even when the online `skills.sh` market does not return them. - Desktop should bundle the first-party curated skill pack in the project so JianYing, Seedance, DWG/CAD, and PPT skills are available even when the online `skills.sh` market does not return them.
## Acceptance Criteria ## Acceptance Criteria
- [x] Sidebar contains an Agent Workbench entry and selecting it renders the new page. - [x] Chat empty state contains the Agent Workbench composer; no separate Agent Workbench sidebar entry is required.
- [x] Agent Workbench page has a centered creator composer with mode dropdown, model control, upload button, `@` subject popover, and submit button. - [x] Agent Workbench page has a centered creator composer with mode dropdown, model control, upload button, `@` subject popover, and submit button.
- [x] Available chat models are derived from existing enabled desktop providers/models, with a custom model option. - [x] Available chat models are derived from existing enabled desktop providers/models, with a custom model option.
- [x] Selecting a mode updates the composer label and mode-specific model controls where applicable. - [x] Selecting a mode updates the composer label and mode-specific model controls where applicable.
@@ -18,7 +18,7 @@ import {
X, X,
} from 'lucide-react'; } from 'lucide-react';
import { invoke } from '@/lib/invoke'; import { invoke } from '@/lib/invoke';
import { useConversationStore, useProviderStore, useSkillStore, useUIStore } from '@/stores'; import { useConversationStore, useProviderStore, useSkillStore } from '@/stores';
import type { AttachmentInput, Model } from '@/types'; import type { AttachmentInput, Model } from '@/types';
type CreationMode = 'agent' | 'image' | 'video' | 'music' | 'voice' | 'digital-human' | 'action'; type CreationMode = 'agent' | 'image' | 'video' | 'music' | 'voice' | 'digital-human' | 'action';
@@ -120,7 +120,7 @@ function toAttachmentInput(item: AttachmentItem): AttachmentInput {
}; };
} }
export function AgentWorkbenchPage() { export function AgentWorkbenchComposer() {
const { token } = theme.useToken(); const { token } = theme.useToken();
const { message } = App.useApp(); const { message } = App.useApp();
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
@@ -133,7 +133,6 @@ export function AgentWorkbenchPage() {
const loadSkills = useSkillStore((s) => s.loadSkills); const loadSkills = useSkillStore((s) => s.loadSkills);
const installSkill = useSkillStore((s) => s.installSkill); const installSkill = useSkillStore((s) => s.installSkill);
const toggleSkill = useSkillStore((s) => s.toggleSkill); const toggleSkill = useSkillStore((s) => s.toggleSkill);
const setActivePage = useUIStore((s) => s.setActivePage);
const [creationMode, setCreationMode] = useState<CreationMode>('agent'); const [creationMode, setCreationMode] = useState<CreationMode>('agent');
const [autoMode, setAutoMode] = useState(true); const [autoMode, setAutoMode] = useState(true);
const [prompt, setPrompt] = useState(''); const [prompt, setPrompt] = useState('');
@@ -292,7 +291,6 @@ export function AgentWorkbenchPage() {
setResult(`Agent 已开始执行。模型:${modelId};参考素材:${attachments.length} 个。`); setResult(`Agent 已开始执行。模型:${modelId};参考素材:${attachments.length} 个。`);
setPrompt(''); setPrompt('');
setAttachments([]); setAttachments([]);
setActivePage('chat');
} catch (error) { } catch (error) {
message.error(String(error)); message.error(String(error));
setResult(`Agent 启动失败:${String(error)}`); setResult(`Agent 启动失败:${String(error)}`);
@@ -312,7 +310,6 @@ export function AgentWorkbenchPage() {
selectedChatModel, selectedChatModel,
selectedModel, selectedModel,
sendAgentMessage, sendAgentMessage,
setActivePage,
submitting, submitting,
updateConversation, updateConversation,
]); ]);
+5 -70
View File
@@ -2,16 +2,13 @@ import React, { useMemo, useCallback, useRef, useState, useEffect, useSyncExtern
import { CloseCircleFilled, SyncOutlined } from '@ant-design/icons'; import { CloseCircleFilled, SyncOutlined } from '@ant-design/icons';
import { Typography, Button, Dropdown, Input, App, Avatar, Alert, Popconfirm, Popover, theme, Tag, Image, Tooltip, Modal, Spin } from 'antd'; import { Typography, Button, Dropdown, Input, App, Avatar, Alert, Popconfirm, Popover, theme, Tag, Image, Tooltip, Modal, Spin } from 'antd';
import type { InputRef } from 'antd'; import type { InputRef } from 'antd';
import { Pencil, Share2, FileImage, FileCode, FileText, FileType, Bot, Brain, Lightbulb, Code, Languages, Copy, Check, RotateCcw, User, Trash2, ChevronLeft, ChevronRight, ChevronDown, Scissors, Paperclip, AlertCircle, X, ArrowDown, ArrowUp, ArrowLeftRight, Zap, Sparkles, TextCursorInput, GitBranch, ChartNoAxesColumn, MessageSquare, ArrowUpRight, ArrowDownRight, Coins, Clock, Timer, Download, PanelLeftClose, PanelLeftOpen } from 'lucide-react'; import { Pencil, Share2, FileImage, FileCode, FileText, FileType, Bot, Brain, Code, Copy, Check, RotateCcw, User, Trash2, ChevronLeft, ChevronRight, ChevronDown, Scissors, Paperclip, AlertCircle, X, ArrowDown, ArrowUp, ArrowLeftRight, Zap, Sparkles, TextCursorInput, GitBranch, ChartNoAxesColumn, MessageSquare, ArrowUpRight, ArrowDownRight, Coins, Clock, Timer, Download, PanelLeftClose, PanelLeftOpen } from 'lucide-react';
import { ModelIcon } from '@lobehub/icons'; import { ModelIcon } from '@lobehub/icons';
import { getConvIcon } from '@/lib/convIcon'; import { getConvIcon } from '@/lib/convIcon';
import { normalizeAutoConversationTitle } from '@/lib/conversationTitle';
import Bubble from '@ant-design/x/es/bubble'; import Bubble from '@ant-design/x/es/bubble';
import Prompts from '@ant-design/x/es/prompts';
import Actions from '@ant-design/x/es/actions'; import Actions from '@ant-design/x/es/actions';
import Think from '@ant-design/x/es/think'; import Think from '@ant-design/x/es/think';
import type { BubbleItemType, BubbleListRef, RoleType } from '@ant-design/x/es/bubble/interface'; import type { BubbleItemType, BubbleListRef, RoleType } from '@ant-design/x/es/bubble/interface';
import type { PromptsItemType } from '@ant-design/x/es/prompts';
import NodeRenderer, { setCustomComponents, withMarkstreamComponentDisplay, type NodeComponentProps, type CodeBlockActionContext, type CodeBlockPreviewPayload, type MermaidBlockActionContext, type InfographicBlockActionContext } from 'markstream-react'; import NodeRenderer, { setCustomComponents, withMarkstreamComponentDisplay, type NodeComponentProps, type CodeBlockActionContext, type CodeBlockPreviewPayload, type MermaidBlockActionContext, type InfographicBlockActionContext } from 'markstream-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CodeBlockHeaderActions } from './CodeBlockHeaderActions'; import { CodeBlockHeaderActions } from './CodeBlockHeaderActions';
@@ -33,6 +30,7 @@ import { useUserProfileStore } from '@/stores/userProfileStore';
import { useResolvedDarkMode } from '@/hooks/useResolvedDarkMode'; import { useResolvedDarkMode } from '@/hooks/useResolvedDarkMode';
import { InputArea } from './InputArea'; import { InputArea } from './InputArea';
import { ModelSelector } from './ModelSelector'; import { ModelSelector } from './ModelSelector';
import { AgentWorkbenchComposer } from './AgentWorkbenchComposer';
import { parseSearchContent } from '@/lib/searchUtils'; import { parseSearchContent } from '@/lib/searchUtils';
import { import {
CHAT_CUSTOM_HTML_TAGS, CHAT_CUSTOM_HTML_TAGS,
@@ -2111,7 +2109,6 @@ export function ChatView() {
const [mermaidPreviewSvg, setMermaidPreviewSvg] = useState<string | null>(null); const [mermaidPreviewSvg, setMermaidPreviewSvg] = useState<string | null>(null);
const [mermaidPreviewOpen, setMermaidPreviewOpen] = useState(false); const [mermaidPreviewOpen, setMermaidPreviewOpen] = useState(false);
const [streamStatusNow, setStreamStatusNow] = useState(() => Date.now()); const [streamStatusNow, setStreamStatusNow] = useState(() => Date.now());
const createConversation = useConversationStore((s) => s.createConversation);
const providers = useProviderStore((s) => s.providers); const providers = useProviderStore((s) => s.providers);
const settings = useSettingsStore((s) => s.settings); const settings = useSettingsStore((s) => s.settings);
const saveSettings = useSettingsStore((s) => s.saveSettings); const saveSettings = useSettingsStore((s) => s.saveSettings);
@@ -2780,62 +2777,6 @@ export function ChatView() {
[messages, activeConversation, t, messageApi], [messages, activeConversation, t, messageApi],
); );
// ── Welcome prompt items ───────────────────────────────────────────
const greetingText = useMemo(() => {
const hour = new Date().getHours();
let key: string;
if (hour >= 5 && hour < 12) key = 'chat.greetingMorning';
else if (hour >= 12 && hour < 14) key = 'chat.greetingNoon';
else if (hour >= 14 && hour < 18) key = 'chat.greetingAfternoon';
else key = 'chat.greetingEvening';
return `👋 ${t(key)}`;
}, [t]);
const promptItems: PromptsItemType[] = useMemo(
() => [
{ key: '1', icon: <Lightbulb size={16} />, label: t('chat.welcomePrompt1') },
{ key: '2', icon: <Languages size={16} />, label: t('chat.welcomePrompt2') },
{ key: '3', icon: <Code size={16} />, label: t('chat.welcomePrompt3') },
{ key: '4', icon: <Lightbulb size={16} />, label: t('chat.welcomePrompt4') },
],
[t],
);
const handlePromptClick = useCallback(
async (info: { data: PromptsItemType }) => {
const text = typeof info.data.label === 'string' ? info.data.label : '';
if (!text) return;
try {
if (!activeConversationId) {
// Prefer settings default model, fall back to first enabled
let provider = settings.default_provider_id
? providers.find((p) => p.id === settings.default_provider_id && p.enabled)
: undefined;
let model = provider?.models.find(
(m) => m.model_id === settings.default_model_id && m.enabled,
);
if (!provider || !model) {
provider = providers.find((p) => p.enabled && p.models.some((m) => m.enabled));
model = provider?.models.find((m) => m.enabled);
}
if (!provider || !model) {
messageApi.warning(t('chat.noModel'));
return;
}
await createConversation(normalizeAutoConversationTitle(text), model.model_id, provider.id);
}
// Route through InputArea's send pipeline so companion models are respected
useConversationStore.getState().setPendingPromptText(text);
} catch (e) {
console.error('[handlePromptClick] error:', e);
messageApi.error(String(e));
}
},
[activeConversationId, providers, settings, createConversation, messageApi, t],
);
// ── Bubble items (only show active messages) ──────────────────────── // ── Bubble items (only show active messages) ────────────────────────
const activeMessages = useMemo( const activeMessages = useMemo(
() => messages.filter((msg) => msg.is_active !== false), () => messages.filter((msg) => msg.is_active !== false),
@@ -4201,15 +4142,7 @@ export function ChatView() {
</div> </div>
) : ( ) : (
<div className="flex flex-col items-center justify-center h-full" style={{ padding: '0 24px' }}> <div className="flex flex-col items-center justify-center h-full" style={{ padding: '0 24px' }}>
<Typography.Title level={3} style={{ marginBottom: 24, fontWeight: 500 }}> <AgentWorkbenchComposer />
{greetingText}
</Typography.Title>
<Prompts
items={promptItems}
onItemClick={handlePromptClick}
wrap
style={{ marginTop: 16 }}
/>
</div> </div>
) )
) : ( ) : (
@@ -4254,6 +4187,7 @@ export function ChatView() {
)} )}
{/* Input Area */} {/* Input Area */}
{messages.length > 0 && (
<div className="relative"> <div className="relative">
{showScrollToBottom && ( {showScrollToBottom && (
<Button <Button
@@ -4275,6 +4209,7 @@ export function ChatView() {
)} )}
<InputArea /> <InputArea />
</div> </div>
)}
<Modal <Modal
title={t('chat.compressionSummary')} title={t('chat.compressionSummary')}
open={summaryModalOpen} open={summaryModalOpen}
@@ -1,5 +1,4 @@
import type { PageKey } from '@/types'; import type { PageKey } from '@/types';
import { AgentWorkbenchPage } from '@/pages/AgentWorkbenchPage';
import { ChatPage } from '@/pages/ChatPage'; import { ChatPage } from '@/pages/ChatPage';
import { DrawingPage } from '@/pages/DrawingPage'; import { DrawingPage } from '@/pages/DrawingPage';
import { KnowledgePage } from '@/pages/KnowledgePage'; import { KnowledgePage } from '@/pages/KnowledgePage';
@@ -17,8 +16,6 @@ export function ContentArea({ activePage }: ContentAreaProps) {
switch (activePage) { switch (activePage) {
case 'chat': case 'chat':
return <ChatPage />; return <ChatPage />;
case 'agent-workbench':
return <AgentWorkbenchPage />;
case 'drawing': case 'drawing':
return <DrawingPage />; return <DrawingPage />;
case 'knowledge': case 'knowledge':
+1 -2
View File
@@ -1,6 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { Tooltip, Avatar, theme } from 'antd'; import { Tooltip, Avatar, theme } from 'antd';
import { MessageSquare, BookOpen, Brain, Router, FolderOpen, User, Sparkles, ImagePlus, WandSparkles } from 'lucide-react'; import { MessageSquare, BookOpen, Brain, Router, FolderOpen, User, Sparkles, ImagePlus } from 'lucide-react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useUIStore, useSettingsStore } from '@/stores'; import { useUIStore, useSettingsStore } from '@/stores';
import { useUserProfileStore } from '@/stores/userProfileStore'; import { useUserProfileStore } from '@/stores/userProfileStore';
@@ -12,7 +12,6 @@ import type { PageKey } from '@/types';
const mainNavItems: { key: PageKey; icon: React.ReactNode; labelKey: string }[] = [ const mainNavItems: { key: PageKey; icon: React.ReactNode; labelKey: string }[] = [
{ key: 'chat', icon: <MessageSquare size={18} />, labelKey: 'nav.chat' }, { key: 'chat', icon: <MessageSquare size={18} />, labelKey: 'nav.chat' },
{ key: 'agent-workbench', icon: <WandSparkles size={18} />, labelKey: 'nav.agentWorkbench' },
{ key: 'drawing', icon: <ImagePlus size={18} />, labelKey: 'nav.drawing' }, { key: 'drawing', icon: <ImagePlus size={18} />, labelKey: 'nav.drawing' },
{ key: 'skills', icon: <Sparkles size={18} />, labelKey: 'nav.skills' }, { key: 'skills', icon: <Sparkles size={18} />, labelKey: 'nav.skills' },
{ key: 'knowledge', icon: <BookOpen size={18} />, labelKey: 'nav.knowledge' }, { key: 'knowledge', icon: <BookOpen size={18} />, labelKey: 'nav.knowledge' },
+13 -19
View File
@@ -31,25 +31,19 @@ body {
.agent-workbench-desktop { .agent-workbench-desktop {
position: relative; position: relative;
height: 100%; width: 100%;
overflow: hidden; max-width: 1380px;
overflow: visible;
color: var(--color-text); color: var(--color-text);
background: transparent !important;
} }
.agent-workbench-desktop-bg { .agent-workbench-desktop-bg {
position: absolute; display: none;
inset: 0;
background:
linear-gradient(180deg, rgba(247, 249, 251, 0.96), rgba(248, 250, 252, 0.98)),
radial-gradient(circle at 18% 18%, rgba(22, 119, 255, 0.08), transparent 28%),
radial-gradient(circle at 82% 78%, rgba(20, 184, 166, 0.08), transparent 30%);
} }
html[data-theme="dark"] .agent-workbench-desktop-bg { html[data-theme="dark"] .agent-workbench-desktop-bg {
background: display: none;
linear-gradient(180deg, rgba(12, 18, 24, 0.98), rgba(14, 18, 22, 0.98)),
radial-gradient(circle at 18% 18%, rgba(22, 119, 255, 0.12), transparent 28%),
radial-gradient(circle at 82% 78%, rgba(20, 184, 166, 0.1), transparent 30%);
} }
.agent-workbench-desktop-hero { .agent-workbench-desktop-hero {
@@ -57,15 +51,15 @@ html[data-theme="dark"] .agent-workbench-desktop-bg {
display: grid; display: grid;
place-items: center; place-items: center;
align-content: center; align-content: center;
gap: 54px; gap: 34px;
height: 100%; width: 100%;
padding: 48px 24px; padding: 0 24px;
} }
.agent-workbench-desktop-hero h1 { .agent-workbench-desktop-hero h1 {
margin: 0; margin: 0;
color: var(--color-text); color: var(--color-text);
font-size: clamp(28px, 4vw, 42px); font-size: 32px;
font-weight: 780; font-weight: 780;
letter-spacing: 0; letter-spacing: 0;
} }
@@ -76,13 +70,13 @@ html[data-theme="dark"] .agent-workbench-desktop-bg {
grid-template-columns: 96px minmax(0, 1fr); grid-template-columns: 96px minmax(0, 1fr);
grid-template-rows: minmax(118px, auto) auto auto; grid-template-rows: minmax(118px, auto) auto auto;
gap: 10px 18px; gap: 10px 18px;
width: min(78vw, 1280px); width: min(72vw, 1180px);
min-height: 232px; min-height: 232px;
padding: 24px; padding: 24px;
border: 1px solid color-mix(in srgb, var(--border-color) 86%, transparent); border: 1px solid color-mix(in srgb, var(--border-color) 86%, transparent);
border-radius: 26px; border-radius: 22px;
background: color-mix(in srgb, var(--color-bg-elevated) 94%, transparent); background: color-mix(in srgb, var(--color-bg-elevated) 94%, transparent);
box-shadow: 0 24px 80px rgba(15, 23, 42, 0.1); box-shadow: 0 24px 80px rgba(15, 23, 42, 0.08);
} }
.agent-workbench-file-input { .agent-workbench-file-input {
-1
View File
@@ -658,7 +658,6 @@ export interface RealtimeConfig {
// === UI State === // === UI State ===
export type PageKey = export type PageKey =
| 'chat' | 'chat'
| 'agent-workbench'
| 'drawing' | 'drawing'
| 'knowledge' | 'knowledge'
| 'memory' | 'memory'