feat: add desktop document skills workspace

This commit is contained in:
boos
2026-06-29 14:35:34 +08:00
parent 2b181344b2
commit 258f4c1a00
11 changed files with 537 additions and 96 deletions
@@ -1,6 +1,6 @@
import { useState, useCallback, useMemo, useRef, useEffect } from 'react';
import { Modal, Input, List, Tag, Typography, theme } from 'antd';
import { Search, MessageSquare, Settings, Network, Plus, PanelLeftClose, Sparkles, ImagePlus } from 'lucide-react';
import { Search, MessageSquare, Settings, Plus, PanelLeftClose, Sparkles, ImagePlus, FileText } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useUIStore } from '@/stores';
import { formatShortcutForDisplay } from '@/lib/shortcuts';
@@ -61,11 +61,11 @@ export default function CommandPalette({ open, onClose }: CommandPaletteProps) {
action: () => { setActivePage('drawing'); onClose(); },
},
{
id: 'go-gateway',
label: t('commandPalette.goToGateway'),
icon: <Network size={16} />,
id: 'go-documents',
label: t('commandPalette.goToDocuments'),
icon: <FileText size={16} />,
category: nav,
action: () => { setActivePage('gateway'); onClose(); },
action: () => { setActivePage('documents'); onClose(); },
},
{
id: 'go-skills',
@@ -3,10 +3,10 @@ import { ChatPage } from '@/pages/ChatPage';
import { DrawingPage } from '@/pages/DrawingPage';
import { KnowledgePage } from '@/pages/KnowledgePage';
import { MemoryPage } from '@/pages/MemoryPage';
import { GatewayPage } from '@/pages/GatewayPage';
import { FilesPage } from '@/pages/FilesPage';
import { SettingsPage } from '@/pages/SettingsPage';
import { SkillsPage } from '@/pages/SkillsPage';
import { DocumentsPage } from '@/pages/DocumentsPage';
interface ContentAreaProps {
activePage: PageKey;
@@ -22,14 +22,14 @@ export function ContentArea({ activePage }: ContentAreaProps) {
return <KnowledgePage />;
case 'memory':
return <MemoryPage />;
case 'gateway':
return <GatewayPage />;
case 'files':
return <FilesPage />;
case 'settings':
return <SettingsPage />;
case 'skills':
return <SkillsPage />;
case 'documents':
return <DocumentsPage />;
default: {
const _exhaustive: never = activePage;
throw new Error(`Unhandled page key: ${_exhaustive}`);
+4 -15
View File
@@ -1,11 +1,9 @@
import { useState } from 'react';
import { Tooltip, Avatar, theme } from 'antd';
import { MessageSquare, BookOpen, Brain, Router, FolderOpen, User, Sparkles, ImagePlus } from 'lucide-react';
import { MessageSquare, BookOpen, Brain, FolderOpen, User, Sparkles, ImagePlus, FileText } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useUIStore, useSettingsStore } from '@/stores';
import { useUIStore } from '@/stores';
import { useUserProfileStore } from '@/stores/userProfileStore';
import { getShortcutBinding, formatShortcutForDisplay } from '@/lib/shortcuts';
import type { ShortcutAction } from '@/lib/shortcuts';
import { useResolvedAvatarSrc } from '@/hooks/useResolvedAvatarSrc';
import { UserProfileModal } from './UserProfileModal';
import type { PageKey } from '@/types';
@@ -14,9 +12,9 @@ const mainNavItems: { key: PageKey; icon: React.ReactNode; labelKey: string }[]
{ key: 'chat', icon: <MessageSquare size={18} />, labelKey: 'nav.chat' },
{ key: 'drawing', icon: <ImagePlus size={18} />, labelKey: 'nav.drawing' },
{ key: 'skills', icon: <Sparkles size={18} />, labelKey: 'nav.skills' },
{ key: 'documents', icon: <FileText size={18} />, labelKey: 'nav.documents' },
{ key: 'knowledge', icon: <BookOpen size={18} />, labelKey: 'nav.knowledge' },
{ key: 'memory', icon: <Brain size={18} />, labelKey: 'nav.memory' },
{ key: 'gateway', icon: <Router size={18} />, labelKey: 'nav.gateway' },
{ key: 'files', icon: <FolderOpen size={18} />, labelKey: 'nav.files' },
];
@@ -28,21 +26,12 @@ export function Sidebar() {
const profile = useUserProfileStore((s) => s.profile);
const [profileModalOpen, setProfileModalOpen] = useState(false);
const resolvedAvatarSrc = useResolvedAvatarSrc(profile.avatarType, profile.avatarValue);
const settings = useSettingsStore((s) => s.settings);
const NAV_SHORTCUT_MAP: Partial<Record<PageKey, ShortcutAction>> = {
gateway: 'toggleGateway',
};
const renderNavButton = (item: { key: PageKey; icon: React.ReactNode; labelKey: string }) => {
const isActive = activePage === item.key;
const label = t(item.labelKey);
const action = NAV_SHORTCUT_MAP[item.key];
const title = action
? `${label} (${formatShortcutForDisplay(getShortcutBinding(settings, action))})`
: label;
return (
<Tooltip key={item.key} title={title} placement="right">
<Tooltip key={item.key} title={label} placement="right">
<button
onClick={() => setActivePage(item.key)}
className="flex items-center justify-center text-base transition-colors"