feat(editor): add themed callouts and desktop command boundary

This commit is contained in:
2026-09-06 11:33:19 +08:00
parent 15e10dfd68
commit d21ce23932
22 changed files with 662 additions and 11 deletions
+44
View File
@@ -22,3 +22,47 @@ it.each(mockCommunityThemes)('provides interaction and Markdown colors in $theme
}
expect(css).toContain(`color-scheme: ${theme.is_dark ? 'dark' : 'light'}`)
})
const calloutThemes = ['light', 'dark', 'sepia', ...mockCommunityThemes.map(theme => theme.theme_id)]
const calloutTones = ['info', 'success', 'warning', 'danger', 'important', 'quote']
it.each(calloutThemes)('keeps callout headings readable against tinted surfaces in %s', themeId => {
const doc = document.implementation.createHTMLDocument('theme contrast')
const style = doc.createElement('style')
style.textContent = files['styles/tokens.css'] ?? files['styles\\tokens.css']!
style.textContent += getCommunityThemePreviewCss(themeId)
doc.head.append(style)
const values = new Map<string, string>()
for (const rule of Array.from(doc.styleSheets[0]!.cssRules) as CSSStyleRule[]) {
if (![':root', `[data-theme='${themeId}']`, `[data-theme="${themeId}"]`].includes(rule.selectorText)) continue
for (const name of ['--color-surface-primary', ...calloutTones.map(tone => `--color-callout-${tone}`)]) {
const value = rule.style.getPropertyValue(name).trim()
if (value) values.set(name, value)
}
}
const rgb = (hex: string) => [1, 3, 5].map(offset => parseInt(hex.slice(offset, offset + 2), 16) / 255)
const luminance = (color: number[]) => color.map(value => value <= .04045 ? value / 12.92 : ((value + .055) / 1.055) ** 2.4).reduce((sum, value, index) => sum + value * [.2126, .7152, .0722][index]!, 0)
const surface = rgb(values.get('--color-surface-primary')!)
for (const tone of calloutTones) {
const hex = values.get(`--color-callout-${tone}`)!
expect(hex).toMatch(/^#[\da-f]{6}$/i)
const ink = rgb(hex)
const background = surface.map((value, index) => value * .92 + ink[index]! * .08)
const first = luminance(ink), second = luminance(background)
expect((Math.max(first, second) + .05) / (Math.min(first, second) + .05), tone).toBeGreaterThanOrEqual(4.5)
}
})
it('preserves warning and success colors inside the editor selector cascade', () => {
const style = document.createElement('style')
style.textContent = readFileSync(join(root, 'styles/callouts.css'), 'utf8')
document.head.append(style)
const host = document.createElement('div')
host.className = 'milkdown'
host.innerHTML = '<div class="ProseMirror"><blockquote class="markdown-callout" data-callout="warning"></blockquote><blockquote class="markdown-callout" data-callout="success"></blockquote></div>'
document.body.append(host)
try {
const blocks = host.querySelectorAll('blockquote')
expect(getComputedStyle(blocks[0]!).getPropertyValue('--callout-color')).toContain('--color-callout-warning')
expect(getComputedStyle(blocks[1]!).getPropertyValue('--callout-color')).toContain('--color-callout-success')
} finally { host.remove(); style.remove() }
})