diff --git a/dashboard/eslint.config.js b/dashboard/eslint.config.js index f53da2d8..26a7be20 100644 --- a/dashboard/eslint.config.js +++ b/dashboard/eslint.config.js @@ -6,7 +6,7 @@ import reactRefresh from 'eslint-plugin-react-refresh' import tseslint from 'typescript-eslint' export default tseslint.config( - { ignores: ['dist'] }, + { ignores: ['dist', 'out'] }, jsxA11y.flatConfigs.recommended, { extends: [js.configs.recommended, ...tseslint.configs.recommended], @@ -25,10 +25,7 @@ export default tseslint.config( acc[key] = 'warn' return acc }, {}), - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], + 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], // 关闭或降级其他规则 '@typescript-eslint/no-explicit-any': 'warn', '@typescript-eslint/no-unused-vars': 'warn', @@ -37,4 +34,11 @@ export default tseslint.config( 'jsx-a11y/no-autofocus': 'warn', }, }, + { + files: ['**/*.d.ts'], + rules: { + // Ambient global declarations use `var` in TypeScript declaration files. + 'no-var': 'off', + }, + } ) diff --git a/dashboard/src/components/CodeEditor.tsx b/dashboard/src/components/CodeEditor.tsx index ae928f90..58a5875d 100644 --- a/dashboard/src/components/CodeEditor.tsx +++ b/dashboard/src/components/CodeEditor.tsx @@ -1,19 +1,8 @@ -import { useEffect, useState } from 'react' -import CodeMirror from '@uiw/react-codemirror' -import { css } from '@codemirror/lang-css' -import { json, jsonParseLinter } from '@codemirror/lang-json' -import { linter } from '@codemirror/lint' -import { python } from '@codemirror/lang-python' -import { oneDark } from '@codemirror/theme-one-dark' -import { EditorView } from '@codemirror/view' -import { StreamLanguage } from '@codemirror/language' -import { toml as tomlMode } from '@codemirror/legacy-modes/mode/toml' - -import { useTheme } from '@/components/use-theme' +import { lazy, Suspense } from 'react' export type Language = 'python' | 'json' | 'toml' | 'css' | 'text' -interface CodeEditorProps { +export interface CodeEditorProps { value: string onChange?: (value: string) => void @@ -27,109 +16,38 @@ interface CodeEditorProps { className?: string } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const languageExtensions: Record = { - python: [python()], - json: [json(), linter(jsonParseLinter())], - toml: [StreamLanguage.define(tomlMode)], - css: [css()], - text: [], -} +const CodeEditorImpl = lazy(() => import('./CodeEditorImpl')) -export function CodeEditor({ - value, - onChange, - language = 'text', - readOnly = false, - height = '400px', +function CodeEditorFallback({ + height, minHeight, maxHeight, - placeholder, - theme, className = '', -}: CodeEditorProps) { - const [mounted, setMounted] = useState(false) - const { resolvedTheme } = useTheme() +}: Pick) { + return ( +
+ ) +} - useEffect(() => { - setMounted(true) - }, []) - - if (!mounted) { - return ( -
- ) - } - - const extensions = [ - ...(languageExtensions[language] || []), - EditorView.lineWrapping, - // 应用 JetBrains Mono 字体 - EditorView.theme({ - '&': { - fontFamily: '"JetBrains Mono", "Fira Code", "Consolas", "Monaco", monospace', - }, - '.cm-content': { - fontFamily: '"JetBrains Mono", "Fira Code", "Consolas", "Monaco", monospace', - }, - '.cm-gutters': { - fontFamily: '"JetBrains Mono", "Fira Code", "Consolas", "Monaco", monospace', - }, - '.cm-scroller': { - fontFamily: '"JetBrains Mono", "Fira Code", "Consolas", "Monaco", monospace', - }, - }), - ] - - if (readOnly) { - extensions.push(EditorView.editable.of(false)) - } - - // 如果外部传了 theme prop 则使用,否则从 context 自动获取 - const effectiveTheme = theme ?? resolvedTheme +export function CodeEditor(props: CodeEditorProps) { + const { height = '400px', minHeight, maxHeight, className = '' } = props return ( -
- -
+ + } + > + + ) } diff --git a/dashboard/src/components/CodeEditorImpl.tsx b/dashboard/src/components/CodeEditorImpl.tsx new file mode 100644 index 00000000..5477a449 --- /dev/null +++ b/dashboard/src/components/CodeEditorImpl.tsx @@ -0,0 +1,105 @@ +import { css } from '@codemirror/lang-css' +import { json, jsonParseLinter } from '@codemirror/lang-json' +import { python } from '@codemirror/lang-python' +import { StreamLanguage } from '@codemirror/language' +import { toml as tomlMode } from '@codemirror/legacy-modes/mode/toml' +import { linter } from '@codemirror/lint' +import { oneDark } from '@codemirror/theme-one-dark' +import { EditorView } from '@codemirror/view' +import CodeMirror from '@uiw/react-codemirror' + +import { useTheme } from '@/components/use-theme' + +import type { CodeEditorProps, Language } from './CodeEditor' + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const languageExtensions: Record = { + python: [python()], + json: [json(), linter(jsonParseLinter())], + toml: [StreamLanguage.define(tomlMode)], + css: [css()], + text: [], +} + +export default function CodeEditorImpl({ + value, + onChange, + language = 'text', + readOnly = false, + height = '400px', + minHeight, + maxHeight, + placeholder, + theme, + className = '', +}: CodeEditorProps) { + const { resolvedTheme } = useTheme() + + const extensions = [ + ...(languageExtensions[language] || []), + EditorView.lineWrapping, + // 应用 JetBrains Mono 字体 + EditorView.theme({ + '&': { + fontFamily: '"JetBrains Mono", "Fira Code", "Consolas", "Monaco", monospace', + }, + '.cm-content': { + fontFamily: '"JetBrains Mono", "Fira Code", "Consolas", "Monaco", monospace', + }, + '.cm-gutters': { + fontFamily: '"JetBrains Mono", "Fira Code", "Consolas", "Monaco", monospace', + }, + '.cm-scroller': { + fontFamily: '"JetBrains Mono", "Fira Code", "Consolas", "Monaco", monospace', + }, + }), + ] + + if (readOnly) { + extensions.push(EditorView.editable.of(false)) + } + + // 如果外部传了 theme prop 则使用,否则从 context 自动获取 + const effectiveTheme = theme ?? resolvedTheme + + return ( +
+ +
+ ) +} diff --git a/dashboard/src/components/expression-reviewer.tsx b/dashboard/src/components/expression-reviewer.tsx index 9f3e8b27..e03d9f06 100644 --- a/dashboard/src/components/expression-reviewer.tsx +++ b/dashboard/src/components/expression-reviewer.tsx @@ -1443,6 +1443,7 @@ export function ExpressionReviewer({ open, onOpenChange }: ExpressionReviewerPro
@@ -1561,14 +1562,14 @@ if (isCurrent) {
{/* 情景 */}
- +
情景

{expr.situation}

{/* 风格 */}
- +
风格
{expr.style.split(/[,,]/).map((s, i) => ( @@ -1614,14 +1615,14 @@ if (isCurrent) {
{/* 情景 */}
- +
情景

{expr.situation}

{/* 风格 */}
- +
风格
{expr.style.split(/[,,]/).map((s, i) => ( diff --git a/dashboard/src/components/memory/MemoryConfigEditor.tsx b/dashboard/src/components/memory/MemoryConfigEditor.tsx index d9cec1c2..47f55655 100644 --- a/dashboard/src/components/memory/MemoryConfigEditor.tsx +++ b/dashboard/src/components/memory/MemoryConfigEditor.tsx @@ -1,6 +1,6 @@ import { useMemo, useState } from 'react' -import { ListFieldEditor } from '@/components' +import { ListFieldEditor } from '@/components/ListFieldEditor' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' diff --git a/dashboard/src/router.tsx b/dashboard/src/router.tsx index 5fcb42e7..f2c684d8 100644 --- a/dashboard/src/router.tsx +++ b/dashboard/src/router.tsx @@ -1,31 +1,13 @@ -import { createRootRoute, createRoute, createRouter, Outlet, redirect } from '@tanstack/react-router' +import { + createRootRoute, + createRoute, + createRouter, + lazyRouteComponent, + Outlet, + redirect, +} from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/router-devtools' -import { IndexPage } from './routes/index' -import { SettingsPage } from './routes/settings' -import { AuthPage } from './routes/auth' -import { SetupPage } from './routes/setup' import { NotFoundPage } from './routes/404' -import { BotConfigPage } from './routes/config/bot' -import { ModelProviderConfigPage } from './routes/config/modelProvider' -import { ModelConfigPage } from './routes/config/model' -import { AdapterConfigPage } from './routes/config/adapter' -import { EmojiManagementPage } from './routes/resource/emoji' -import { ExpressionManagementPage } from './routes/resource/expression' -import { JargonManagementPage } from './routes/resource/jargon' -import { PersonManagementPage } from './routes/person' -import { KnowledgeGraphPage } from './routes/resource/knowledge-graph' -import { KnowledgeBasePage } from './routes/resource/knowledge-base' -import { LogViewerPage } from './routes/logs' -import { PlannerMonitorPage } from './routes/monitor' -import { PluginsPage } from './routes/plugins' -import { ModelPresetsPage } from './routes/model-presets' -import { PluginConfigPage } from './routes/plugin-config' -import { PluginMirrorsPage } from './routes/plugin-mirrors' -import { PluginDetailPage } from './routes/plugin-detail' -import { ChatPage } from './routes/chat/index' -import { WebUIFeedbackSurveyPage, MaiBotFeedbackSurveyPage } from './routes/survey' -import PackMarketPage from './routes/config/pack-market' -import PackDetailPage from './routes/config/pack-detail' import { Layout } from './components/layout' import { checkAuth } from './hooks/use-auth' import { RouteErrorBoundary } from './components/error-boundary' @@ -50,14 +32,14 @@ const rootRoute = createRootRoute({ const authRoute = createRoute({ getParentRoute: () => rootRoute, path: '/auth', - component: AuthPage, + component: lazyRouteComponent(() => import('./routes/auth'), 'AuthPage'), }) // 首次配置路由(无 Layout) const setupRoute = createRoute({ getParentRoute: () => rootRoute, path: '/setup', - component: SetupPage, + component: lazyRouteComponent(() => import('./routes/setup/index.tsx'), 'SetupPage'), }) // 受保护的路由 Root(带 Layout) @@ -76,168 +58,192 @@ const protectedRoute = createRoute({ const indexRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/', - component: IndexPage, + component: lazyRouteComponent(() => import('./routes/index'), 'IndexPage'), }) // 配置路由 - 麦麦主程序配置 const botConfigRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/config/bot', - component: BotConfigPage, + component: lazyRouteComponent(() => import('./routes/config/bot'), 'BotConfigPage'), }) // 配置路由 - 麦麦模型提供商配置 const modelProviderConfigRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/config/modelProvider', - component: ModelProviderConfigPage, + component: lazyRouteComponent( + () => import('./routes/config/modelProvider/index.tsx'), + 'ModelProviderConfigPage' + ), }) // 配置路由 - 麦麦模型配置 const modelConfigRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/config/model', - component: ModelConfigPage, + component: lazyRouteComponent(() => import('./routes/config/model'), 'ModelConfigPage'), }) // 配置路由 - 麦麦适配器配置 const adapterConfigRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/config/adapter', - component: AdapterConfigPage, + component: lazyRouteComponent(() => import('./routes/config/adapter'), 'AdapterConfigPage'), }) // 资源管理路由 - 表情包管理 const emojiManagementRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/resource/emoji', - component: EmojiManagementPage, + component: lazyRouteComponent( + () => import('./routes/resource/emoji/index.tsx'), + 'EmojiManagementPage' + ), }) // 资源管理路由 - 表达方式管理 const expressionManagementRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/resource/expression', - component: ExpressionManagementPage, + component: lazyRouteComponent( + () => import('./routes/resource/expression/index.tsx'), + 'ExpressionManagementPage' + ), }) // 资源管理路由 - 人物信息管理 const personManagementRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/resource/person', - component: PersonManagementPage, + component: lazyRouteComponent(() => import('./routes/person'), 'PersonManagementPage'), }) // 资源管理路由 - 黑话管理 const jargonManagementRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/resource/jargon', - component: JargonManagementPage, + component: lazyRouteComponent( + () => import('./routes/resource/jargon/index.tsx'), + 'JargonManagementPage' + ), }) // 资源管理路由 - 知识库图谱可视化 const knowledgeGraphRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/resource/knowledge-graph', - component: KnowledgeGraphPage, + component: lazyRouteComponent( + () => import('./routes/resource/knowledge-graph/index.tsx'), + 'KnowledgeGraphPage' + ), }) // 资源管理路由 - 麦麦知识库管理 const knowledgeBaseRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/resource/knowledge-base', - component: KnowledgeBasePage, + component: lazyRouteComponent( + () => import('./routes/resource/knowledge-base'), + 'KnowledgeBasePage' + ), }) // 日志查看器路由 const logsRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/logs', - component: LogViewerPage, + component: lazyRouteComponent(() => import('./routes/logs'), 'LogViewerPage'), }) // MaiSaka 聊天流监控路由 const plannerMonitorRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/planner-monitor', - component: PlannerMonitorPage, + component: lazyRouteComponent(() => import('./routes/monitor/index.tsx'), 'PlannerMonitorPage'), }) // 本地聊天室路由 const chatRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/chat', - component: ChatPage, + component: lazyRouteComponent(() => import('./routes/chat/index'), 'ChatPage'), }) // 插件市场路由 const pluginsRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/plugins', - component: PluginsPage, + component: lazyRouteComponent(() => import('./routes/plugins/index'), 'PluginsPage'), }) // 插件详情路由 const pluginDetailRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/plugin-detail', - component: PluginDetailPage, + component: lazyRouteComponent(() => import('./routes/plugin-detail'), 'PluginDetailPage'), }) // 模型分配预设市场路由 const modelPresetsRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/model-presets', - component: ModelPresetsPage, + component: lazyRouteComponent(() => import('./routes/model-presets'), 'ModelPresetsPage'), }) // 插件配置路由 const pluginConfigRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/plugin-config', - component: PluginConfigPage, + component: lazyRouteComponent(() => import('./routes/plugin-config'), 'PluginConfigPage'), }) // 插件镜像源配置路由 const pluginMirrorsRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/plugin-mirrors', - component: PluginMirrorsPage, + component: lazyRouteComponent(() => import('./routes/plugin-mirrors'), 'PluginMirrorsPage'), }) // 设置页路由 const settingsRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/settings', - component: SettingsPage, + component: lazyRouteComponent(() => import('./routes/settings/index.tsx'), 'SettingsPage'), }) // 配置模板市场路由 const packMarketRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/config/pack-market', - component: PackMarketPage, + component: lazyRouteComponent(() => import('./routes/config/pack-market')), }) // 配置模板详情路由 export const packDetailRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/config/pack-market/$packId', - component: PackDetailPage, + component: lazyRouteComponent(() => import('./routes/config/pack-detail')), }) // 问卷调查路由 - WebUI 反馈 const webuiFeedbackSurveyRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/survey/webui-feedback', - component: WebUIFeedbackSurveyPage, + component: lazyRouteComponent( + () => import('./routes/survey/webui-feedback'), + 'WebUIFeedbackSurveyPage' + ), }) // 问卷调查路由 - 麦麦体验反馈 const maibotFeedbackSurveyRoute = createRoute({ getParentRoute: () => protectedRoute, path: '/survey/maibot-feedback', - component: MaiBotFeedbackSurveyPage, + component: lazyRouteComponent( + () => import('./routes/survey/maibot-feedback'), + 'MaiBotFeedbackSurveyPage' + ), }) // 404 路由 @@ -294,7 +300,7 @@ function collectRoutePaths(node: RouteNode): string[] { export const registeredRoutePaths = new Set(collectRoutePaths(routeTree as RouteNode)) // 创建路由器 -export const router = createRouter({ +export const router = createRouter({ routeTree, defaultNotFoundComponent: NotFoundPage, defaultErrorComponent: ({ error }) => , diff --git a/dashboard/src/routes/config/bot.tsx b/dashboard/src/routes/config/bot.tsx index 44c48884..6263e2ef 100644 --- a/dashboard/src/routes/config/bot.tsx +++ b/dashboard/src/routes/config/bot.tsx @@ -16,7 +16,7 @@ import { import { Button } from '@/components/ui/button' import { ScrollArea } from '@/components/ui/scroll-area' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' -import { CodeEditor } from '@/components' +import { CodeEditor } from '@/components/CodeEditor' import { DynamicConfigForm } from '@/components/dynamic-form' import { RestartOverlay } from '@/components/restart-overlay' import { useToast } from '@/hooks/use-toast' diff --git a/dashboard/src/routes/plugin-config.tsx b/dashboard/src/routes/plugin-config.tsx index 27309c5e..809a14ce 100644 --- a/dashboard/src/routes/plugin-config.tsx +++ b/dashboard/src/routes/plugin-config.tsx @@ -12,7 +12,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible' import { ListFieldEditor } from '@/components/ListFieldEditor' import { Alert, AlertDescription } from '@/components/ui/alert' -import { CodeEditor } from '@/components' +import { CodeEditor } from '@/components/CodeEditor' import { parse as parseToml } from 'smol-toml' import { Select, diff --git a/dashboard/src/routes/plugin-detail.tsx b/dashboard/src/routes/plugin-detail.tsx index 5c6eb1d5..90e964fd 100644 --- a/dashboard/src/routes/plugin-detail.tsx +++ b/dashboard/src/routes/plugin-detail.tsx @@ -37,8 +37,8 @@ import { type GitStatus, type MaimaiVersion, } from '@/lib/plugin-api' +import { MarkdownRenderer } from '@/components/markdown-renderer' import { PluginStats } from '@/components/plugin-stats' -import { MarkdownRenderer } from '@/components' import { recordPluginDownload } from '@/lib/plugin-stats' // 分类名称映射 diff --git a/dashboard/src/routes/resource/knowledge-base.tsx b/dashboard/src/routes/resource/knowledge-base.tsx index 561f8b97..b539aff2 100644 --- a/dashboard/src/routes/resource/knowledge-base.tsx +++ b/dashboard/src/routes/resource/knowledge-base.tsx @@ -16,7 +16,7 @@ import { Upload, } from 'lucide-react' -import { CodeEditor } from '@/components' +import { CodeEditor } from '@/components/CodeEditor' import { MemoryDeleteDialog } from '@/components/memory/MemoryDeleteDialog' import { MemoryConfigEditor } from '@/components/memory/MemoryConfigEditor' import { Alert, AlertDescription } from '@/components/ui/alert' diff --git a/dashboard/src/routes/resource/knowledge-graph/GraphDialogs.tsx b/dashboard/src/routes/resource/knowledge-graph/GraphDialogs.tsx index ba6b4819..3da2e1b2 100644 --- a/dashboard/src/routes/resource/knowledge-graph/GraphDialogs.tsx +++ b/dashboard/src/routes/resource/knowledge-graph/GraphDialogs.tsx @@ -171,10 +171,14 @@ export function NodeDetailDialog({ {onDeleteEntity ? (
- +
+ setIncludeParagraphs(Boolean(checked))} + aria-label="删除该实体相关证据段落" + /> + 删除该实体相关证据段落 +
{onDeleteEdgeGroup ? (
- +
+ setIncludeParagraphs(Boolean(checked))} + aria-label="同时删除支撑段落" + /> + 同时删除支撑段落 +