fix: keep New note in the magic notes page header
This commit is contained in:
+2
-2
@@ -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 变为可见或再次隐藏时,主导航项行高和相邻菜单位置必须保持不变。
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -2178,22 +2178,26 @@ export function MagicNotesWorkspace({
|
||||
{libraryView === 'notes' ? <ListTodo aria-hidden="true" size={15} /> : <BookOpen aria-hidden="true" size={15} />}
|
||||
{t(libraryView === 'notes' ? 'actions.switchToTodos' : 'actions.switchToNotes')}
|
||||
</button>
|
||||
{libraryView === 'todos' && <button
|
||||
id="magic-note-new"
|
||||
className="primary-button"
|
||||
disabled={Boolean(busy)}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setValidation(undefined)
|
||||
setLibraryView('notes')
|
||||
setCreating(true)
|
||||
}}
|
||||
>
|
||||
<Plus aria-hidden="true" size={15} />
|
||||
{t('actions.newNote')}
|
||||
</button>}
|
||||
</>
|
||||
)}
|
||||
<button
|
||||
id="magic-note-new"
|
||||
className="primary-button"
|
||||
disabled={Boolean(busy)}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setValidation(undefined)
|
||||
setLibraryView('notes')
|
||||
setCreating(true)
|
||||
if (detailView) {
|
||||
if (isNarrowWorkspace) void requestDraftSwitch({ kind: 'overview' })
|
||||
else setNotesPaneOpen(true)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Plus aria-hidden="true" size={15} />
|
||||
{t('actions.newNote')}
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
help={t('page.description')}
|
||||
@@ -2265,11 +2269,6 @@ export function MagicNotesWorkspace({
|
||||
disabled={Boolean(busy)}
|
||||
onClick={() => setTagManagerOpen(true)}
|
||||
/>
|
||||
<button id="magic-note-new" type="button" className="icon-button"
|
||||
disabled={Boolean(busy)} aria-label={t('actions.newNote')} title={t('actions.newNote')}
|
||||
onClick={() => { setValidation(undefined); setCreating(true) }}>
|
||||
<Plus size={15} />
|
||||
</button>
|
||||
</div>
|
||||
<label className="magic-notes-search">
|
||||
<span className="sr-only">{t('notes.searchLabel')}</span>
|
||||
|
||||
@@ -1408,21 +1408,16 @@
|
||||
.magic-note-list-item[aria-current='true'] { border-color: Highlight; }
|
||||
}
|
||||
|
||||
/* Heading: title and count grouped left, new-note action right. */
|
||||
/* Heading: title and count grouped left, tag management right. New note lives in the page header. */
|
||||
#magic-library-panel-notes > .magic-notes-pane-heading {
|
||||
justify-content: flex-start;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
#magic-library-panel-notes > .magic-notes-pane-heading > .icon-button,
|
||||
#magic-library-panel-notes > .magic-notes-pane-heading > #magic-note-tag-manager {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#magic-library-panel-notes > .magic-notes-pane-heading > #magic-note-tag-manager + .icon-button {
|
||||
margin-left: var(--space-1);
|
||||
}
|
||||
|
||||
.magic-note-tag-manager-button {
|
||||
min-height: 28px;
|
||||
margin-block: -4px;
|
||||
|
||||
Reference in New Issue
Block a user