feat(editor): add scroll navigation and update theme Markdown behavior

This commit is contained in:
2026-09-06 17:15:41 +08:00
parent f32971d32e
commit f0fe8f2629
15 changed files with 211 additions and 8 deletions
@@ -6,6 +6,7 @@ vi.mock('@/styles/features.css?raw', async () => ({ default: (await import('node
vi.mock('@/styles/tokens.css?raw', async () => ({ default: (await import('node:fs')).readFileSync(process.cwd() + '/src/styles/tokens.css', 'utf8') }))
vi.mock('@/styles/callouts.css?raw', async () => ({ default: (await import('node:fs')).readFileSync(process.cwd() + '/src/styles/callouts.css', 'utf8') }))
import CommunityThemePreview from './CommunityThemePreview.vue'
vi.mock('@/styles/markdown-behavior.css?raw', async () => ({ default: (await import('node:fs')).readFileSync(process.cwd() + '/src/styles/markdown-behavior.css', 'utf8') }))
import { mockCommunityThemes } from '@/services/themePackageService'
const themes = [...['light','dark','sepia'].map(theme_id => ({theme_id,name:theme_id,builtin:true})), ...mockCommunityThemes.map(t => ({...t,builtin:false}))]
it.each(themes)('previews shared component states safely for $theme_id', theme => {
@@ -16,6 +17,9 @@ it.each(themes)('previews shared component states safely for $theme_id', theme =
const doc = new DOMParser().parseFromString(iframe.attributes('srcdoc')!, 'text/html')
expect(doc.documentElement.dataset.theme).toBe(theme.theme_id)
expect(doc.querySelector('script')).toBeNull()
for (const selector of ['.editor-scroll-buttons button', '.markdown-content h6', '.task-list-item input:checked', '.markdown-content[data-code-wrap="true"][data-line-numbers="true"] .line', '.heading-fold-toggle']) expect(doc.querySelector(selector), selector).not.toBeNull()
expect(doc.querySelector('style')!.textContent).toContain('.editor-scroll-buttons button:focus-visible')
expect(doc.querySelector('style')!.textContent).toContain(".markdown-content[data-code-wrap='true'] .shiki code")
expect(doc.querySelectorAll('.specimen-callouts > aside.markdown-callout')).toHaveLength(14)
expect(doc.querySelector('.specimen-callouts > details[open]')).not.toBeNull()
expect(doc.querySelector('.specimen-callouts > details:not([open])')).not.toBeNull()
@@ -6,6 +6,7 @@ import { getCommunityThemePreviewCss, mockCommunityThemes } from '@/services/the
import tokensCss from '@/styles/tokens.css?raw'
import featuresCss from '@/styles/features.css?raw'
import headingsCss from '@/styles/headings.css?raw'
import markdownBehaviorCss from '@/styles/markdown-behavior.css?raw'
import calloutsCss from '@/styles/callouts.css?raw'
import { calloutTypes } from '@/utils/callouts'
import specimenHtml from './themeSpecimen.html?raw'
@@ -25,6 +26,7 @@ const previewDocument = computed(() => {
const style = doc.createElement('style')
style.textContent = `${tokensCss}\n${featuresCss}\n${calloutsCss}\n${headingsCss}\n${props.css ?? getCommunityThemePreviewCss(props.themeId)}\nhtml { height:100% !important; overflow-y:auto !important; overflow-x:hidden !important; overscroll-behavior:contain; } body { height:auto !important; min-height:100%; overflow:visible !important; margin:0; padding:24px; background:var(--color-background-primary); color:var(--color-text-primary); font:16px/1.6 system-ui; } article { min-width:0; } .theme-specimen { display:grid; gap:16px; margin-top:20px; } .surface-nested { padding:12px; border:1px solid var(--color-border-default); border-radius:var(--radius-md); } .specimen-markdown { overflow:auto; } .specimen-markdown code { background:var(--color-code-background, var(--color-background-secondary)); color:var(--color-code-text, var(--color-text-primary)); padding:3px 6px; border-radius:4px; } .specimen-markdown pre { padding:12px; background:var(--color-background-secondary); } .specimen-markdown blockquote { border-left:3px solid var(--color-accent-primary); padding-left:12px; } .specimen-markdown table { width:100%; border-collapse:collapse; } .specimen-markdown td,.specimen-markdown th { padding:8px; border:1px solid var(--color-border-default); } .specimen-markdown th { background:var(--color-markdown-table-header); } .specimen-chart > div { display:flex; align-items:flex-end; gap:12px; height:100px; border-bottom:1px solid var(--color-border-default); } .specimen-chart span { width:36px; } .specimen-long { overflow-wrap:anywhere; } @media(max-width:480px) { body { padding:12px; } .form-grid { grid-template-columns:minmax(0,1fr); } }`
style.textContent += `\n${markdownBehaviorCss}\n.specimen-scroll { position:relative; min-height:110px; }`
doc.head.append(style)
const article = doc.createElement('article')
article.className = 'panel'
@@ -105,6 +105,6 @@ it('offers and applies the paper theme update without discarding the active them
const card = wrapper.findAll('article.theme-card').find(item => item.text().includes('Paper Moments'))!
await card.findAll('button').find(button => button.text() === '更新')!.trigger('click')
await flushPromises()
expect(store.allThemes.find(theme => theme.theme_id === 'paper-moments')?.version).toBe('1.8.1')
expect(store.allThemes.find(theme => theme.theme_id === 'paper-moments')?.version).toBe('1.9.0')
expect(document.getElementById('theme-style-paper-moments')!.textContent).toContain('.surface-nested')
})
@@ -20,3 +20,20 @@ console.log(note);</code></pre><table><thead><tr><th>名称</th><th>状态</th><
<section class="surface-nested markdown-preferences"><h2>Markdown 语法预设</h2><label>标题样式 <select class="select"><option>ATX (#)</option><option>Setext</option></select></label><label><input type="checkbox" checked> 警告框与提示框</label><button class="button-secondary">保存为预设</button></section>
<section class="milkdown"><div class="ProseMirror"><h2><button class="heading-fold-toggle" aria-expanded="true" aria-label="折叠示例标题"></button>悬停查看折叠箭头</h2><p>折叠按钮跟随主题,键盘聚焦时也可见。</p></div></section>
<section class="markdown-content specimen-markdown">
<h2>Markdown 行为与状态</h2>
<h4>四级标题</h4><h5>五级标题</h5><h6>六级标题</h6>
<p><a href="#markdown-preview-target">链接:检查悬停与键盘焦点</a><code>行内代码</code><strong>强调</strong><em>斜体</em><del>删除线</del>。选中这段文字检查主题选区颜色。</p>
<ul><li>无序列表<ul><li>嵌套列表</li></ul></li></ul><ol><li>有序列表</li><li>第二项</li></ol>
<ul><li class="task-list-item"><input type="checkbox" disabled> 未完成任务</li><li class="task-list-item"><input type="checkbox" checked disabled> 已完成任务</li></ul>
<blockquote><p>普通引用</p><blockquote>嵌套引用</blockquote></blockquote><hr>
<details class="markdown-callout" data-callout="tip"><summary class="callout-title">点击展开提示</summary><div class="callout-body"><p>提示内的 <code>代码</code> 与列表也继承主题。</p></div></details>
<p id="markdown-preview-target">ATX 和 Setext 解析后使用相同标题样式;关闭扩展语法时保留源码展示。</p>
<pre><code>&gt; [!NOTE]
关闭警告框扩展时显示原始内容
```mermaid
graph LR; A--&gt;B
```</code></pre>
</section>
<section class="surface-nested specimen-scroll"><p>工作区滚动按钮:悬停、按下或 Tab 聚焦检查配色。</p><div class="editor-scroll-buttons"><button class="button-secondary" aria-label="滑动到顶部" title="滑动到顶部"></button><button class="button-secondary" aria-label="滑动到底部" title="滑动到底部"></button></div></section>
<section class="markdown-content specimen-markdown" data-code-wrap="true" data-line-numbers="true"><h3>代码换行与行号</h3><pre class="shiki"><code><span class="line">const description = "这是一段用于检查代码自动换行、行号与主题配色的较长说明,较窄视口下也应该保留可读内容。";</span><span class="line">console.log(description);</span></code></pre></section>