diff --git a/UI-DESIGN.md b/UI-DESIGN.md index 1b5873fe..c90e6464 100644 --- a/UI-DESIGN.md +++ b/UI-DESIGN.md @@ -794,7 +794,7 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证 - 魔法笔记设置提供“发送画布页数”单选框,复用设置表单 `.field` 控件样式,范围 1~8,默认 1。标题同行的问号使用 `InlineHelp`,下拉框通过稳定的帮助 ID 关联展开的说明,交互遵循 6.14。说明包含当前页序前 N 页、不足时发送全部页、数量指单个画布页数而非记录数,以及完整保存与导出不受影响。修改后沿用设置保存反馈,下次分析生效。 - 草稿、已保存画布记录及来源为画布的待办,按默认模型的 `supportsImageInput` 使用所选前 N 页图像与文字,或只发送同一范围文字并标注“仅文字分析(未使用画布图像)”。所选页只有视觉内容的记录/草稿遇到文字模型时提示切换支持图像输入的默认模型,不声称已理解笔迹或布局。待办分析同时保留任务标题、说明和所选来源画布页的上下文。 - 画布文字评论在提取的 `plainText` 不变时保留;图像评论随画布内容或布局变化失效,在保存后自动评论模式下重新分析。草稿内容变化清除旧草稿分析,评论区显示实际使用的输入模式。存储和判定细节见[画布技术设计](./docs/features/magic-notes/technical-design.md#canvas-analysis-and-comments)。 -- 总览是全宽的“全部笔记”页,以自适应卡片网格(最小 `260px`)显示全部笔记,不显示右侧空白详情,也不自动打开第一篇;卡片显示标题、短日期、两行预览和记录数。打开笔记后切为左侧紧凑列表与右侧详情,页头右上角显示带文字的“返回总览”,点击回到全部笔记页并恢复搜索、滚动位置与卡片焦点;“切换到待办”只在总览显示。列表按紧凑两行行项显示:第一行为标题与短日期(今天显示时间,其余显示月/日,完整时间在工具提示中),第二行为单行预览;记录数只提供给读屏。行项无边框、透明底色,悬停为次级底色,选中为淡蓝底色加左侧 `3px` 强调标记;“更多”按钮悬停或聚焦时覆盖在日期位置。保留搜索、新建、置顶菜单和当前选中状态。打开笔记只更新右侧详情;列表独立滚动,切换保留搜索和列表位置,并在工作区挂载期间记住各篇笔记的阅读位置。明确定位某条记录时优先跳到该记录。 +- 总览是全宽的“全部笔记”页,以自适应卡片网格(最小 `260px`)显示全部笔记,不显示右侧空白详情,也不自动打开第一篇;卡片显示标题、短日期、两行预览和记录数。打开笔记后切为左侧紧凑列表与右侧详情,页头右上角显示带文字的“返回总览”,点击回到全部笔记页并恢复搜索、滚动位置与卡片焦点;“切换到待办”只在总览显示。列表按紧凑两行行项显示:第一行为标题与短日期(今天显示时间,其余显示月/日,完整时间在工具提示中),第二行为单行预览;记录数只提供给读屏。行项无边框、透明底色,悬停为次级底色,选中为淡蓝底色加左侧 `3px` 强调标记;“更多”按钮悬停或聚焦时覆盖在日期位置。保留搜索、置顶菜单和当前选中状态。打开笔记只更新右侧详情;列表独立滚动,切换保留搜索和列表位置,并在工作区挂载期间记住各篇笔记的阅读位置。明确定位某条记录时优先跳到该记录。 - 笔记列表默认展开、宽 `280px`,支持拖动分隔线在 `240–420px` 内调宽,方向键每次调整 `16px`,Home/End 跳到边界;桌面端详情页提供列表显隐开关,记住宽度和展开偏好。工作区宽度不超过 `900px` 时切换显示列表与详情,返回恢复列表位置和入口焦点,不覆盖桌面偏好。新建成功后打开新笔记;切换、新建和删除当前笔记都保护未保存的正文与标题。 - 待办页是独立任务视图:搜索任务标题、说明或来源笔记,搜索框右侧同一工具栏放置“全部 / 未完成 / 已完成”筛选,显示匹配数与总数;任务按来源笔记分组。列表独立滚动,页头、搜索和筛选保持可见,窄屏工具栏空间不足时换行。直接勾选完成或恢复未完成,不自动选择任务;状态变化后按当前筛选更新列表。 - 待办内容容器宽于 `700px` 时,任务列表稳定在左侧,右侧显示当前任务说明、来源记录和 AI 评论;未选择时右侧提示选择任务。点击任务只更新右侧,不改变列表宽度、行位置或滚动位置,再次点击取消选择。AI 不增加第三栏。容器不超过 `700px` 时,按需详情替换同一内容区域,并提供“返回待办列表”;列表保持挂载,返回恢复滚动和任务行焦点,不将列表与详情上下堆叠。任务按钮提供 `aria-expanded` 和 `aria-controls`。 @@ -813,7 +813,7 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证 - 画布新建、编辑和只读态统一保留 `12px` 内容间距;详情中已保存画布去掉预览外框和工具栏底色、下划线,保留灰色画布衬底与白色纸张,编辑态保留编辑器边框。记录头部操作按内容宽度排列,空间不足时自然换行。文字新建的保存区与编辑器属于同一卡片,空白编辑区使用最小高度,不随容器百分比高度拉长。文字工具栏图标、菜单和原生滚动条须跟随主题。 - 标签使用中性胶囊样式(`--surface-subtle` 底色、`--border-subtle` 边框、`--font-caption`),不使用颜色区分。总览搜索框下方是标签筛选行:“全部”加各标签和对应篇数,均为 `aria-pressed` 切换按钮;可以多选,多选时只显示同时带有全部所选标签的笔记。选中时使用 `--accent-subtle` 底色和强调色边框。卡片最多显示 3 个标签,其余合并为“+N”。打开笔记后,左侧列表不显示筛选行,只显示“已筛选:A · B”提示条和清除按钮。 - 笔记标题下方是标签编辑行:已有标签后带 × 移除按钮,末尾是虚线“+ 标签”按钮。点击后展开组合框,按输入内容过滤已有标签,也可以新建。方向键切换选项,Enter、逗号或中文逗号确认,Escape 关闭并把焦点还给“+ 标签”。达到 10 个时,“+ 标签”禁用并说明上限。 -- 列表标题行的新建按钮左侧是“管理标签”:总览显示图标加文字,打开笔记后只显示图标。点击后打开共享 `custom-task-dialog` 对话框,按使用篇数列出标签,每行提供重命名和删除图标按钮。重命名在行内编辑;新名称与已有标签相同时,提示保存后会合并。删除在行内二次确认,并说明涉及的篇数。Escape 依次退出行内编辑或确认,最后关闭对话框。 +- 列表标题行右侧只放“管理标签”,不再放 `+` 新建按钮。“新建笔记”统一放在页头右上角,是主按钮,始终位于“切换到待办 / 切换到笔记”或“返回总览”的右侧。打开笔记时点击,桌面端展开左侧列表并显示标题输入,窄屏先回到列表。“管理标签”在总览显示图标加文字,打开笔记后只显示图标。点击后打开共享 `custom-task-dialog` 对话框,按使用篇数列出标签,每行提供重命名和删除图标按钮。重命名在行内编辑;新名称与已有标签相同时,提示保存后会合并。删除在行内二次确认,并说明涉及的篇数。Escape 依次退出行内编辑或确认,最后关闭对话框。 - 整篇笔记的置顶、取消置顶和删除放在列表行的“更多”菜单,打开详情时仍可操作,标题编辑区不重复提供入口。按钮在悬停、键盘聚焦或菜单展开时显示,触屏设备常显;菜单沿用会话列表样式,通过 Portal 避免滚动区域裁切,支持方向键、Home、End 和 Escape。删除在菜单内二次确认并说明笔记标题及全部记录;删除当前笔记还须处理未保存草稿,删除其他笔记保留当前内容与草稿。打开菜单不切换笔记。 - 左侧“魔法笔记”入口可显示未完成待办数量。数量为 0 时隐藏徽标,超过 99 时显示 `99+`,可访问名称仍说明精确数量;勾选、取消勾选以及 Agent / MCP 对笔记清单的写入都必须刷新数量。 - 未完成待办徽标的高度不得超过主导航图标;数量从 0 变为可见或再次隐藏时,主导航项行高和相邻菜单位置必须保持不变。 diff --git a/docs/features/magic-notes/README.md b/docs/features/magic-notes/README.md index ba1c3618..bc00a4d7 100644 --- a/docs/features/magic-notes/README.md +++ b/docs/features/magic-notes/README.md @@ -22,8 +22,9 @@ comments. The divider can be dragged and remembers its width; each note group can independently collapse its tasks while retaining its title and count. Narrow containers use on-demand detail with an explicit return to the mounted list. One content-sized button switches to the other view in the page -header when note detail is closed; New note lives in the notes list heading or the -To-dos page header. Status filters sit beside search and wrap +header when note detail is closed; New note is always the primary page-header +action to the right of the view switch or Back to overview. The notes list +heading keeps only Manage tags. Status filters sit beside search and wrap when needed. Note detail removes the surrounding frame and stream padding while keeping editor borders and the AI divider. Returning from a source note restores the task context and protects unsaved editor changes. A successful save followed by no diff --git a/docs/features/magic-notes/progress.md b/docs/features/magic-notes/progress.md index a6f04fca..de286130 100644 --- a/docs/features/magic-notes/progress.md +++ b/docs/features/magic-notes/progress.md @@ -1,5 +1,15 @@ # Magic Notes Progress +## 2026-10-01: Header New Note Action + +New note is now one primary page-header button that always follows the view +switch (overview, to-dos) or Back to overview (note detail). The `+` icon in the +notes list heading was removed; the heading keeps only Manage tags. From note +detail the button opens the list's title form (desktop reopens a collapsed list; +narrow returns to the list first). Workspace tests (133) and the Electron layout +test pass, with overview and detail screenshots checked at 1280px; the App note +subset (11), typecheck, and lint on changed files pass. + ## 2026-09-30: Navigate After Record Mount Quick Notes navigation now scrolls after React commits the target record, rather diff --git a/src/renderer/src/MagicNotesWorkspace.test.tsx b/src/renderer/src/MagicNotesWorkspace.test.tsx index 88fd6a48..24caafae 100644 --- a/src/renderer/src/MagicNotesWorkspace.test.tsx +++ b/src/renderer/src/MagicNotesWorkspace.test.tsx @@ -518,7 +518,11 @@ describe('MagicNotesWorkspace overview navigation', () => { expect(css).toMatch(/\.magic-notes-page #magic-library-switch\s*\{[^}]*flex: 0 0 auto/) expect(css).toMatch(/\.magic-notes-page #magic-library-switch\s*\{[^}]*width: fit-content/) const switchButton = screen.getByRole('button', { name: '切换到待办' }) - expect(screen.getByRole('region', { name: '笔记列表' })).toContainElement(screen.getByRole('button', { name: '新建笔记' })) + // New note sits in the page header, directly after the library switch; the list heading has no + button. + const newNoteButton = screen.getByRole('button', { name: '新建笔记' }) + expect(newNoteButton.closest('.page-header__actions')).not.toBeNull() + expect(switchButton.nextElementSibling).toBe(newNoteButton) + expect(container.querySelector('#magic-library-panel-notes .magic-notes-pane-heading .icon-button')).toBeNull() expect(switchButton.querySelector('.lucide-list-todo')).toBeInTheDocument() switchButton.focus() fireEvent.click(switchButton) @@ -527,6 +531,7 @@ describe('MagicNotesWorkspace overview navigation', () => { expect(screen.getByRole('region', { name: '待办列表' })).toBeVisible() expect(container.querySelector('.magic-notes-layout')).not.toHaveClass('magic-notes-layout--detail') expect(screen.queryByRole('tabpanel')).not.toBeInTheDocument() + expect(switchButton.nextElementSibling).toBe(screen.getByRole('button', { name: '新建笔记' })) await waitFor(() => expect(switchButton).toHaveFocus()) fireEvent.click(switchButton) expect(screen.getByRole('button', { name: '切换到待办' })).toBe(switchButton) @@ -554,6 +559,10 @@ describe('MagicNotesWorkspace overview navigation', () => { expect(screen.getByRole('separator', { name: '调整笔记列表宽度' })).toBeVisible() // Back to overview lives in the page header's top-right actions while a note is open. expect(screen.getByRole('button', { name: '返回总览' }).closest('.page-header__actions')).not.toBeNull() + expect(screen.getByRole('button', { name: '返回总览' }).nextElementSibling).toBe(screen.getByRole('button', { name: '新建笔记' })) + fireEvent.click(screen.getByRole('button', { name: '新建笔记' })) + expect(within(overview).getByLabelText('笔记标题')).toBeVisible() + fireEvent.click(within(overview).getByRole('button', { name: '取消' })) expect(screen.queryByRole('button', { name: '切换到待办' })).not.toBeInTheDocument() expect(screen.getByRole('searchbox')).toBeVisible() expect(screen.getByRole('button', { name: '新建笔记' })).toBeVisible() diff --git a/src/renderer/src/MagicNotesWorkspace.tsx b/src/renderer/src/MagicNotesWorkspace.tsx index 25a0bdf8..10d741c4 100644 --- a/src/renderer/src/MagicNotesWorkspace.tsx +++ b/src/renderer/src/MagicNotesWorkspace.tsx @@ -2178,22 +2178,26 @@ export function MagicNotesWorkspace({ {libraryView === 'notes' ?