From f5ee9b219822d13d73b46d2c3f5db69a968c2091 Mon Sep 17 00:00:00 2001 From: mesalogo Date: Mon, 17 Aug 2026 10:33:19 +0800 Subject: [PATCH] fix: refine Magic Notes editing layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Magic Notes editor had ambiguous font-size labels, cramped pane spacing, and a fixed short writing area. The toolbar now groups controls and uses numeric sizes, the pane and empty states are balanced, and the editor can be resized vertically from a taller default. Release note: 优化魔法笔记编辑体验:字号改为数字、工具栏与分栏布局更清晰,输入区域支持纵向拖拽并默认显示更多内容。 --- src/renderer/src/MagicNoteEditor.test.tsx | 14 +- src/renderer/src/MagicNoteEditor.tsx | 217 ++++++++++-------- src/renderer/src/MagicNotesWorkspace.test.tsx | 22 +- src/renderer/src/MagicNotesWorkspace.tsx | 48 ++-- .../src/i18n/locales/en-US/magicNotes.ts | 17 +- .../src/i18n/locales/zh-CN/magicNotes.ts | 16 +- src/renderer/src/styles.css | 69 +++++- 7 files changed, 258 insertions(+), 145 deletions(-) diff --git a/src/renderer/src/MagicNoteEditor.test.tsx b/src/renderer/src/MagicNoteEditor.test.tsx index 72cea6f..0f33eea 100644 --- a/src/renderer/src/MagicNoteEditor.test.tsx +++ b/src/renderer/src/MagicNoteEditor.test.tsx @@ -4,8 +4,8 @@ import type { MagicNoteRichContent } from '../../shared/magic-notes-contracts' import { MagicNoteEditor } from './MagicNoteEditor' describe('MagicNoteEditor', () => { - it('exposes font size, text color, and attachment controls', () => { - render( + it('exposes numeric font sizes, text color, and attachment controls', () => { + const { container } = render( { /> ) + expect( + screen.getByRole('toolbar', { name: '笔记格式工具栏' }) + ).toBeInTheDocument() + expect(container.querySelectorAll('.ql-formats')).toHaveLength(5) expect(screen.getByLabelText('字体大小')).toBeInTheDocument() + expect( + Array.from( + container.querySelectorAll('.ql-size .ql-picker-item'), + (item) => item.getAttribute('data-label') + ) + ).toEqual(['12', '14', '18', '24']) expect(screen.getByLabelText('字体颜色')).toBeInTheDocument() expect( screen.getByRole('button', { name: '上传视频或附件' }) diff --git a/src/renderer/src/MagicNoteEditor.tsx b/src/renderer/src/MagicNoteEditor.tsx index c7c67d0..74553c8 100644 --- a/src/renderer/src/MagicNoteEditor.tsx +++ b/src/renderer/src/MagicNoteEditor.tsx @@ -454,107 +454,122 @@ export function MagicNoteEditor({ } }} > -
- - - + + + + + + + + + { ).toBeInTheDocument() }) - it('keeps history editing contained and uses standard delete buttons', async () => { + it('keeps history editing contained and de-emphasizes note deletion', async () => { const { container } = render( ) @@ -389,8 +389,11 @@ describe('MagicNotesWorkspace', () => { const deleteEntry = screen.getByRole('button', { name: '删除记录' }) - expect(deleteNote).toHaveClass('danger-button', 'danger-button--quiet') - expect(deleteNote).toHaveTextContent('删除笔记') + expect(deleteNote).toHaveClass( + 'icon-button', + 'magic-note-detail-header__delete' + ) + expect(deleteNote).toHaveTextContent('') expect(deleteEntry).toHaveClass('danger-button', 'danger-button--quiet') expect(deleteEntry).toHaveTextContent('删除记录') @@ -445,6 +448,17 @@ describe('MagicNotesWorkspace', () => { expect(pane).toBeVisible() }) + it('shows a concise empty state before a note has AI comments', async () => { + get.mockResolvedValueOnce(alternateDetail(noteId, detail.title)) + + render() + + expect(await screen.findByText('暂无 AI 评论')).toBeInTheDocument() + expect( + screen.getByText('写完一句并停止输入 5 秒后,评论会显示在这里。') + ).toBeInTheDocument() + }) + it('resizes the AI comments pane with pointer and keyboard controls', async () => { const originalInnerWidth = window.innerWidth Object.defineProperty(window, 'innerWidth', { @@ -466,6 +480,8 @@ describe('MagicNotesWorkspace', () => { releasePointerCapture: { value: releasePointerCapture } }) + expect(separator).toHaveAttribute('aria-valuenow', '280') + fireEvent.pointerDown(separator, { button: 0, clientX: 800, diff --git a/src/renderer/src/MagicNotesWorkspace.tsx b/src/renderer/src/MagicNotesWorkspace.tsx index b1a7d48..7ec5146 100644 --- a/src/renderer/src/MagicNotesWorkspace.tsx +++ b/src/renderer/src/MagicNotesWorkspace.tsx @@ -62,7 +62,7 @@ type ValidationTarget = | 'new-entry' | 'edit-entry' -const defaultAiPaneWidth = 300 +const defaultAiPaneWidth = 280 const minimumAiPaneWidth = 240 const maximumAiPaneWidth = 520 const magicNotesListPaneWidth = 220 @@ -1611,12 +1611,12 @@ export function MagicNotesWorkspace({
@@ -2128,14 +2128,18 @@ export function MagicNotesWorkspace({ ) : null} {libraryView === 'todos' ? ( !selectedTodo ? ( -

- {t('comments.selectTodo')} -

+ } + title={t('comments.emptyTitle')} + /> ) : selectedTodo.comments.length === 0 ? ( !liveAnalysis && ( -

- {t('comments.analyzeTodoHint')} -

+ } + title={t('comments.emptyTitle')} + /> ) ) : (
@@ -2150,20 +2154,26 @@ export function MagicNotesWorkspace({
) ) : !detail ? ( -

- {t('comments.selectNote')} -

+ } + title={t('comments.emptyTitle')} + /> ) : aiEntries.length === 0 && draftAnalyses.length === 0 && !draftAnalysisRunning && !liveAnalysis ? ( -

- {commentMode === 'immediate' - ? t('comments.immediateHint') - : commentMode === 'after-save-auto' - ? t('comments.autoHint') - : t('comments.manualHint')} -

+ } + title={t('comments.emptyTitle')} + /> ) : (
{draftAnalysisRunning && !liveAnalysis && ( diff --git a/src/renderer/src/i18n/locales/en-US/magicNotes.ts b/src/renderer/src/i18n/locales/en-US/magicNotes.ts index 26f1718..55b246d 100644 --- a/src/renderer/src/i18n/locales/en-US/magicNotes.ts +++ b/src/renderer/src/i18n/locales/en-US/magicNotes.ts @@ -85,8 +85,7 @@ export const magicNotes = { emptySelectionDescription: 'Select a note on the left, or create a note to start writing.', newEntryLabel: 'New entry content', - composerImmediateHint: - 'Press Enter, then stop typing for 5 seconds for AI to comment on the draft', + composerImmediateHint: 'AI comments after you stop typing for 5 seconds', composerRichTextHint: 'Supports rich text and pasting or dropping local images, videos, and attachments', emptyEntries: 'No entries yet. Write the first entry above.', @@ -122,10 +121,11 @@ export const magicNotes = { comments: { paneLabel: 'AI comments', closePane: 'Close AI comments pane', + emptyTitle: 'No AI comments yet', directionLabel: 'Comment direction', directionAriaLabel: 'AI comment direction', directionHelp: - 'Direction changes apply only to the next comment and do not alter existing content. Change the comment format in Settings.', + 'Applies only to the next comment. Change the format in Settings.', directions: { general: 'General feedback', expand: 'Expand writing', @@ -144,7 +144,7 @@ export const magicNotes = { 'Choose “Analyze with AI” in the to-do details to show comments here.', selectNote: 'Select a note to show AI comments.', immediateHint: - 'Finish a sentence, press Enter, and stop typing for 5 seconds to show comments here.', + 'Finish a sentence and stop typing for 5 seconds to show comments here.', autoHint: 'AI comments automatically after you save an entry.', manualHint: 'Choose “Analyze with AI” on an entry to show comments here.' @@ -154,6 +154,7 @@ export const magicNotes = { aiPaneWidth: 'AI comments pane, {{width}} pixels' }, editor: { + toolbarLabel: 'Note formatting toolbar', imageReadFailed: 'Could not read the image', fileReadFailed: 'Could not read the file', maxImages: 'Each entry can contain up to {{count}} images', @@ -173,10 +174,10 @@ export const magicNotes = { heading3: 'Heading 3', body: 'Body', fontSize: 'Font size', - fontSizeSmall: 'Small', - fontSizeNormal: 'Normal', - fontSizeLarge: 'Large', - fontSizeHuge: 'Extra large', + fontSizeSmall: '12', + fontSizeNormal: '14', + fontSizeLarge: '18', + fontSizeHuge: '24', textColor: 'Text color', bold: 'Bold', italic: 'Italic', diff --git a/src/renderer/src/i18n/locales/zh-CN/magicNotes.ts b/src/renderer/src/i18n/locales/zh-CN/magicNotes.ts index 6f3a749..3dce802 100644 --- a/src/renderer/src/i18n/locales/zh-CN/magicNotes.ts +++ b/src/renderer/src/i18n/locales/zh-CN/magicNotes.ts @@ -79,7 +79,7 @@ export const magicNotes = { emptySelectionTitle: '还没有选择笔记', emptySelectionDescription: '从左侧选择笔记,或新建一篇笔记开始记录。', newEntryLabel: '新记录内容', - composerImmediateHint: '按回车并停止输入 5 秒后,AI 评论当前草稿', + composerImmediateHint: '停止输入 5 秒后自动评论', composerRichTextHint: '支持富文本,可粘贴或拖入本地图片、视频和附件', emptyEntries: '还没有记录,在上方写下第一条内容。', editEntryLabel: '编辑记录内容', @@ -113,9 +113,10 @@ export const magicNotes = { comments: { paneLabel: 'AI 评论', closePane: '关闭 AI 评论面板', + emptyTitle: '暂无 AI 评论', directionLabel: '评论方向', directionAriaLabel: 'AI 评论方向', - directionHelp: '评论方向更改仅用于下一次评论,不会改动已生成的内容;评论形式可在设置中心修改。', + directionHelp: '仅影响下一次评论;评论形式可在设置中心修改。', directions: { general: '综合点评', expand: '扩展写作', @@ -132,7 +133,7 @@ export const magicNotes = { selectTodo: '选择待办后显示 AI 评论。', analyzeTodoHint: '点击待办详情中的“AI 分析”,评论会显示在这里。', selectNote: '选择笔记后显示 AI 评论。', - immediateHint: '写完一句后按回车,停止输入 5 秒,评论会显示在这里。', + immediateHint: '写完一句并停止输入 5 秒后,评论会显示在这里。', autoHint: '保存记录后,AI 会自动评论。', manualHint: '在记录上点击“AI 分析”,评论会显示在这里。' }, @@ -141,6 +142,7 @@ export const magicNotes = { aiPaneWidth: 'AI 评论栏 {{width}} 像素' }, editor: { + toolbarLabel: '笔记格式工具栏', imageReadFailed: '图片读取失败', fileReadFailed: '文件读取失败', maxImages: '每条记录最多包含 {{count}} 张图片', @@ -158,10 +160,10 @@ export const magicNotes = { heading3: '标题 3', body: '正文', fontSize: '字体大小', - fontSizeSmall: '小号', - fontSizeNormal: '常规', - fontSizeLarge: '大号', - fontSizeHuge: '特大', + fontSizeSmall: '12', + fontSizeNormal: '14', + fontSizeLarge: '18', + fontSizeHuge: '24', textColor: '字体颜色', bold: '粗体', italic: '斜体', diff --git a/src/renderer/src/styles.css b/src/renderer/src/styles.css index b3a48ad..018c278 100644 --- a/src/renderer/src/styles.css +++ b/src/renderer/src/styles.css @@ -526,7 +526,7 @@ container: speech-model-list / inline-size; grid-template-columns: 220px minmax(300px, 1fr) 9px - var(--magic-notes-ai-width, 300px); + var(--magic-notes-ai-width, 280px); } .magic-notes-layout--ai-hidden { @@ -544,7 +544,11 @@ width: 9px; min-width: 9px; padding: 0; - background: var(--surface-subtle); + background: linear-gradient( + to right, + var(--surface-raised) 0 4px, + var(--surface-subtle) 4px 100% + ); cursor: col-resize; touch-action: none; } @@ -872,7 +876,7 @@ } .magic-notes-stream-pane { - padding: var(--space-6); + padding: var(--space-4) var(--space-6) var(--space-6); background: var(--surface-raised); } @@ -1030,9 +1034,19 @@ .magic-note-detail-header > div { display: flex; + align-items: center; gap: var(--space-1); } +.magic-note-detail-header__delete { + color: var(--text-muted); +} + +.magic-note-detail-header__delete:hover { + background: var(--danger-subtle); + color: var(--danger); +} + .magic-note-delete-confirmation { display: flex; align-items: center; @@ -1090,6 +1104,16 @@ gap: var(--space-3); } +.magic-note-composer > footer > span { + min-width: 0; +} + +.magic-note-composer > footer > .primary-button { + min-width: 80px; + flex-shrink: 0; + white-space: nowrap; +} + .magic-note-entry-stream { display: grid; gap: var(--space-3); @@ -1151,10 +1175,28 @@ } .magic-note-editor__toolbar.ql-toolbar.ql-snow { + display: flex; + align-items: center; + flex-wrap: wrap; padding: var(--space-2); border: 0; border-bottom: 1px solid var(--border-subtle); background: var(--surface-subtle); + gap: var(--space-1); +} + +.magic-note-editor__toolbar.ql-toolbar.ql-snow .ql-formats { + display: inline-flex; + align-items: center; + padding-right: var(--space-1); + border-right: 1px solid var(--border-subtle); + margin: 0; +} + +.magic-note-editor__toolbar.ql-toolbar.ql-snow + .ql-formats:last-child { + padding-right: 0; + border-right: 0; } .magic-note-editor__toolbar.ql-toolbar button, @@ -1170,18 +1212,35 @@ } .magic-note-editor__content.ql-container.ql-snow { - min-height: 132px; + min-height: 178px; + overflow: hidden; border: 0; color: var(--text-primary); font-family: inherit; font-size: 14px; + resize: vertical; } .magic-note-editor__content .ql-editor { - min-height: 132px; + min-height: 178px; line-height: 1.65; } +.magic-note-editor .ql-size-small, +.magic-note-content .ql-size-small { + font-size: 12px; +} + +.magic-note-editor .ql-size-large, +.magic-note-content .ql-size-large { + font-size: 18px; +} + +.magic-note-editor .ql-size-huge, +.magic-note-content .ql-size-huge { + font-size: 24px; +} + .magic-note-editor .ql-editor img, .magic-note-content .ql-editor img { display: block;