diff --git a/frontend/src/MemoIntegration.test.ts b/frontend/src/MemoIntegration.test.ts index dc5193d..ca44ce8 100644 --- a/frontend/src/MemoIntegration.test.ts +++ b/frontend/src/MemoIntegration.test.ts @@ -35,6 +35,17 @@ describe('memo shell integration', () => { expect(app).toContain('
任务备注') }) + it('adds a compact, reusable Markdown editor and preview to memo details', () => { + expect(editor).toContain("import { applyMarkdownFormat, renderMarkdown") + expect(editor).toContain('aria-label="备忘录 Markdown 格式"') + expect(editor).toContain('aria-label="备忘录 Markdown 预览"') + expect(editor).toContain('@keydown="handleMemoBodyShortcut"') + expect(editor).toContain('v-if="!memoPreview && !memo.deleted_at"') + expect(css).toContain('.memo-markdown-field{display:grid;gap:7px') + expect(css).toContain('.memo-markdown-editor .markdown-toolbar{') + expect(css).toContain('.memo-markdown-preview{min-height:250px') + }) + it('tracks editor state in the shell, reserves desktop space, hides the FAB, and marks mobile background regions inert', () => { expect(app).toContain('const memoDetailOpen = ref(false)') expect(app).toContain("'memo-detail-open': activeView==='memos' && memoDetailOpen") diff --git a/frontend/src/components/MemoEditor.test.ts b/frontend/src/components/MemoEditor.test.ts index ed92587..8b2e069 100644 --- a/frontend/src/components/MemoEditor.test.ts +++ b/frontend/src/components/MemoEditor.test.ts @@ -24,6 +24,49 @@ async function mount(overrides: Record = {}) { afterEach(() => { vi.restoreAllMocks(); cleanups.splice(0).forEach((cleanup) => cleanup()) }) describe('MemoEditor', () => { + it('renders and formats memo Markdown locally before the explicit save', async () => { + const request = vi.fn(async (): Promise => memo) + const markdownMemo = { ...memo, content: '# 标题\n\n- 父项\n - 子项\n\n```js\nconst x = 1\n```' } + const { host } = await mount({ memo: markdownMemo, request }) + const body = host.querySelector('[aria-label="备忘录正文"]')! + body.setSelectionRange(2, 4) + host.querySelector('[aria-label="粗体"]')!.click() + await nextTick() + expect(body.value).toContain('# **标题**') + expect(request).not.toHaveBeenCalled() + + host.querySelectorAll('.markdown-mode-switch button')[1].click() + await nextTick() + const preview = host.querySelector('[aria-label="备忘录 Markdown 预览"]')! + expect(preview.innerHTML).toContain('

') + expect(preview.innerHTML).toContain('
    ') + expect(preview.innerHTML).toContain('
    ')
    +    expect(request).not.toHaveBeenCalled()
    +
    +    host.querySelector('.memo-save')!.click()
    +    await flush()
    +    expect(request).toHaveBeenCalledWith('/memos/m1', expect.objectContaining({
    +      method: 'PATCH',
    +      body: JSON.stringify({ title: '旧标题', content: body.value, version: 3 }),
    +    }))
    +  })
    +
    +  it('supports Markdown shortcuts and keeps deleted memos preview-only', async () => {
    +    const active = await mount({ memo: { ...memo, content: '正文' } })
    +    const body = active.host.querySelector('[aria-label="备忘录正文"]')!
    +    body.setSelectionRange(0, 2)
    +    const shortcut = new KeyboardEvent('keydown', { key: 'i', ctrlKey: true, bubbles: true, cancelable: true })
    +    body.dispatchEvent(shortcut)
    +    await nextTick()
    +    expect(shortcut.defaultPrevented).toBe(true)
    +    expect(body.value).toBe('*正文*')
    +
    +    const deleted = await mount({ memo: { ...memo, content: '**只读正文**', deleted_at: '2026-09-15T00:00:00Z' } })
    +    expect(deleted.host.querySelector('[aria-label="备忘录正文"]')).toBeNull()
    +    expect(deleted.host.querySelector('[aria-label="备忘录 Markdown 格式"]')).toBeNull()
    +    expect(deleted.host.querySelector('[aria-label="备忘录 Markdown 预览"]')?.innerHTML).toContain('只读正文')
    +  })
    +
       it('creates a local draft only on first save and locks click plus shortcut re-entry', async () => {
         const pending = deferred()
         const request = vi.fn(() => pending.promise)
    diff --git a/frontend/src/components/MemoEditor.vue b/frontend/src/components/MemoEditor.vue
    index 4a67464..6354055 100644
    --- a/frontend/src/components/MemoEditor.vue
    +++ b/frontend/src/components/MemoEditor.vue
    @@ -1,6 +1,7 @@