From a189b89942c67e37c7e671cc70e63cf61793fc24 Mon Sep 17 00:00:00 2001 From: KiriAky 107 Date: Mon, 7 Sep 2026 19:25:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(desktop):=20=E8=A1=A5=E9=BD=90=E9=A1=B6?= =?UTF-8?q?=E9=83=A8=E6=AE=B5=E8=90=BD=E8=8F=9C=E5=8D=95=E4=B8=8E=E6=B5=AE?= =?UTF-8?q?=E5=8A=A8=E6=8C=89=E9=92=AE=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/contracts/Host-v1契约.md | 2 +- .../Tauri-Rust桌面客户端需求说明-第三阶段.md | 6 +- docs/development/第三阶段实施与验收记录.md | 2 +- docs/guides/代码注释与TODO约定.md | 2 +- frontend/src/components/common/TitleBar.vue | 2 + .../components/common/TitleBarMenu.spec.ts | 42 ++++++++++++ .../src/components/common/TitleBarMenu.vue | 67 +++++++++++++++++++ .../src/features/workspace/WorkspaceView.vue | 2 +- 8 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 frontend/src/components/common/TitleBarMenu.spec.ts create mode 100644 frontend/src/components/common/TitleBarMenu.vue diff --git a/docs/contracts/Host-v1契约.md b/docs/contracts/Host-v1契约.md index c21de31..1eef8d7 100644 --- a/docs/contracts/Host-v1契约.md +++ b/docs/contracts/Host-v1契约.md @@ -34,6 +34,6 @@ OS 文件锁配合 Rust Mutex 维持单实例 Vault 写入。Web `serialized_vau ## 前端与发布 -Web 模式沿用现有服务。Tauri Workspace Service 调用原生命令;AI Core 未接通时明确返回 CORE_UNAVAILABLE。桌面窗口关闭系统装饰,统一由应用标题栏提供拖动、最小化、最大化/还原及关闭;窗口命令只授予本地 `main` WebView。关闭请求先尝试保存,冲突或保存失败时保持窗口。原生“导入为笔记属性”菜单仅在源码模式和已打开笔记时启用,转换作为单次编辑器历史事务执行。 +Web 模式沿用现有服务。Tauri Workspace Service 调用原生命令;AI Core 未接通时明确返回 CORE_UNAVAILABLE。桌面窗口关闭系统装饰,统一由应用标题栏提供拖动、最小化、最大化/还原及关闭;窗口命令只授予本地 `main` WebView。关闭请求先尝试保存,冲突或保存失败时保持窗口。原生菜单事件和标题栏可见的“段落”菜单复用 `editor.import-note-properties`;仅在源码模式、已打开笔记且无冲突时启用,转换作为单次编辑器历史事务执行。 `bundle.active=false`,无自动更新、无签名安装包。开发命令为前端 `pnpm dev` 与 `cargo run --features desktop`;前端生产构建后可 `cargo build --features desktop --release`,仍只构建预览程序。需要对应平台 C++ 工具链和 WebView;本次 Windows 已使用系统 `x86_64-pc-windows-gnu` 工具链构建,仍不替代 MSVC 或其他平台验证。 diff --git a/docs/contracts/Tauri-Rust桌面客户端需求说明-第三阶段.md b/docs/contracts/Tauri-Rust桌面客户端需求说明-第三阶段.md index 1757a6d..7b88a8b 100644 --- a/docs/contracts/Tauri-Rust桌面客户端需求说明-第三阶段.md +++ b/docs/contracts/Tauri-Rust桌面客户端需求说明-第三阶段.md @@ -1,6 +1,6 @@ # Tauri / Rust 桌面客户端需求说明(第三阶段) -状态:需求预留,尚未实现桌面客户端。本文不表示已有可调用的 Tauri Command 或可发布安装包。 +状态:需求基线;Rust Host、原生 Vault、无边框窗口控制和属性导入已进入 Alpha,Sidecar、Stronghold、多窗口及发布包仍未完成。本文不表示已有可发布安装包。 基线日期:2026-09-06。第三阶段完整范围与实施顺序见[第三阶段实施规划](../architecture/第三阶段实施规划.md)。 @@ -21,7 +21,7 @@ 桌面客户端顶部菜单栏的 **段落 → 导入为笔记属性…** 预留元数据格式导入功能,与标题、正文、列表等段落操作归组。它处理笔记内容中的元数据,不是主题包安装入口。 -稳定的前端命令标识为 `editor.import-note-properties`,已进入前端 v1 命令目录,但属性转换处理器与 Tauri IPC 尚未实现。原生菜单和编辑器命令面板应分发同一命令,避免两套转换逻辑。快捷键待第三阶段统一分配,不抢占现有编辑快捷键。 +稳定的前端命令标识为 `editor.import-note-properties`。属性转换处理器、Tauri 原生菜单事件和无边框窗口内的可见“段落”菜单均分发同一命令,没有维护第二套转换逻辑。快捷键待第三阶段统一分配,不抢占现有编辑快捷键。 Markdown 格式(含警告框)与元数据共用 `editorCommandService` 的能力查询和命令分发接口,详见 [警告框与桌面编辑命令开发说明](../development/警告框与桌面编辑命令开发说明.md)。Host 根据 supported / enabled 显隐或禁用菜单,不能把已预留的命令 ID 当作已可执行能力;原生快捷键不绕过活动文档、只读和冲突检查。 @@ -41,7 +41,7 @@ Markdown 格式(含警告框)与元数据共用 `editorCommandService` 的 - 转换作用于当前内存文档,不先从磁盘读取旧内容覆盖未保存编辑。操作绑定文件标识和文档版本,异步处理期间切换文件或继续编辑时,应取消或重新校验。 - 成功后进入现有脏状态和自动保存流程。磁盘保存失败显示可重试状态,撤销/重做同时恢复正文、属性及标签。 - 属性块不进入正文大纲;标题跳转、引用定位仍使用完整原文件的正确偏移。写作/源码切换、保存后重开不得改变属性语义。 -- 当前分支的 `frontend/src/features/editor/noteMetadata.ts` 仅是简单属性块展示与标签编辑基础;桌面阶段需补齐完整解析、合并冲突、单事务撤销和原生菜单分发,不能直接视为本节已经验收。 +- 当前 Alpha 已覆盖完整解析、合并冲突、单事务撤销和菜单分发的自动测试;写作模式属性面板、重开后的标签检索、缩放和真实 WebView 操作仍须按本节验收,不能仅凭单元测试视为全部通过。 ## 3. 桌面基础需求 diff --git a/docs/development/第三阶段实施与验收记录.md b/docs/development/第三阶段实施与验收记录.md index bf123cf..4a9e42f 100644 --- a/docs/development/第三阶段实施与验收记录.md +++ b/docs/development/第三阶段实施与验收记录.md @@ -17,7 +17,7 @@ ## 已执行验证 - 后端:887 项 pytest 通过;新增 Web/桌面单写入者门禁测试。 -- 前端:88 个文件、474 项 Vitest 通过;TypeScript 检查与 Vite 生产构建通过,保留既有大 chunk 提示。桌面标题栏窗口操作使用 Tauri capability,Web 模式不显示伪窗口按钮。 +- 前端:89 个文件、476 项 Vitest 通过;TypeScript 检查与 Vite 生产构建通过,保留既有大 chunk 提示。桌面标题栏窗口操作使用 Tauri capability,Web 模式不显示伪窗口按钮;标题栏“段落”菜单复用编辑命令能力门禁。AI 与文档滚动按钮改为同一底边横向错位,避免双滚动按钮出现时重叠。 - Sync v1:18 项隔离测试通过,Windows / Python 3.13.9 / SQLite / 磁盘对象 Fixture。包括双线程并发 CAS;不是 PostgreSQL 多 Worker 压测。 - Community v1:13 项隔离测试通过;前端使用 Python 生成的真实 Ed25519 向量互验。 - Rust:系统 `rustc/cargo 1.98.1`(`x86_64-pc-windows-gnu`)下,7 项文件恢复及最近 Vault 授权测试、rustfmt、clippy `-D warnings` 和 Tauri desktop 开发构建通过。没有安装包或新标题栏实机 UI 证据。 diff --git a/docs/guides/代码注释与TODO约定.md b/docs/guides/代码注释与TODO约定.md index 7bd5dab..c952c00 100644 --- a/docs/guides/代码注释与TODO约定.md +++ b/docs/guides/代码注释与TODO约定.md @@ -36,7 +36,7 @@ | Security | 本地主密钥目前保存在数据目录;Rust Vault 文件服务已接入,Stronghold 迁移尚未完成 | | Extension | 安装状态已持久化、社区发行签名原型已接入;生产 OS 隔离和桌面 Extension Manager 尚未完成 | | AI Core | 音频转写当前只读取文本或 Host 预生成旁路文本,后续接入本地 ASR 队列 | -| Desktop | Tauri IPC 已支持单个原生 Vault 的选择、版本保存和恢复;最近列表、多窗口、监听、Sidecar 与同步循环尚未完成 | +| Desktop | Tauri IPC 已支持原生 Vault 选择、最近授权重开、版本保存和恢复;多窗口、监听、Sidecar 与同步循环尚未完成 | | Editor / Chat | 待补文件冲突合并及受控链接对话框;会话持久化已接入后端 SQLite | | Performance | Shiki 已复用单例,后续按首屏指标评估延迟加载或 Web Worker | diff --git a/frontend/src/components/common/TitleBar.vue b/frontend/src/components/common/TitleBar.vue index da5819a..cc087c8 100644 --- a/frontend/src/components/common/TitleBar.vue +++ b/frontend/src/components/common/TitleBar.vue @@ -6,6 +6,7 @@ import { useEditorStore } from '@/stores/editor' import { useThemeStore } from '@/stores/theme' import { Moon, Sunny } from '@element-plus/icons-vue' import AppIcon from './AppIcon.vue' +import TitleBarMenu from './TitleBarMenu.vue' import { t } from '@/i18n' import { isDesktop } from '@/services/platform/desktop' import { minimizeWindow, requestWindowClose, toggleMaximizeWindow } from '@/services/platform/windowControls' @@ -61,6 +62,7 @@ function toggleFromTitlebar(event: MouseEvent) { {{ currentFileName }} +
NotesAgent diff --git a/frontend/src/components/common/TitleBarMenu.spec.ts b/frontend/src/components/common/TitleBarMenu.spec.ts new file mode 100644 index 0000000..c13c061 --- /dev/null +++ b/frontend/src/components/common/TitleBarMenu.spec.ts @@ -0,0 +1,42 @@ +// @vitest-environment happy-dom +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 TitleBarMenu from './TitleBarMenu.vue' + +const execute = vi.hoisted(() => vi.fn()) +vi.mock('@/services/editorCommandService', () => ({ executeEditorCommand: execute })) + +beforeEach(() => { + setActivePinia(createPinia()) + execute.mockReset().mockResolvedValue({ ok: true }) +}) + +describe('桌面顶部段落菜单', () => { + it('源码笔记通过统一编辑命令执行属性导入', async () => { + const editor = useEditorStore() + editor.mode = 'source' + editor.currentFilePath = '/示例.md' + editor.saveStatus = 'saved' + const wrapper = mount(TitleBarMenu) + await wrapper.get('.menu-trigger').trigger('click') + const item = wrapper.get('[role="menuitem"]') + expect((item.element as HTMLButtonElement).disabled).toBe(false) + await item.trigger('click') + expect(execute).toHaveBeenCalledWith('editor.import-note-properties') + expect(wrapper.find('[role="menu"]').exists()).toBe(false) + wrapper.unmount() + }) + + it('写作模式显示入口但禁止绕过编辑器能力', async () => { + const editor = useEditorStore() + editor.mode = 'wysiwyg' + editor.currentFilePath = '/示例.md' + const wrapper = mount(TitleBarMenu) + await wrapper.get('.menu-trigger').trigger('click') + expect((wrapper.get('[role="menuitem"]').element as HTMLButtonElement).disabled).toBe(true) + expect(wrapper.text()).toContain('请在无冲突的 Markdown 源码笔记中使用') + wrapper.unmount() + }) +}) diff --git a/frontend/src/components/common/TitleBarMenu.vue b/frontend/src/components/common/TitleBarMenu.vue new file mode 100644 index 0000000..9918929 --- /dev/null +++ b/frontend/src/components/common/TitleBarMenu.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/frontend/src/features/workspace/WorkspaceView.vue b/frontend/src/features/workspace/WorkspaceView.vue index 04291e7..9a35808 100644 --- a/frontend/src/features/workspace/WorkspaceView.vue +++ b/frontend/src/features/workspace/WorkspaceView.vue @@ -33,7 +33,7 @@ const workspaceStore = useWorkspaceStore()