diff --git a/frontend/src-tauri/src/main.rs b/frontend/src-tauri/src/main.rs index 3d2fd1a..77dfaa3 100644 --- a/frontend/src-tauri/src/main.rs +++ b/frontend/src-tauri/src/main.rs @@ -129,9 +129,9 @@ async fn core_request( } #[tauri::command] -fn editor_capabilities(app: tauri::AppHandle, import_enabled: bool) -> Result<(), String> { +fn editor_capabilities(app: tauri::AppHandle, metadata_enabled: bool) -> Result<(), String> { app.state::>() - .set_enabled(import_enabled) + .set_enabled(metadata_enabled) .map_err(|_| "MENU_UNAVAILABLE".into()) } @@ -256,7 +256,7 @@ fn main() { let import = MenuItem::with_id( app, "editor.import-note-properties", - "导入为笔记属性…", + "元数据 / YAML Front Matter…", false, Some("CmdOrCtrl+Alt+P"), )?; diff --git a/frontend/src/components/common/TitleBarMenu.spec.ts b/frontend/src/components/common/TitleBarMenu.spec.ts index 1cb7bb4..d630264 100644 --- a/frontend/src/components/common/TitleBarMenu.spec.ts +++ b/frontend/src/components/common/TitleBarMenu.spec.ts @@ -3,19 +3,26 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { mount } from '@vue/test-utils' import { createPinia, setActivePinia } from 'pinia' import { useEditorStore } from '@/stores/editor' +import { useWorkspaceStore } from '@/stores/workspace' import TitleBarMenu from './TitleBarMenu.vue' const execute = vi.hoisted(() => vi.fn()) const capabilities = vi.hoisted(() => vi.fn()) +const routerPush = vi.hoisted(() => vi.fn()) vi.mock('@/services/editorCommandService', () => ({ executeEditorCommand: execute, getEditorCommandCapabilities: capabilities, subscribeEditorCommandCapabilities: () => () => undefined, })) +vi.mock('vue-router', () => ({ + useRouter: () => ({ push: routerPush }), + useRoute: () => ({ name: 'workspace' }), +})) beforeEach(() => { setActivePinia(createPinia()) execute.mockReset().mockResolvedValue({ ok: true }) + routerPush.mockReset() capabilities.mockReturnValue([ { id: 'editor.paragraph', supported: true, enabled: true }, { id: 'editor.heading', supported: true, enabled: true }, @@ -25,6 +32,33 @@ beforeEach(() => { }) describe('桌面顶部段落菜单', () => { + it('文件菜单承载实际工作区命令和笔记导出', async () => { + const editor = useEditorStore() + editor.currentFilePath = '/示例.md' + editor.content = '# 示例' + editor.saveStatus = 'saved' + const wrapper = mount(TitleBarMenu, { global: { stubs: { ExportDialog: { template: '
' } } } }) + await wrapper.get('[data-menu="file"] .menu-trigger').trigger('click') + expect(wrapper.text()).toContain('新建笔记…') + expect(wrapper.text()).toContain('打开其他知识库…') + expect(wrapper.text()).toContain('刷新文件树') + expect(wrapper.text()).toContain('下载 Markdown 副本') + await wrapper.get('.export-command').trigger('click') + expect(wrapper.find('[data-testid="export-dialog"]').exists()).toBe(true) + wrapper.unmount() + }) + + it('视图和帮助菜单只连接项目已有页面', async () => { + useWorkspaceStore().hasVault = true + const wrapper = mount(TitleBarMenu) + await wrapper.get('[data-menu="view"] .menu-trigger').trigger('click') + expect(wrapper.text()).toContain('工作区搜索AI 对话智能体任务音视频') + expect(wrapper.text()).toContain('Skill 管理Plugin 管理MCP 服务器') + await wrapper.get('[data-menu="help"] .menu-trigger').trigger('click') + expect(wrapper.text()).toContain('运行日志Benchmark 评测社区目录设置与诊断…') + wrapper.unmount() + }) + it('源码笔记通过统一编辑命令执行属性导入', async () => { const editor = useEditorStore() editor.mode = 'source' diff --git a/frontend/src/components/common/TitleBarMenu.vue b/frontend/src/components/common/TitleBarMenu.vue index 0acb56b..a31fb3d 100644 --- a/frontend/src/components/common/TitleBarMenu.vue +++ b/frontend/src/components/common/TitleBarMenu.vue @@ -1,23 +1,47 @@