♻️ refactor: restructure SPA routes to src/routes and src/router (#12542)

* 📝 docs: add SPA routes restructure design and implementation plan

* ♻️ refactor: restructure SPA routes to src/routes and src/router

- Move SPA page components from src/app/[variants] to src/routes/
  - (main) -> Desktop pages
  - (mobile) -> Mobile pages
  - (desktop) -> Desktop-specific pages
  - onboarding -> Onboarding pages
  - share -> Share pages
- Move router configurations from src/app/[variants]/router to src/router/
  - desktopRouter.config.tsx
  - desktopRouter.config.desktop.tsx
  - mobileRouter.config.tsx
- Keep auth pages in src/app/[variants]/(auth) for SSR
- Update all import paths:
  - @/app/[variants]/ -> @/routes/
  - Relative paths adjusted for new directory structure
- Update CLAUDE.md and project-overview skill documentation

* 🔧 chore: restore imports for RouteConfig and ErrorBoundary in desktopRouter.config.desktop.tsx

- Reintroduced the imports for RouteConfig, ErrorBoundary, and redirectElement in the desktop router configuration file.
- Ensured proper organization and functionality of the desktop routing setup.

Signed-off-by: Innei <tukon479@gmail.com>

* 🐛 fix: update import paths after routes restructure

- Fix imports from old `src/app/[variants]/` to new `src/routes/` paths
- Update Title, Sidebar, MakedownRender, McpList imports
- Fix desktop-onboarding/storage import path
- Run lint --fix to sort imports

* 📝 docs: SPA routes convention and spa-routes skill

- Add roots vs features rules to CLAUDE.md and AGENTS.md
- Add .agents/skills/spa-routes for route/feature file division
- Phase 1: move page route logic to src/features/Pages, thin route files

Made-with: Cursor

* 🌐 chore: translate non-English comments to English in memory module (#12547)

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* ♻️ refactor: move router and entries to src/spa, platform-based warmup

- Move src/router and entry.*.tsx to src/spa/
- Update HTML, vite.config, and entry imports
- Warmup only the entry matching current platform (web/mobile)
- Update CLAUDE.md, AGENTS.md, and spa-routes skill

Made-with: Cursor

* 🗂️ chore: restructure SPA routes and configurations

- Deleted outdated SPA routes and implementation plan documents.
- Migrated SPA page components to new `src/routes/` directory.
- Moved route configurations to `src/router/`.
- Updated import paths across the project to reflect new structure.
- Revised AI documentation to align with the updated directory layout.

Signed-off-by: Innei <tukon479@gmail.com>

---------

Signed-off-by: Innei <tukon479@gmail.com>
Co-authored-by: LobeHub Bot <i@lobehub.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Innei
2026-03-01 18:35:38 +08:00
committed by GitHub
parent 5e3a8146d1
commit 794fe5f60b
1176 changed files with 868 additions and 606 deletions
+16 -7
View File
@@ -101,13 +101,20 @@ lobe-chat/
│ │ │ ├── oidc/
│ │ │ ├── trpc/
│ │ │ └── webapi/
│ │ ├── [variants]/
│ │ │ ├── (auth)/
│ │ ── (main)/
├── (mobile)/
│ │ │ ├── onboarding/
│ │ │ └── router/
│ │ ── desktop/
│ │ ├── spa/ # SPA HTML template service
│ │ └── [variants]/
│ │ ── (auth)/ # Auth pages (SSR required)
├── routes/ # SPA page components (Vite)
│ │ ├── (main)/
│ │ ├── (mobile)/
│ │ ── (desktop)/
│ │ ├── onboarding/
│ │ └── share/
│ ├── spa/ # SPA entry points and router config
│ │ ├── entry.web.tsx
│ │ ├── entry.mobile.tsx
│ │ ├── entry.desktop.tsx
│ │ └── router/
│ ├── business/ # Cloud-only (client/server)
│ │ ├── client/
│ │ ├── locales/
@@ -155,6 +162,8 @@ lobe-chat/
| Layer | Location |
| ---------------- | --------------------------------------------------- |
| UI Components | `src/components`, `src/features` |
| SPA Pages | `src/routes/` |
| React Router | `src/spa/router/` |
| Global Providers | `src/layout` |
| Zustand Stores | `src/store` |
| Client Services | `src/services/` |
+3 -3
View File
@@ -36,9 +36,9 @@ Hybrid routing: Next.js App Router (static pages) + React Router DOM (main SPA).
### Key Files
- Entry: `src/app/[variants]/page.tsx`
- Desktop router: `src/app/[variants]/router/desktopRouter.config.tsx`
- Mobile router: `src/app/[variants]/(mobile)/router/mobileRouter.config.tsx`
- Entry: `src/spa/entry.web.tsx` (web), `src/spa/entry.mobile.tsx`, `src/spa/entry.desktop.tsx`
- Desktop router: `src/spa/router/desktopRouter.config.tsx`
- Mobile router: `src/spa/router/mobileRouter.config.tsx`
- Router utilities: `src/utils/router.tsx`
### Router Utilities
+145
View File
@@ -0,0 +1,145 @@
---
name: spa-routes
description: SPA route and feature structure. Use when adding or modifying SPA routes in src/routes, defining new route segments, or moving route logic into src/features. Covers how to keep routes thin and how to divide files between routes and features.
---
# SPA Routes and Features Guide
SPA structure:
- **`src/spa/`** Entry points (`entry.web.tsx`, `entry.mobile.tsx`, `entry.desktop.tsx`) and router config (`router/`). Router lives here to avoid confusion with `src/routes/`.
- **`src/routes/`** Page segments only (roots).
- **`src/features/`** Business logic and UI by domain.
This project uses a **roots vs features** split: `src/routes/` only holds page segments; business logic and UI live in `src/features/` by domain.
## When to Use This Skill
- Adding a new SPA route or route segment
- Defining or refactoring layout/page files under `src/routes/`
- Moving route-specific components or logic into `src/features/`
- Deciding where to put a new component (route folder vs feature folder)
---
## 1. What Belongs in `src/routes/` (roots)
Each route directory should contain **only**:
| File / folder | Purpose |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `_layout/index.tsx` or `layout.tsx` | Layout for this segment: wrap with `<Outlet />`, optional shell (e.g. sidebar + main). Should be thin: prefer re-exporting or composing from `@/features/*`. |
| `index.tsx` or `page.tsx` | Page entry for this segment. Only import from features and render; no business logic. |
| `[param]/index.tsx` (e.g. `[id]`, `[cronId]`) | Dynamic segment page. Same rule: thin, delegate to features. |
**Rule:** Route files should only **import and compose**. No new `features/` folders or heavy components inside `src/routes/`.
---
## 2. What Belongs in `src/features/`
Put **domain-oriented** UI and logic here:
- Layout building blocks: sidebars, headers, body panels, drawers
- Hooks and store usage for that domain
- Domain-specific forms, lists, modals, etc.
Organize by **domain** (e.g. `Pages`, `Home`, `Agent`, `PageEditor`), not by route path. One route can use several features; one feature can be used by several routes.
Each feature should:
- Live under `src/features/<FeatureName>/`
- Export a clear public API via `index.ts` or `index.tsx`
- Use `@/features/<FeatureName>/...` for internal imports when needed
---
## 3. How to Add a New SPA Route
1. **Choose the route group**
- `(main)/` desktop main app
- `(mobile)/` mobile
- `(desktop)/` Electron-specific
- `onboarding/`, `share/` special flows
2. **Create only segment files under `src/routes/`**
- e.g. `src/routes/(main)/my-feature/_layout/index.tsx` and `src/routes/(main)/my-feature/index.tsx` (and optional `[id]/index.tsx`).
3. **Implement layout and page content in `src/features/`**
- Create or reuse a domain (e.g. `src/features/MyFeature/`).
- Put layout (sidebar, header, body) and page UI there; export from the features `index`.
4. **Keep route files thin**
- Layout: `export { default } from '@/features/MyFeature/MyLayout'` or compose a few feature components + `<Outlet />`.
- Page: import from `@/features/MyFeature` (or a specific subpath) and render; no business logic in the route file.
5. **Register the route**
- Add the segment to `src/spa/router/desktopRouter.config.tsx` (or the right router config) with `dynamicElement` / `dynamicLayout` pointing at the new route paths (e.g. `@/routes/(main)/my-feature`).
---
## 4. How to Divide Files (route vs feature)
| Question | Put in `src/routes/` | Put in `src/features/` |
| -------------------------------------------------------- | -------------------------------------------------------- | ---------------------------- |
| Is it the routes layout wrapper or page entry? | Yes `_layout/index.tsx`, `index.tsx`, `[id]/index.tsx` | No |
| Does it contain business logic or non-trivial UI? | No | Yes under the right domain |
| Is it a reusable layout piece (sidebar, header, body)? | No | Yes |
| Is it a hook, store usage, or domain logic? | No | Yes |
| Is it only re-exporting or composing feature components? | Yes | No |
**Examples**
- **Route (thin):**\
`src/routes/(main)/page/_layout/index.tsx``export { default } from '@/features/Pages/PageLayout'`
- **Feature (real implementation):**\
`src/features/Pages/PageLayout/` → Sidebar, DataSync, Body, Header, styles, etc.
- **Route (thin):**\
`src/routes/(main)/page/index.tsx` → Import `PageTitle`, `PageExplorerPlaceholder` from `@/features/Pages` and `@/features/PageExplorer`; render with `<PageTitle />` and placeholder.
- **Feature:**\
Page list, actions, drawers, and hooks live under `src/features/Pages/`.
---
## 5. Progressive Migration (existing code)
We are migrating existing routes to this structure step by step:
- **Phase 1 (done):** `/page` route segment files in `src/routes/(main)/page/`, implementation in `src/features/Pages/`.
- **Later phases:** home, settings, agent/group, community/resource/memory, mobile/share/onboarding.
When touching an old route that still has logic or `features/` inside `src/routes/`:
1. Prefer adding **new** code in `src/features/<Domain>/` and importing from routes.
2. For larger refactors, move existing route-only logic into the right feature and then thin out the route files (re-export or compose from features).
3. Use `git mv` when moving files so history is preserved.
---
## 6. Reference Structure (after Phase 1)
**Route (thin):**
```
src/routes/(main)/page/
├── _layout/index.tsx → re-export or compose from @/features/Pages/PageLayout
├── index.tsx → import from @/features/Pages, @/features/PageExplorer
└── [id]/index.tsx → import from @/features/Pages, @/features/PageExplorer
```
**Feature (implementation):**
```
src/features/Pages/
├── index.ts → export PageLayout, PageTitle
├── PageTitle.tsx
└── PageLayout/
├── index.tsx → Sidebar + Outlet + DataSync
├── DataSync.tsx
├── Sidebar.tsx
├── style.ts
├── Body/ → list, actions, drawer, etc.
└── Header/ → breadcrumb, add button, etc.
```
Router config continues to point at **route** paths (e.g. `@/routes/(main)/page`, `@/routes/(main)/page/_layout`); route files then delegate to features.
+22 -12
View File
@@ -26,6 +26,9 @@ lobe-chat/
│ └── ...
├── src/
│ ├── app/ # Next.js app router
│ ├── spa/ # SPA entry points (entry.*.tsx) and router config
│ ├── routes/ # SPA page components (roots)
│ ├── features/ # Business components by domain
│ ├── store/ # Zustand stores
│ ├── services/ # Client services
│ ├── server/ # Server services and routers
@@ -87,19 +90,26 @@ cd packages/[package-name] && bunx vitest run --silent='passed-only' '[file-path
Follow [Linear rules in CLAUDE.md](CLAUDE.md#linear-issue-management-ignore-if-not-installed-linear-mcp) when working with Linear issues.
## SPA Routes and Features
- **`src/routes/`** holds only page segments (layout + page entry files). Keep route files thin; they should import from `@/features/*` and compose.
- **`src/features/`** holds business components by domain. Put layout pieces, hooks, and domain UI here.
- See [CLAUDE.md SPA Routes and Features](CLAUDE.md#spa-routes-and-features) and the **spa-routes** skill for how to add new routes and how to split files.
## Skills (Auto-loaded)
All AI development skills are available in `.agents/skills/` directory:
| Category | Skills |
| ----------- | ------------------------------------------ |
| Frontend | `react`, `typescript`, `i18n`, `microcopy` |
| State | `zustand` |
| Backend | `drizzle` |
| Desktop | `desktop` |
| Testing | `testing` |
| UI | `modal`, `hotkey`, `recent-data` |
| Config | `add-provider-doc`, `add-setting-env` |
| Workflow | `linear`, `debug` |
| Performance | `vercel-react-best-practices` |
| Overview | `project-overview` |
| Category | Skills |
| ------------ | ------------------------------------------ |
| Frontend | `react`, `typescript`, `i18n`, `microcopy` |
| State | `zustand` |
| Backend | `drizzle` |
| Desktop | `desktop` |
| Testing | `testing` |
| UI | `modal`, `hotkey`, `recent-data` |
| Config | `add-provider-doc`, `add-setting-env` |
| Workflow | `linear`, `debug` |
| Architecture | `spa-routes` |
| Performance | `vercel-react-best-practices` |
| Overview | `project-overview` |
+35 -1
View File
@@ -21,7 +21,21 @@ lobe-chat/
│ ├── agent-runtime/ # Agent runtime
│ └── ...
├── src/
│ ├── app/ # Next.js app router
│ ├── app/ # Next.js App Router (backend API + auth)
│ │ ├── (backend)/ # API routes (trpc, webapi, etc.)
│ │ ├── spa/ # SPA HTML template service
│ │ └── [variants]/(auth)/ # Auth pages (SSR required)
│ ├── routes/ # SPA page components (Vite)
│ │ ├── (main)/ # Desktop pages
│ │ ├── (mobile)/ # Mobile pages
│ │ ├── (desktop)/ # Desktop-specific pages
│ │ ├── onboarding/ # Onboarding pages
│ │ └── share/ # Share pages
│ ├── spa/ # SPA entry points and router config
│ │ ├── entry.web.tsx # Web entry
│ │ ├── entry.mobile.tsx
│ │ ├── entry.desktop.tsx
│ │ └── router/ # React Router configuration
│ ├── store/ # Zustand stores
│ ├── services/ # Client services
│ ├── server/ # Server services and routers
@@ -29,6 +43,26 @@ lobe-chat/
└── e2e/ # E2E tests (Cucumber + Playwright)
```
## SPA Routes and Features
SPA-related code is grouped under `src/spa/` (entries + router) and `src/routes/` (page segments). We use a **roots vs features** split: route trees only hold page segments; business logic and UI live in features.
- **`src/spa/`** SPA entry points (`entry.web.tsx`, `entry.mobile.tsx`, `entry.desktop.tsx`) and React Router config (`router/`). Keeps router config next to entries to avoid confusion with `src/routes/`.
- **`src/routes/` (roots)**\
Only page-segment files: `_layout/index.tsx`, `index.tsx` (or `page.tsx`), and dynamic segments like `[id]/index.tsx`. Keep these **thin**: they should only import from `@/features/*` and compose layout/page, with no business logic or heavy UI.
- **`src/features/`**\
Business components by **domain** (e.g. `Pages`, `PageEditor`, `Home`). Put layout chunks (sidebar, header, body), hooks, and domain-specific UI here. Each feature exposes an `index.ts` (or `index.tsx`) with clear exports.
When adding or changing SPA routes:
1. In `src/routes/`, add only the route segment files (layout + page) that delegate to features.
2. Implement layout and page content under `src/features/<Domain>/` and export from there.
3. In route files, use `import { X } from '@/features/<Domain>'` (or `import Y from '@/features/<Domain>/...'`). Do not add new `features/` folders inside `src/routes/`.
See the **spa-routes** skill (`.agents/skills/spa-routes/SKILL.md`) for the full convention and file-division rules.
## Development
### Starting the Dev Environment
+1 -1
View File
@@ -85,6 +85,6 @@
<script>
window.__SERVER_CONFIG__ = undefined;
</script>
<script type="module" src="../../src/entry.desktop.tsx"></script>
<script type="module" src="../../src/spa/entry.desktop.tsx"></script>
</body>
</html>
+1 -1
View File
@@ -136,6 +136,6 @@
<div id="root" style="height: 100%"></div>
<!--ANALYTICS_SCRIPTS-->
<script type="module" src="/src/entry.web.tsx"></script>
<script type="module" src="/src/spa/entry.web.tsx"></script>
</body>
</html>
+1 -1
View File
@@ -63,6 +63,6 @@
<div id="root" style="height: 100%"></div>
<!--ANALYTICS_SCRIPTS-->
<script type="module" src="/src/entry.mobile.tsx"></script>
<script type="module" src="/src/spa/entry.mobile.tsx"></script>
</body>
</html>
@@ -1,2 +0,0 @@
// Re-export from chat version to avoid code duplication
export { default } from '@/app/[variants]/(main)/agent/features/Conversation/AgentWelcome/ToolAuthAlert';
@@ -1,3 +0,0 @@
import Loading from '@/components/Loading/BrandTextLoading';
export default () => <Loading debugId="Image Page" />;
@@ -1,98 +0,0 @@
import { Icon } from '@lobehub/ui';
import { cssVar } from 'antd-style';
import { Loader2Icon } from 'lucide-react';
import { type CSSProperties } from 'react';
import React, { memo, useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import RepoIcon from '@/components/LibIcon';
import NavItem from '@/features/NavPanel/components/NavItem';
import { useKnowledgeBaseStore } from '@/store/library';
import Actions from './Actions';
import Editing from './Editing';
import { useDropdownMenu } from './useDropdownMenu';
interface KnowledgeBaseItemProps {
active?: boolean;
className?: string;
description?: string | null;
id: string;
name: string;
style?: CSSProperties;
}
const KnowledgeBaseItem = memo<KnowledgeBaseItemProps>(({ id, name, description, active, style, className }) => {
const setLibraryId = useResourceManagerStore((s) => s.setLibraryId);
const navigate = useNavigate();
const [editing, isLoading] = useKnowledgeBaseStore((s) => [
s.knowledgeBaseRenamingId === id,
s.knowledgeBaseLoadingIds.includes(id),
]);
const toggleEditing = useCallback(
(visible?: boolean) => {
useKnowledgeBaseStore.setState(
{ knowledgeBaseRenamingId: visible ? id : null },
false,
'toggleEditing',
);
},
[id],
);
const handleClick = useCallback(() => {
if (!editing) {
navigate(`/resource/library/${id}`);
setLibraryId(id);
}
}, [editing, navigate, id]);
const handleDoubleClick = useCallback(
(e: React.MouseEvent) => {
if (e.altKey) {
toggleEditing(true);
}
},
[toggleEditing],
);
// Icon (show loader when updating)
const icon = useMemo(() => {
if (isLoading) {
return <Icon spin color={cssVar.colorTextDescription} icon={Loader2Icon} size={18} />;
}
return <RepoIcon size={18} />;
}, [isLoading]);
const dropdownMenu = useDropdownMenu({
description,
id,
name,
toggleEditing,
});
return (
<>
<NavItem
actions={<Actions dropdownMenu={dropdownMenu} />}
active={active}
className={className}
contextMenuItems={dropdownMenu}
disabled={editing}
icon={icon}
key={id}
loading={isLoading}
style={style}
title={name}
onClick={handleClick}
onDoubleClick={handleDoubleClick}
/>
<Editing id={id} name={name} toggleEditing={toggleEditing} />
</>
);
});
export default KnowledgeBaseItem;
@@ -7,8 +7,7 @@ import { Fragment, isValidElement, memo } from 'react';
export const toolsListStyles = createStaticStyles(({ css }) => ({
groupLabel: css`
padding-block-start: 12px;
padding-block-end: 4px;
padding-block: 12px 4px;
padding-inline: 12px;
`,
item: css`
+1 -1
View File
@@ -4,12 +4,12 @@ import { useCallback, useEffect, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import useSWR from 'swr';
import { useCreateMenuItems } from '@/app/[variants]/(main)/home/_layout/hooks';
import { isDesktop } from '@/const/version';
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 { useCreateMenuItems } from '@/routes/(main)/home/_layout/hooks';
import { electronSystemService } from '@/services/electron/system';
import { useAgentStore } from '@/store/agent';
import { builtinAgentSelectors } from '@/store/agent/selectors/builtinAgentSelectors';
+1 -1
View File
@@ -5,7 +5,7 @@ import { useWatchBroadcast } from '@lobechat/electron-client-ipc';
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { useCreateMenuItems } from '@/app/[variants]/(main)/home/_layout/hooks/useCreateMenuItems';
import { useCreateMenuItems } from '@/routes/(main)/home/_layout/hooks/useCreateMenuItems';
import { useAgentStore } from '@/store/agent';
import { builtinAgentSelectors } from '@/store/agent/selectors';
@@ -7,7 +7,7 @@ import { AlertCircle, LogIn } from 'lucide-react';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { getDesktopOnboardingCompleted } from '@/app/[variants]/(desktop)/desktop-onboarding/storage';
import { getDesktopOnboardingCompleted } from '@/routes/(desktop)/desktop-onboarding/storage';
import { useElectronStore } from '@/store/electron';
interface AuthRequiredModalContentProps {
@@ -3,8 +3,8 @@ import { Drawer } from 'antd';
import { createStaticStyles, cssVar } from 'antd-style';
import { Suspense, useCallback } from 'react';
import LoginStep from '@/app/[variants]/(desktop)/desktop-onboarding/features/LoginStep';
import { BrandTextLoading } from '@/components/Loading';
import LoginStep from '@/routes/(desktop)/desktop-onboarding/features/LoginStep';
import { useElectronStore } from '@/store/electron';
import { isMacOS } from '@/utils/platform';
@@ -2,8 +2,8 @@ import { Flexbox } from '@lobehub/ui';
import { type ReactNode } from 'react';
import { memo } from 'react';
import { type TitleProps } from '../../app/[variants]/(main)/community/features/Title';
import Title from '../../app/[variants]/(main)/community/features/Title';
import { type TitleProps } from '@/routes/(main)/community/features/Title';
import Title from '@/routes/(main)/community/features/Title';
export type CollapseItemType = {
children: ReactNode;
@@ -28,9 +28,9 @@ import { useTranslation } from 'react-i18next';
import Descriptions from '@/components/Descriptions';
import InlineTable from '@/components/InlineTable';
import Title from '@/routes/(main)/community/features/Title';
import { markdownToTxt } from '@/utils/markdownToTxt';
import Title from '../../../app/[variants]/(main)/community/features/Title';
import InstallationIcon from '../../../components/MCPDepsIcon';
import CollapseDesc from '../CollapseDesc';
import CollapseLayout from '../CollapseLayout';
@@ -3,9 +3,10 @@ import qs from 'query-string';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import MarkdownRender from '../../../app/[variants]/(main)/community/(detail)/features/MakedownRender';
import McpList from '../../../app/[variants]/(main)/community/(list)/mcp/features/List';
import Title from '../../../app/[variants]/(main)/community/features/Title';
import MarkdownRender from '@/routes/(main)/community/(detail)/features/MakedownRender';
import McpList from '@/routes/(main)/community/(list)/mcp/features/List';
import Title from '@/routes/(main)/community/features/Title';
import { useDetailContext } from '../DetailProvider';
import TagList from './TagList';
@@ -3,7 +3,8 @@ import { type ReactNode } from 'react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import Title from '../../../app/[variants]/(main)/community/features/Title';
import Title from '@/routes/(main)/community/features/Title';
import { ModeType } from './types';
interface BlockProps {
@@ -5,9 +5,9 @@ import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import InlineTable from '@/components/InlineTable';
import Title from '@/routes/(main)/community/features/Title';
import { markdownToTxt } from '@/utils/markdownToTxt';
import Title from '../../../app/[variants]/(main)/community/features/Title';
import CollapseDesc from '../CollapseDesc';
import CollapseLayout from '../CollapseLayout';
import { useDetailContext } from '../DetailProvider';
@@ -5,9 +5,9 @@ import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import InlineTable from '@/components/InlineTable';
import Title from '@/routes/(main)/community/features/Title';
import { markdownToTxt } from '@/utils/markdownToTxt';
import Title from '../../../app/[variants]/(main)/community/features/Title';
import CollapseDesc from '../CollapseDesc';
import CollapseLayout from '../CollapseLayout';
import { useDetailContext } from '../DetailProvider';
@@ -4,7 +4,7 @@ import { BanIcon, CircleCheckBigIcon, CircleDashedIcon } from 'lucide-react';
import { type ReactNode } from 'react';
import { memo } from 'react';
import Title from '../../../app/[variants]/(main)/community/features/Title';
import Title from '@/routes/(main)/community/features/Title';
export interface ScoreItemProps {
check: boolean;
+1 -1
View File
@@ -9,8 +9,8 @@ import {
sortItemsByPriority,
} from '@/features/MCP/calculateScore';
import { useScoreList } from '@/features/MCP/useScoreList';
import Title from '@/routes/(main)/community/features/Title';
import Title from '../../../app/[variants]/(main)/community/features/Title';
import { useDetailContext } from '../DetailProvider';
import GithubBadge from './GithubBadge';
import ScoreList from './ScoreList';
+1 -1
View File
@@ -2,8 +2,8 @@ import { Flexbox, ScrollShadow, TooltipGroup } from '@lobehub/ui';
import { type ReactNode } from 'react';
import { memo, Suspense } from 'react';
import Footer from '@/app/[variants]/(main)/home/_layout/Footer';
import SkeletonList, { SkeletonItem } from '@/features/NavPanel/components/SkeletonList';
import Footer from '@/routes/(main)/home/_layout/Footer';
interface SidebarLayoutProps {
body?: ReactNode;
@@ -3,12 +3,12 @@
import { DraggablePanel, Freeze } from '@lobehub/ui';
import { createStaticStyles, cssVar } from 'antd-style';
import { AnimatePresence, motion, useIsPresent } from 'motion/react';
import { type ReactNode } from 'react';
import { type ReactNode } from 'react';
import { memo, useLayoutEffect, useMemo, useRef } from 'react';
import { USER_DROPDOWN_ICON_ID } from '@/app/[variants]/(main)/home/_layout/Header/components/User';
import { isDesktop } from '@/const/version';
import { TOGGLE_BUTTON_ID } from '@/features/NavPanel/ToggleLeftPanelButton';
import { USER_DROPDOWN_ICON_ID } from '@/routes/(main)/home/_layout/Header/components/User';
import { useGlobalStore } from '@/store/global';
import { systemStatusSelectors } from '@/store/global/selectors';
import { useUserStore } from '@/store/user';
+2 -1
View File
@@ -3,7 +3,8 @@
import { type PropsWithChildren, type ReactNode } from 'react';
import { memo, useLayoutEffect, useSyncExternalStore } from 'react';
import Sidebar from '../../app/[variants]/(main)/home/_layout/Sidebar';
import Sidebar from '@/routes/(main)/home/_layout/Sidebar';
import { NavPanelDraggable } from './components/NavPanelDraggable';
export const NAV_PANEL_RIGHT_DRAWER_ID = 'nav-panel-drawer';
@@ -2,8 +2,8 @@ import { type GroupMemberAvatar } from '@lobechat/types';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import AgentAvatar from '@/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/Avatar';
import NavItem from '@/features/NavPanel/components/NavItem';
import AgentAvatar from '@/routes/(main)/home/_layout/Body/Agent/List/AgentItem/Avatar';
interface AgentItemProps {
active: boolean;
@@ -4,10 +4,10 @@ import { ChevronsUpDownIcon } from 'lucide-react';
import { memo, Suspense, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import AgentAvatar from '@/app/[variants]/(main)/home/_layout/Body/Agent/List/AgentItem/Avatar';
import { AgentModalProvider } from '@/app/[variants]/(main)/home/_layout/Body/Agent/ModalProvider';
import SkeletonList from '@/features/NavPanel/components/SkeletonList';
import { useFetchAgentList } from '@/hooks/useFetchAgentList';
import AgentAvatar from '@/routes/(main)/home/_layout/Body/Agent/List/AgentItem/Avatar';
import { AgentModalProvider } from '@/routes/(main)/home/_layout/Body/Agent/ModalProvider';
import { useAgentStore } from '@/store/agent';
import { useHomeStore } from '@/store/home';
import { homeAgentListSelectors } from '@/store/home/selectors';
+1 -1
View File
@@ -12,7 +12,7 @@ interface PageExplorerProps {
/**
* Dedicated for the /page route
*
* Work together with a sidebar src/app/[variants]/(main)/page/_layout/Body/index.tsx
* Work together with a sidebar @/features/Pages/PageLayout/Body
*/
const PageExplorer = memo<PageExplorerProps>(({ pageId }) => {
const updatePageOptimistically = usePageStore((s) => s.updatePageOptimistically);
@@ -1,7 +1,7 @@
'use client';
import { Accordion, AccordionItem, ContextMenuTrigger, Flexbox, Text } from '@lobehub/ui';
import React, { memo,Suspense } from 'react';
import React, { memo, Suspense } from 'react';
import { useTranslation } from 'react-i18next';
import SkeletonList from '@/features/NavPanel/components/SkeletonList';
+2
View File
@@ -0,0 +1,2 @@
export { default as PageLayout } from './PageLayout';
export { default as PageTitle } from './PageTitle';
@@ -2,7 +2,7 @@
import { memo } from 'react';
import FileDetailComponent from '@/app/[variants]/(main)/resource/features/FileDetail';
import FileDetailComponent from '@/routes/(main)/resource/features/FileDetail';
import { fileManagerSelectors, useFileStore } from '@/store/file';
interface FileDetailProps {
@@ -8,11 +8,11 @@ import { ArrowLeftIcon, DownloadIcon, InfoIcon } from 'lucide-react';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import FileDetailComponent from '@/app/[variants]/(main)/resource/features/FileDetail';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import Loading from '@/components/Loading/BrandTextLoading';
import NavHeader from '@/features/NavHeader';
import PageAgentProvider from '@/features/PageEditor/PageAgentProvider';
import FileDetailComponent from '@/routes/(main)/resource/features/FileDetail';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { useAgentStore } from '@/store/agent';
import { builtinAgentSelectors } from '@/store/agent/selectors';
import { fileManagerSelectors, useFileStore } from '@/store/file';
@@ -39,7 +39,12 @@ const FileEditorCanvas = memo<FileEditorProps>(({ onBack }) => {
<Flexbox flex={1} height={'100%'}>
<NavHeader
left={
<Flexbox horizontal align={'center'} gap={12} style={{ minHeight: 32, minWidth: 0, overflow: 'hidden' }}>
<Flexbox
horizontal
align={'center'}
gap={12}
style={{ minHeight: 32, minWidth: 0, overflow: 'hidden' }}
>
<ActionIcon icon={ArrowLeftIcon} title={t('back')} onClick={onBack} />
<span
title={fileDetail?.name}
@@ -4,8 +4,8 @@ import { createStaticStyles, cssVar } from 'antd-style';
import { ArrowUpIcon, PlusIcon } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import { useCreateNewModal } from '@/features/LibraryModal';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { useFileStore } from '@/store/file';
const ICON_SIZE = 80;
@@ -4,8 +4,8 @@ import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useFolderPath } from '@/app/[variants]/(main)/resource/features/hooks/useFolderPath';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import { useFolderPath } from '@/routes/(main)/resource/features/hooks/useFolderPath';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { useFileStore } from '@/store/file';
import { knowledgeBaseSelectors, useKnowledgeBaseStore } from '@/store/library';
import { FilesTabs } from '@/types/files';
@@ -7,7 +7,7 @@ import { SearchIcon, XIcon } from 'lucide-react';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
const SearchInput = memo(() => {
const { t } = useTranslation('components');
@@ -7,8 +7,8 @@ import { BookMinusIcon, FileBoxIcon, Trash2Icon } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import NavHeader from '@/features/NavHeader';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { FilesTabs } from '@/types/files';
import AddButton from '../../Header/AddButton';
@@ -18,15 +18,15 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { shallow } from 'zustand/shallow';
import FileIcon from '@/components/FileIcon';
import { clearTreeFolderCache } from '@/features/ResourceManager/components/LibraryHierarchy';
import { PAGE_FILE_TYPE } from '@/features/ResourceManager/constants';
import {
getTransparentDragImage,
useDragActive,
useDragState,
} from '@/app/[variants]/(main)/resource/features/DndContextWrapper';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import FileIcon from '@/components/FileIcon';
import { clearTreeFolderCache } from '@/features/ResourceManager/components/LibraryHierarchy';
import { PAGE_FILE_TYPE } from '@/features/ResourceManager/constants';
} from '@/routes/(main)/resource/features/DndContextWrapper';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { fileManagerSelectors, useFileStore } from '@/store/file';
import { type FileListItem as FileListItemType } from '@/types/files';
import { formatSize } from '@/utils/format';
@@ -9,13 +9,13 @@ import { useTranslation } from 'react-i18next';
import { type VirtuosoHandle } from 'react-virtuoso';
import { Virtuoso } from 'react-virtuoso';
import { useDragActive } from '@/app/[variants]/(main)/resource/features/DndContextWrapper';
import { useFolderPath } from '@/app/[variants]/(main)/resource/features/hooks/useFolderPath';
import { useDragActive } from '@/routes/(main)/resource/features/DndContextWrapper';
import { useFolderPath } from '@/routes/(main)/resource/features/hooks/useFolderPath';
import {
useResourceManagerFetchFolderBreadcrumb,
useResourceManagerStore,
} from '@/app/[variants]/(main)/resource/features/store';
import { sortFileList } from '@/app/[variants]/(main)/resource/features/store/selectors';
} from '@/routes/(main)/resource/features/store';
import { sortFileList } from '@/routes/(main)/resource/features/store/selectors';
import { useFileStore } from '@/store/file';
import { useFetchResources } from '@/store/file/slices/resource/hooks';
import { useGlobalStore } from '@/store/global';
@@ -6,7 +6,7 @@ import {
getTransparentDragImage,
useDragActive,
useDragState,
} from '@/app/[variants]/(main)/resource/features/DndContextWrapper';
} from '@/routes/(main)/resource/features/DndContextWrapper';
import { documentService } from '@/services/document';
import { type FileListItem } from '@/types/files';
@@ -7,8 +7,8 @@ import { type UIEvent } from 'react';
import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import { sortFileList } from '@/app/[variants]/(main)/resource/features/store/selectors';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { sortFileList } from '@/routes/(main)/resource/features/store/selectors';
import { useFileStore } from '@/store/file';
import { useFetchResources } from '@/store/file/slices/resource/hooks';
import { type FileListItem } from '@/types/files';
@@ -8,9 +8,9 @@ import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Virtuoso } from 'react-virtuoso';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
import { useClientDataSWR } from '@/libs/swr';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { resourceService } from '@/services/resource';
import { useGlobalStore } from '@/store/global';
import { INITIAL_STATUS } from '@/store/global/initialState';
@@ -11,8 +11,8 @@ import {
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import RepoIcon from '@/components/LibIcon';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { useKnowledgeBaseStore } from '@/store/library';
import ActionIconWithChevron from './ActionIconWithChevron';
@@ -5,7 +5,7 @@ import { BookMinusIcon, BookPlusIcon, FileBoxIcon, Trash2Icon } from 'lucide-rea
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
const styles = createStaticStyles(({ css }) => ({
total: css`
@@ -4,8 +4,8 @@ import { ArrowDownAZ, CalendarIcon, Check, HardDriveIcon } from 'lucide-react';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import { type MenuProps } from '@/components/Menu';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import ActionIconWithChevron from './ActionIconWithChevron';
@@ -1,7 +1,7 @@
import { useCallback } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
export interface UseFileItemClickOptions {
id: string;
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { type FileListItem } from '@/types/files';
/**
@@ -1,8 +1,8 @@
import { useCallback, useEffect } from 'react';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import { type ViewMode } from '@/app/[variants]/(main)/resource/features/store/initialState';
import { parseAsStringEnum, useQueryState } from '@/hooks/useQueryParam';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { type ViewMode } from '@/routes/(main)/resource/features/store/initialState';
/**
* Hook to manage view mode with URL query sync
@@ -3,10 +3,10 @@
import { Flexbox } from '@lobehub/ui';
import { memo, useEffect, useMemo } from 'react';
import { useFolderPath } from '@/app/[variants]/(main)/resource/features/hooks/useFolderPath';
import { useResourceManagerUrlSync } from '@/app/[variants]/(main)/resource/features/hooks/useResourceManagerUrlSync';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import { sortFileList } from '@/app/[variants]/(main)/resource/features/store/selectors';
import { useFolderPath } from '@/routes/(main)/resource/features/hooks/useFolderPath';
import { useResourceManagerUrlSync } from '@/routes/(main)/resource/features/hooks/useResourceManagerUrlSync';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { sortFileList } from '@/routes/(main)/resource/features/store/selectors';
import { useFetchResources, useResourceStore } from '@/store/file/slices/resource/hooks';
import EmptyPlaceholder from './EmptyPlaceholder';
@@ -1,16 +1,16 @@
import { useCallback, useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useFolderPath } from '@/app/[variants]/(main)/resource/features/hooks/useFolderPath';
import { useAddFilesToKnowledgeBaseModal } from '@/features/LibraryModal';
import { useFolderPath } from '@/routes/(main)/resource/features/hooks/useFolderPath';
import {
useResourceManagerFetchFolderBreadcrumb,
useResourceManagerFetchKnowledgeItem,
useResourceManagerFetchKnowledgeItems,
useResourceManagerStore,
} from '@/app/[variants]/(main)/resource/features/store';
import { type MultiSelectActionType } from '@/app/[variants]/(main)/resource/features/store/action';
import { selectors, sortFileList } from '@/app/[variants]/(main)/resource/features/store/selectors';
import { useAddFilesToKnowledgeBaseModal } from '@/features/LibraryModal';
} from '@/routes/(main)/resource/features/store';
import { type MultiSelectActionType } from '@/routes/(main)/resource/features/store/action';
import { selectors, sortFileList } from '@/routes/(main)/resource/features/store/selectors';
import { fileManagerSelectors, useFileStore } from '@/store/file';
import { type FilesTabs } from '@/types/files';
@@ -10,10 +10,10 @@ import { type ChangeEvent } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import { message } from '@/components/AntdStaticMethods';
import GuideModal from '@/components/GuideModal';
import GuideVideo from '@/components/GuideVideo';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { useFileStore } from '@/store/file';
import { FilesTabs } from '@/types/files';
@@ -9,14 +9,14 @@ import * as motion from 'motion/react-m';
import React, { memo, useCallback, useMemo, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import FileIcon from '@/components/FileIcon';
import { PAGE_FILE_TYPE } from '@/features/ResourceManager/constants';
import {
getTransparentDragImage,
useDragActive,
useDragState,
} from '@/app/[variants]/(main)/resource/features/DndContextWrapper';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import FileIcon from '@/components/FileIcon';
import { PAGE_FILE_TYPE } from '@/features/ResourceManager/constants';
} from '@/routes/(main)/resource/features/DndContextWrapper';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { useFileStore } from '@/store/file';
import { useFileItemClick } from '../Explorer/hooks/useFileItemClick';
@@ -4,8 +4,8 @@ import { Flexbox } from '@lobehub/ui';
import { memo, useCallback, useEffect, useMemo, useReducer, useRef } from 'react';
import { VList } from 'virtua';
import { useFolderPath } from '@/app/[variants]/(main)/resource/features/hooks/useFolderPath';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import { useFolderPath } from '@/routes/(main)/resource/features/hooks/useFolderPath';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { fileService } from '@/services/file';
import { useFileStore } from '@/store/file';
import { type ResourceQueryParams } from '@/types/resource';
+1 -1
View File
@@ -6,10 +6,10 @@ import { createStaticStyles, useTheme } from 'antd-style';
import { memo, useCallback, useEffect, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
import DragUploadZone from '@/components/DragUploadZone';
import { PageEditor } from '@/features/PageEditor';
import dynamic from '@/libs/next/dynamic';
import { useResourceManagerStore } from '@/routes/(main)/resource/features/store';
import { documentService } from '@/services/document';
import { useFileStore } from '@/store/file';
import { documentSelectors } from '@/store/file/slices/document/selectors';
@@ -6,12 +6,12 @@ import { createStaticStyles, cssVar } from 'antd-style';
import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import Title from '@/app/[variants]/(main)/community/features/Title';
import ContentViewer from '@/features/AgentSkillDetail/ContentViewer';
import FileTree from '@/features/AgentSkillDetail/FileTree';
import { DetailProvider } from '@/features/MCPPluginDetail/DetailProvider';
import Tools from '@/features/MCPPluginDetail/Schema/Tools';
import { ModeType } from '@/features/MCPPluginDetail/Schema/types';
import Title from '@/routes/(main)/community/features/Title';
import { useDetailContext } from './DetailContext';
+3 -3
View File
@@ -3,13 +3,13 @@ import { Flexbox } from '@lobehub/ui';
import { type FC } from 'react';
import { Link } from 'react-router-dom';
import { navigateToDesktopOnboarding } from '@/app/[variants]/(desktop)/desktop-onboarding/navigation';
import { clearDesktopOnboardingCompleted } from '@/app/[variants]/(desktop)/desktop-onboarding/storage';
import { DesktopOnboardingScreen } from '@/app/[variants]/(desktop)/desktop-onboarding/types';
import BusinessPanelContent from '@/business/client/features/User/BusinessPanelContent';
import BrandWatermark from '@/components/BrandWatermark';
import Menu from '@/components/Menu';
import { isDesktop } from '@/const/version';
import { navigateToDesktopOnboarding } from '@/routes/(desktop)/desktop-onboarding/navigation';
import { clearDesktopOnboardingCompleted } from '@/routes/(desktop)/desktop-onboarding/storage';
import { DesktopOnboardingScreen } from '@/routes/(desktop)/desktop-onboarding/types';
import { useUserStore } from '@/store/user';
import { authSelectors } from '@/store/user/selectors';
@@ -3,8 +3,8 @@
import { OFFICIAL_URL } from '@lobechat/const';
import { useCallback } from 'react';
import { getDesktopOnboardingCompleted } from '@/app/[variants]/(desktop)/desktop-onboarding/storage';
import { isDesktop } from '@/const/version';
import { getDesktopOnboardingCompleted } from '@/routes/(desktop)/desktop-onboarding/storage';
import { useElectronStore } from '@/store/electron';
import { useUserStore } from '@/store/user';
import { onboardingSelectors } from '@/store/user/selectors';
@@ -2,7 +2,7 @@
import { memo, useEffect } from 'react';
import { getDesktopOnboardingCompleted } from '@/app/[variants]/(desktop)/desktop-onboarding/storage';
import { getDesktopOnboardingCompleted } from '@/routes/(desktop)/desktop-onboarding/storage';
import { useElectronStore } from '@/store/electron';
import {
getDesktopAutoOidcFirstOpenHandled,
@@ -8,7 +8,6 @@ import { lazy, Suspense } from 'react';
import { HotkeysProvider } from 'react-hotkeys-hook';
import { Outlet } from 'react-router-dom';
import { DndContextWrapper } from '@/app/[variants]/(main)/resource/features/DndContextWrapper';
import Loading from '@/components/Loading/BrandTextLoading';
import { isDesktop } from '@/const/version';
import { BANNER_HEIGHT } from '@/features/AlertBanner/CloudBanner';
@@ -23,6 +22,7 @@ import { usePlatform } from '@/hooks/usePlatform';
import { MarketAuthProvider } from '@/layout/AuthProvider/MarketAuth';
import CmdkLazy from '@/layout/GlobalProvider/CmdkLazy';
import dynamic from '@/libs/next/dynamic';
import { DndContextWrapper } from '@/routes/(main)/resource/features/DndContextWrapper';
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
import { HotkeyScopeEnum } from '@/types/hotkey';

Some files were not shown because too many files have changed in this diff Show More