From 5f6b2f1a6337dc8883a06cc77fa85b42dff4c781 Mon Sep 17 00:00:00 2001
From: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com>
Date: Sun, 17 Dec 2023 12:55:54 -0800
Subject: [PATCH] Mobile,Desktop: Fixes #9522: Fix code block borders in
headers of Beta Markdown editor (#9523)
---
.../editor/CodeMirror/createEditor.test.ts | 5 +-
.../CodeMirror/markdown/decoratorExtension.ts | 114 ++++++++++--------
packages/editor/CodeMirror/theme.ts | 70 ++++++-----
3 files changed, 96 insertions(+), 93 deletions(-)
diff --git a/packages/editor/CodeMirror/createEditor.test.ts b/packages/editor/CodeMirror/createEditor.test.ts
index bc5350b16d..40db1bea94 100644
--- a/packages/editor/CodeMirror/createEditor.test.ts
+++ b/packages/editor/CodeMirror/createEditor.test.ts
@@ -42,15 +42,12 @@ describe('createEditor', () => {
const headerLine = document.body.querySelector('.cm-headerLine')!;
expect(headerLine.textContent).toBe(headerLineText);
+ expect(getComputedStyle(headerLine).fontSize).toBe('1.6em');
// CodeMirror nests the tag that styles the header within .cm-headerLine:
//
const headerLineContent = document.body.querySelectorAll('.cm-headerLine > span');
expect(headerLineContent.length).toBeGreaterThanOrEqual(1);
- for (const part of headerLineContent) {
- const style = getComputedStyle(part);
- expect(style.fontSize).toBe('1.6em');
- }
// Cleanup
editor.remove();
diff --git a/packages/editor/CodeMirror/markdown/decoratorExtension.ts b/packages/editor/CodeMirror/markdown/decoratorExtension.ts
index 558de6dabe..db54e1a069 100644
--- a/packages/editor/CodeMirror/markdown/decoratorExtension.ts
+++ b/packages/editor/CodeMirror/markdown/decoratorExtension.ts
@@ -48,8 +48,28 @@ const blockQuoteDecoration = Decoration.line({
attributes: { class: 'cm-blockQuote' },
});
-const headerLineDecoration = Decoration.line({
- attributes: { class: 'cm-headerLine' },
+const header1LineDecoration = Decoration.line({
+ attributes: { class: 'cm-h1 cm-headerLine' },
+});
+
+const header2LineDecoration = Decoration.line({
+ attributes: { class: 'cm-h2 cm-headerLine' },
+});
+
+const header3LineDecoration = Decoration.line({
+ attributes: { class: 'cm-h3 cm-headerLine' },
+});
+
+const header4LineDecoration = Decoration.line({
+ attributes: { class: 'cm-h4 cm-headerLine' },
+});
+
+const header5LineDecoration = Decoration.line({
+ attributes: { class: 'cm-h5 cm-headerLine' },
+});
+
+const header6LineDecoration = Decoration.line({
+ attributes: { class: 'cm-h6 cm-headerLine' },
});
const tableHeaderDecoration = Decoration.line({
@@ -72,6 +92,37 @@ const taskMarkerDecoration = Decoration.mark({
attributes: { class: 'cm-taskMarker' },
});
+const nodeNameToLineDecoration: Record = {
+ 'FencedCode': codeBlockDecoration,
+ 'CodeBlock': codeBlockDecoration,
+ 'BlockMath': mathBlockDecoration,
+ 'Blockquote': blockQuoteDecoration,
+
+ 'SetextHeading1': header1LineDecoration,
+ 'ATXHeading1': header1LineDecoration,
+ 'SetextHeading2': header2LineDecoration,
+ 'ATXHeading2': header2LineDecoration,
+ 'ATXHeading3': header3LineDecoration,
+ 'ATXHeading4': header4LineDecoration,
+ 'ATXHeading5': header5LineDecoration,
+ 'ATXHeading6': header6LineDecoration,
+
+ 'TableHeader': tableHeaderDecoration,
+ 'TableDelimiter': tableDelimiterDecoration,
+ 'TableRow': tableBodyDecoration,
+};
+
+const nodeNameToMarkDecoration: Record = {
+ 'InlineCode': inlineCodeDecoration,
+ 'URL': urlDecoration,
+ 'InlineMath': inlineMathDecoration,
+ 'HTMLTag': htmlTagNameDecoration,
+ 'TagName': htmlTagNameDecoration,
+ 'HorizontalRule': horizontalRuleDecoration,
+ 'TaskMarker': taskMarkerDecoration,
+};
+
+
type DecorationDescription = { pos: number; length?: number; decoration: Decoration };
// Returns a set of [Decoration]s, associated with block syntax groups that require
@@ -116,58 +167,15 @@ const computeDecorations = (view: EditorView) => {
const viewFrom = Math.max(from, node.from);
const viewTo = Math.min(to, node.to);
- switch (node.name) {
- case 'FencedCode':
- case 'CodeBlock':
- addDecorationToLines(viewFrom, viewTo, codeBlockDecoration);
+ if (nodeNameToLineDecoration.hasOwnProperty(node.name)) {
+ const decoration = nodeNameToLineDecoration[node.name];
+ addDecorationToLines(viewFrom, viewTo, decoration);
blockDecorated = true;
- break;
- case 'BlockMath':
- addDecorationToLines(viewFrom, viewTo, mathBlockDecoration);
- blockDecorated = true;
- break;
- case 'Blockquote':
- addDecorationToLines(viewFrom, viewTo, blockQuoteDecoration);
- blockDecorated = true;
- break;
- case 'InlineMath':
- addDecorationToRange(viewFrom, viewTo, inlineMathDecoration);
- break;
- case 'InlineCode':
- addDecorationToRange(viewFrom, viewTo, inlineCodeDecoration);
- break;
- case 'URL':
- addDecorationToRange(viewFrom, viewTo, urlDecoration);
- break;
- case 'SetextHeading1':
- case 'SetextHeading2':
- case 'ATXHeading1':
- case 'ATXHeading2':
- case 'ATXHeading3':
- case 'ATXHeading4':
- case 'ATXHeading5':
- case 'ATXHeading6':
- addDecorationToLines(viewFrom, viewTo, headerLineDecoration);
- break;
- case 'HTMLTag':
- case 'TagName':
- addDecorationToRange(viewFrom, viewTo, htmlTagNameDecoration);
- break;
- case 'TableHeader':
- addDecorationToLines(viewFrom, viewTo, tableHeaderDecoration);
- break;
- case 'TableDelimiter':
- addDecorationToLines(viewFrom, viewTo, tableDelimiterDecoration);
- break;
- case 'TableRow':
- addDecorationToLines(viewFrom, viewTo, tableBodyDecoration);
- break;
- case 'HorizontalRule':
- addDecorationToRange(viewFrom, viewTo, horizontalRuleDecoration);
- break;
- case 'TaskMarker':
- addDecorationToRange(viewFrom, viewTo, taskMarkerDecoration);
- break;
+ }
+
+ if (nodeNameToMarkDecoration.hasOwnProperty(node.name)) {
+ const decoration = nodeNameToMarkDecoration[node.name];
+ addDecorationToRange(viewFrom, viewTo, decoration);
}
// Only block decorations will have differing first and last lines
diff --git a/packages/editor/CodeMirror/theme.ts b/packages/editor/CodeMirror/theme.ts
index ee9007572b..7a2c73b8bb 100644
--- a/packages/editor/CodeMirror/theme.ts
+++ b/packages/editor/CodeMirror/theme.ts
@@ -78,6 +78,11 @@ const createTheme = (theme: any): Extension[] => {
// be at least this specific.
const selectionBackgroundSelector = '&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground';
+ const baseHeadingStyle = {
+ fontWeight: 'bold',
+ fontFamily: theme.fontFamily,
+ };
+
const codeMirrorTheme = EditorView.theme({
'&': baseGlobalStyle,
@@ -145,7 +150,7 @@ const createTheme = (theme: any): Extension[] => {
// span with a larger font-size, the .cm-inlineCode's bounding box won't
// be big enough for its content.
// As such, we need to style whichever element directly wraps its content.
- '& .cm-headerLine > .cm-inlineCode > *, & :not(.cm-headerLine) > .cm-inlineCode': {
+ '& .cm-inlineCode': {
borderWidth: '1px',
borderStyle: 'solid',
borderColor: isDarkTheme ? 'rgba(200, 200, 200, 0.5)' : 'rgba(100, 100, 100, 0.5)',
@@ -166,6 +171,34 @@ const createTheme = (theme: any): Extension[] => {
opacity: theme.isDesktop ? 0.6 : 1,
},
+ // Applying font size changes with CSS rather than the theme below works
+ // around an issue where the border for code blocks in headings was too
+ // small.
+ '& .cm-h1': {
+ ...baseHeadingStyle,
+ fontSize: '1.6em',
+ },
+ '& .cm-h2': {
+ ...baseHeadingStyle,
+ fontSize: '1.4em',
+ },
+ '& .cm-h3': {
+ ...baseHeadingStyle,
+ fontSize: '1.3em',
+ },
+ '& .cm-h4': {
+ ...baseHeadingStyle,
+ fontSize: '1.2em',
+ },
+ '& .cm-h5': {
+ ...baseHeadingStyle,
+ fontSize: '1.1em',
+ },
+ '& .cm-h6': {
+ ...baseHeadingStyle,
+ fontSize: '1.0em',
+ },
+
// Style the search widget. Use ':root' to increase the selector's precedence
// (override the existing preset styles).
':root & .cm-panel.cm-search': {
@@ -176,11 +209,6 @@ const createTheme = (theme: any): Extension[] => {
},
}, { dark: isDarkTheme });
- const baseHeadingStyle = {
- fontWeight: 'bold',
- fontFamily: theme.fontFamily,
- };
-
const highlightingStyle = HighlightStyle.define([
{
tag: tags.strong,
@@ -190,36 +218,6 @@ const createTheme = (theme: any): Extension[] => {
tag: tags.emphasis,
fontStyle: 'italic',
},
- {
- ...baseHeadingStyle,
- tag: tags.heading1,
- fontSize: '1.6em',
- },
- {
- ...baseHeadingStyle,
- tag: tags.heading2,
- fontSize: '1.4em',
- },
- {
- ...baseHeadingStyle,
- tag: tags.heading3,
- fontSize: '1.3em',
- },
- {
- ...baseHeadingStyle,
- tag: tags.heading4,
- fontSize: '1.2em',
- },
- {
- ...baseHeadingStyle,
- tag: tags.heading5,
- fontSize: '1.1em',
- },
- {
- ...baseHeadingStyle,
- tag: tags.heading6,
- fontSize: '1.0em',
- },
{
tag: tags.list,
fontFamily: theme.fontFamily,