mirror of
https://github.com/lobehub/lobehub
synced 2026-04-21 17:47:27 +00:00
* refactor: migrate theme management to `next-themes` and remove theme from route variants and global store. Signed-off-by: Innei <tukon479@gmail.com> * refactor: Unify theme mode to 'system' instead of 'auto' and streamline Electron theme synchronization. Signed-off-by: Innei <tukon479@gmail.com> * refactor: Remove LOBE_THEME_APPEARANCE constant and simplify desktop theme source assignment. Signed-off-by: Innei <tukon479@gmail.com> * chore: Update antd-style dependency from npm alias to specific alpha version. Signed-off-by: Innei <tukon479@gmail.com> * chore: update pnpm lockfile Signed-off-by: Innei <tukon479@gmail.com> * feat: Default theme to system and update Next.js RSC payload path example. Signed-off-by: Innei <tukon479@gmail.com> * feat: add `dev:static` script for static renderer development Signed-off-by: Innei <tukon479@gmail.com> * refactor: replace useThemeMode with custom useIsDark hook for theme detection and add ClientOnly component Signed-off-by: Innei <tukon479@gmail.com> * refactor: Remove `extractStaticStyle` import and cache prop from `StyleRegistry`. Signed-off-by: Innei <tukon479@gmail.com> * chore: Remove debug console log for current appearance. Signed-off-by: Innei <tukon479@gmail.com> * fix: Migrate legacy 'auto' theme mode to 'system' and refine theme background CSS selectors. Signed-off-by: Innei <tukon479@gmail.com> * feat: Add window dragging to desktop onboarding layout and update antd-style dependency. * refactor: Refine global background styling to target body elements, remove token-based background, and clean up debugging script. Signed-off-by: Innei <tukon479@gmail.com> --------- Signed-off-by: Innei <tukon479@gmail.com>
218 lines
6 KiB
TypeScript
218 lines
6 KiB
TypeScript
import { useDebounce } from 'ahooks';
|
|
import { useTheme as useNextThemesTheme } from 'next-themes';
|
|
import { useEffect, useMemo } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import useSWR from 'swr';
|
|
|
|
import { useCreateMenuItems } from '@/app/[variants]/(main)/home/_layout/hooks';
|
|
import type { SearchResult } from '@/database/repositories/search';
|
|
import { useCreateNewModal } from '@/features/LibraryModal';
|
|
import { useGroupWizard } from '@/layout/GlobalProvider/GroupWizardProvider';
|
|
import { lambdaClient } from '@/libs/trpc/client';
|
|
import { useAgentStore } from '@/store/agent';
|
|
import { builtinAgentSelectors } from '@/store/agent/selectors/builtinAgentSelectors';
|
|
import { useChatStore } from '@/store/chat';
|
|
import { useGlobalStore } from '@/store/global';
|
|
import { globalHelpers } from '@/store/global/helpers';
|
|
import { useHomeStore } from '@/store/home';
|
|
|
|
import { useCommandMenuContext } from './CommandMenuContext';
|
|
import type { ThemeMode } from './types';
|
|
|
|
/**
|
|
* Shared methods for CommandMenu
|
|
*/
|
|
export const useCommandMenu = () => {
|
|
const [open, setOpen] = useGlobalStore((s) => [s.status.showCommandMenu, s.updateSystemStatus]);
|
|
const {
|
|
mounted,
|
|
search,
|
|
setSearch,
|
|
pages,
|
|
setPages,
|
|
typeFilter,
|
|
setTypeFilter,
|
|
page,
|
|
menuContext: context,
|
|
pathname,
|
|
} = useCommandMenuContext();
|
|
|
|
const navigate = useNavigate();
|
|
const { setTheme } = useNextThemesTheme();
|
|
const createAgent = useAgentStore((s) => s.createAgent);
|
|
const refreshAgentList = useHomeStore((s) => s.refreshAgentList);
|
|
const inboxAgentId = useAgentStore(builtinAgentSelectors.inboxAgentId);
|
|
const { openGroupWizard } = useGroupWizard();
|
|
const { createGroupWithMembers, createGroupFromTemplate, createPage } = useCreateMenuItems();
|
|
const { open: openCreateLibraryModal } = useCreateNewModal();
|
|
|
|
// Extract agentId from pathname when in agent context
|
|
const agentId = useMemo(() => {
|
|
if (context === 'agent') {
|
|
const match = pathname?.match(/^\/agent\/([^/?]+)/);
|
|
return match?.[1] || undefined;
|
|
}
|
|
return undefined;
|
|
}, [context, pathname]);
|
|
|
|
// Debounce search input to reduce API calls
|
|
const debouncedSearch = useDebounce(search, { wait: 600 });
|
|
|
|
// Search functionality
|
|
const hasSearch = debouncedSearch.trim().length > 0;
|
|
const searchQuery = debouncedSearch.trim();
|
|
|
|
const { data: searchResults, isLoading: isSearching } = useSWR<SearchResult[]>(
|
|
hasSearch ? ['search', searchQuery, agentId, typeFilter] : null,
|
|
async () => {
|
|
const locale = globalHelpers.getCurrentLanguage();
|
|
return lambdaClient.search.query.query({
|
|
agentId,
|
|
limitPerType: typeFilter ? 50 : 5, // Show more results when filtering by type
|
|
locale,
|
|
query: searchQuery,
|
|
type: typeFilter,
|
|
});
|
|
},
|
|
{
|
|
revalidateOnFocus: false,
|
|
revalidateOnReconnect: false,
|
|
},
|
|
);
|
|
|
|
// Close on Escape key and prevent body scroll
|
|
useEffect(() => {
|
|
if (open) {
|
|
const originalStyle = window.getComputedStyle(document.body).overflow;
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
return () => {
|
|
document.body.style.overflow = originalStyle;
|
|
};
|
|
}
|
|
}, [open]);
|
|
|
|
const closeCommandMenu = () => {
|
|
setOpen({ showCommandMenu: false });
|
|
};
|
|
|
|
const handleNavigate = (path: string) => {
|
|
navigate(path);
|
|
closeCommandMenu();
|
|
};
|
|
|
|
const handleExternalLink = (url: string) => {
|
|
window.open(url, '_blank', 'noopener,noreferrer');
|
|
closeCommandMenu();
|
|
};
|
|
|
|
const handleThemeChange = (theme: ThemeMode) => {
|
|
setTheme(theme);
|
|
closeCommandMenu();
|
|
};
|
|
|
|
const handleAskLobeAI = () => {
|
|
// Navigate to inbox agent with the message query parameter
|
|
if (inboxAgentId && search.trim()) {
|
|
const message = encodeURIComponent(search.trim());
|
|
navigate(`/agent/${inboxAgentId}?message=${message}`);
|
|
closeCommandMenu();
|
|
}
|
|
};
|
|
|
|
const handleAIPainting = () => {
|
|
// Navigate to painting page with search as prompt
|
|
if (search.trim()) {
|
|
const prompt = encodeURIComponent(search.trim());
|
|
navigate(`/image?prompt=${prompt}`);
|
|
closeCommandMenu();
|
|
}
|
|
};
|
|
|
|
const handleBack = () => {
|
|
setPages((prev) => prev.slice(0, -1));
|
|
};
|
|
|
|
const handleCreateSession = async () => {
|
|
const result = await createAgent({});
|
|
await refreshAgentList();
|
|
|
|
// Navigate to the newly created agent
|
|
if (result.agentId) {
|
|
navigate(`/agent/${result.agentId}`);
|
|
}
|
|
|
|
closeCommandMenu();
|
|
};
|
|
|
|
const [openNewTopicOrSaveTopic] = useChatStore((s) => [s.openNewTopicOrSaveTopic]);
|
|
|
|
const handleCreateTopic = async () => {
|
|
openNewTopicOrSaveTopic();
|
|
closeCommandMenu();
|
|
};
|
|
|
|
const handleCreateLibrary = async () => {
|
|
closeCommandMenu();
|
|
openCreateLibraryModal({
|
|
onSuccess: (id) => {
|
|
navigate(`/resource/library/${id}`);
|
|
},
|
|
});
|
|
};
|
|
|
|
const handleCreatePage = async () => {
|
|
await createPage();
|
|
closeCommandMenu();
|
|
};
|
|
|
|
const handleCreateAgentTeam = async () => {
|
|
closeCommandMenu();
|
|
openGroupWizard({
|
|
onCreateCustom: async (selectedAgents, hostConfig, enableSupervisor) => {
|
|
await createGroupWithMembers(selectedAgents, undefined, hostConfig, enableSupervisor);
|
|
},
|
|
onCreateFromTemplate: async (
|
|
templateId,
|
|
hostConfig,
|
|
enableSupervisor,
|
|
selectedMemberTitles,
|
|
) => {
|
|
await createGroupFromTemplate(
|
|
templateId,
|
|
hostConfig,
|
|
enableSupervisor,
|
|
selectedMemberTitles,
|
|
);
|
|
},
|
|
});
|
|
};
|
|
|
|
return {
|
|
closeCommandMenu,
|
|
handleAIPainting,
|
|
handleAskLobeAI,
|
|
handleBack,
|
|
handleCreateAgentTeam,
|
|
handleCreateLibrary,
|
|
handleCreatePage,
|
|
handleCreateSession,
|
|
handleCreateTopic,
|
|
handleExternalLink,
|
|
handleNavigate,
|
|
handleThemeChange,
|
|
hasSearch,
|
|
isSearching,
|
|
mounted,
|
|
open,
|
|
page,
|
|
pages,
|
|
pathname,
|
|
search,
|
|
searchQuery,
|
|
searchResults: searchResults || ([] as SearchResult[]),
|
|
setSearch,
|
|
setTypeFilter,
|
|
typeFilter,
|
|
};
|
|
};
|