diff --git a/packages/app/src/styles.css b/packages/app/src/styles.css index 3a7dbc60..e762c868 100644 --- a/packages/app/src/styles.css +++ b/packages/app/src/styles.css @@ -978,12 +978,16 @@ .markdown-paper .cm-line.cm-markra-h4, .markdown-paper .cm-line.cm-markra-h5, .markdown-paper .cm-line.cm-markra-h6 { + --markra-heading-toolbar-inline-start: -78px; font-family: var(--editor-heading-font-family) !important; letter-spacing: var(--editor-heading-letter-spacing) !important; text-wrap: balance; } + /* Heading controls follow typography even when the final heading has no + foldable content and therefore no fold-toggle decoration. */ .markdown-paper .cm-line.cm-markra-h1 { + --markra-heading-control-center-offset: -5.5px; color: var(--editor-h1-color) !important; padding-block: 0 16px !important; font-size: var(--editor-h1-font-size) !important; @@ -992,6 +996,7 @@ } .markdown-paper .cm-line.cm-markra-h2 { + --markra-heading-control-center-offset: 8px; color: var(--editor-h2-color) !important; padding-block: 28px 12px !important; font-size: var(--editor-h2-font-size) !important; @@ -1000,6 +1005,7 @@ } .markdown-paper .cm-line.cm-markra-h3 { + --markra-heading-control-center-offset: 9px; color: var(--editor-h3-color) !important; padding-block: 22px 4px !important; font-size: var(--editor-h3-font-size) !important; @@ -1008,6 +1014,7 @@ } .markdown-paper .cm-line.cm-markra-h4 { + --markra-heading-control-center-offset: 8px; color: var(--editor-h4-color) !important; padding-block: 18px 2px !important; font-size: var(--editor-h4-font-size) !important; @@ -1017,6 +1024,7 @@ .markdown-paper .cm-line.cm-markra-h5, .markdown-paper .cm-line.cm-markra-h6 { + --markra-heading-control-center-offset: 7px; padding-block-start: 14px !important; } @@ -1528,6 +1536,26 @@ transform: translateY(-50%); } + .markdown-paper .cm-line.cm-markra-h1 > .cm-markra-block-toolbar, + .markdown-paper .cm-line.cm-markra-h2 > .cm-markra-block-toolbar, + .markdown-paper .cm-line.cm-markra-h3 > .cm-markra-block-toolbar, + .markdown-paper .cm-line.cm-markra-h4 > .cm-markra-block-toolbar, + .markdown-paper .cm-line.cm-markra-h5 > .cm-markra-block-toolbar, + .markdown-paper .cm-line.cm-markra-h6 > .cm-markra-block-toolbar { + inset-inline-start: var(--markra-heading-toolbar-inline-start); + top: calc(50% + var(--markra-heading-control-center-offset)); + } + + .markdown-paper .cm-line.cm-markra-h1 > .cm-markra-block-toolbar::after, + .markdown-paper .cm-line.cm-markra-h2 > .cm-markra-block-toolbar::after, + .markdown-paper .cm-line.cm-markra-h3 > .cm-markra-block-toolbar::after, + .markdown-paper .cm-line.cm-markra-h4 > .cm-markra-block-toolbar::after, + .markdown-paper .cm-line.cm-markra-h5 > .cm-markra-block-toolbar::after, + .markdown-paper .cm-line.cm-markra-h6 > .cm-markra-block-toolbar::after { + inset-inline-end: -8px; + width: 8px; + } + /* The controls sit outside the line box. This invisible bridge keeps the line hovered while the pointer crosses the 12px gutter to the buttons. */ .markdown-paper .cm-markra-block-toolbar::after { @@ -2326,7 +2354,7 @@ .markdown-paper .markra-heading-level-control { position: absolute; left: -28px; - top: calc(50% + var(--markra-heading-toggle-center-offset)); + top: calc(50% + var(--markra-heading-control-center-offset)); z-index: 5; display: inline-grid; margin: 0; @@ -2415,54 +2443,9 @@ color: var(--editor-text-heading); } - .markdown-paper .markra-heading-toggle-heading { + .markdown-paper .cm-line.markra-heading-toggle-heading { @apply relative; - --markra-heading-toggle-center-offset: 0px; - } - - /* These controls are all absolutely positioned. Keeping them out of the - line layout prevents a pointer-down from moving heading text before the - matching pointer-up, which browsers can misread as a drag selection. */ - .markdown-paper .markra-heading-toggle-heading > .cm-markra-block-toolbar { - position: absolute !important; - left: -104px; - margin: 0 !important; - top: calc(50% + var(--markra-heading-toggle-center-offset)); - transform: translateY(-50%); - } - - .markdown-paper .markra-heading-toggle-heading > .cm-markra-block-toolbar::after { - inset-inline-end: -8px; - width: 8px; - } - - .markdown-paper h1.markra-heading-toggle-heading, - .markdown-paper .cm-line.cm-markra-h1.markra-heading-toggle-heading { - --markra-heading-toggle-center-offset: -5.5px; - } - - .markdown-paper h2.markra-heading-toggle-heading, - .markdown-paper .cm-line.cm-markra-h2.markra-heading-toggle-heading { - --markra-heading-toggle-center-offset: 8px; - } - - .markdown-paper h3.markra-heading-toggle-heading, - .markdown-paper .cm-line.cm-markra-h3.markra-heading-toggle-heading { - --markra-heading-toggle-center-offset: 9px; - } - - .markdown-paper h4.markra-heading-toggle-heading, - .markdown-paper .cm-line.cm-markra-h4.markra-heading-toggle-heading { - --markra-heading-toggle-center-offset: 8px; - } - - .markdown-paper h5.markra-heading-toggle-heading, - .markdown-paper .cm-line.cm-markra-h5.markra-heading-toggle-heading, - .markdown-paper h6.markra-heading-toggle-heading, - .markdown-paper .cm-line.cm-markra-h6.markra-heading-toggle-heading { - /* Heading rhythm uses top padding. Offset gutter controls by half the - padding imbalance so they remain centered on the editable text row. */ - --markra-heading-toggle-center-offset: 7px; + --markra-heading-toolbar-inline-start: -104px; } .markdown-paper .markra-heading-toggle-button { @@ -2473,7 +2456,7 @@ margin: 0 !important; padding: 0 !important; opacity: 0 !important; - top: calc(50% + var(--markra-heading-toggle-center-offset)); + top: calc(50% + var(--markra-heading-control-center-offset)); transform: translateY(-50%); } diff --git a/packages/app/src/styles.test.ts b/packages/app/src/styles.test.ts index ac002d5d..47a98289 100644 --- a/packages/app/src/styles.test.ts +++ b/packages/app/src/styles.test.ts @@ -571,46 +571,81 @@ describe("editor stylesheet", () => { expect(revealRule).toContain("pointer-events: auto"); }); - it("keeps heading block tools in a separate slot from fold and level controls", () => { + it("keeps terminal heading tools centered and in separate horizontal slots", () => { const styles = readFileSync(`${process.cwd()}/src/styles.css`, "utf8"); - const headingToolbarRuleStart = styles.indexOf( - ".markdown-paper .markra-heading-toggle-heading > .cm-markra-block-toolbar {", + const terminalHeadingToolbarRuleStart = styles.indexOf( + ".markdown-paper .cm-line.cm-markra-h1 > .cm-markra-block-toolbar,", ); - const headingToolbarRuleEnd = styles.indexOf("\n }", headingToolbarRuleStart); - const headingToolbarRule = styles.slice( - headingToolbarRuleStart, - headingToolbarRuleEnd, + const terminalHeadingToolbarRuleEnd = styles.indexOf( + "\n }", + terminalHeadingToolbarRuleStart, + ); + const terminalHeadingToolbarRule = styles.slice( + terminalHeadingToolbarRuleStart, + terminalHeadingToolbarRuleEnd, ); - expect(headingToolbarRuleStart).toBeGreaterThanOrEqual(0); - expect(headingToolbarRule).toContain("position: absolute !important"); - expect(headingToolbarRule).toContain("left: -104px"); - expect(headingToolbarRule).toContain("margin: 0 !important"); - expect(headingToolbarRule).toContain( - "top: calc(50% + var(--markra-heading-toggle-center-offset))", + expect(terminalHeadingToolbarRuleStart).toBeGreaterThanOrEqual(0); + expect(terminalHeadingToolbarRule).toContain( + ".markdown-paper .cm-line.cm-markra-h6 > .cm-markra-block-toolbar", + ); + expect(terminalHeadingToolbarRule).toContain( + "top: calc(50% + var(--markra-heading-control-center-offset));", + ); + expect(terminalHeadingToolbarRule).toContain( + "inset-inline-start: var(--markra-heading-toolbar-inline-start);", + ); + expect(styles).toContain( + "--markra-heading-toolbar-inline-start: -78px;", + ); + expect(styles).toContain( + ".markdown-paper .cm-line.cm-markra-h6 > .cm-markra-block-toolbar::after {", ); - expect(headingToolbarRule).toContain("transform: translateY(-50%)"); expect(styles).toContain( - ".markdown-paper .markra-heading-toggle-heading > .cm-markra-block-toolbar::after", + ".markdown-paper .cm-line.cm-markra-h1 {\n" + + " --markra-heading-control-center-offset: -5.5px;", ); expect(styles).toContain( - ".markdown-paper .cm-line.cm-markra-h2.markra-heading-toggle-heading {\n" + - " --markra-heading-toggle-center-offset: 8px;", + ".markdown-paper .cm-line.cm-markra-h2 {\n" + + " --markra-heading-control-center-offset: 8px;", ); expect(styles).toContain( - ".markdown-paper .cm-line.cm-markra-h3.markra-heading-toggle-heading {\n" + - " --markra-heading-toggle-center-offset: 9px;", + ".markdown-paper .cm-line.cm-markra-h3 {\n" + + " --markra-heading-control-center-offset: 9px;", ); expect(styles).toContain( - ".markdown-paper .cm-line.cm-markra-h4.markra-heading-toggle-heading {\n" + - " --markra-heading-toggle-center-offset: 8px;", + ".markdown-paper .cm-line.cm-markra-h4 {\n" + + " --markra-heading-control-center-offset: 8px;", ); expect(styles).toContain( - ".markdown-paper .cm-line.cm-markra-h5.markra-heading-toggle-heading,\n" + - " .markdown-paper h6.markra-heading-toggle-heading,\n" + - " .markdown-paper .cm-line.cm-markra-h6.markra-heading-toggle-heading {", + "--markra-heading-control-center-offset: 7px;\n" + + " padding-block-start: 14px !important;", + ); + expect(styles).not.toContain( + ".cm-line.cm-markra-h3.markra-heading-toggle-heading {\n" + + " --markra-heading-control-center-offset:", + ); + }); + + it("reserves a third heading-control slot when folding is available", () => { + const styles = readFileSync(`${process.cwd()}/src/styles.css`, "utf8"); + const foldableHeadingRuleStart = styles.indexOf( + ".markdown-paper .cm-line.markra-heading-toggle-heading {", + ); + const foldableHeadingRuleEnd = styles.indexOf( + "\n }", + foldableHeadingRuleStart, + ); + const foldableHeadingRule = styles.slice( + foldableHeadingRuleStart, + foldableHeadingRuleEnd, + ); + + expect(foldableHeadingRuleStart).toBeGreaterThanOrEqual(0); + expect(foldableHeadingRule).toContain( + "--markra-heading-toolbar-inline-start: -104px", ); - expect(styles).toContain("--markra-heading-toggle-center-offset: 7px;"); + expect(foldableHeadingRule).not.toContain("top:"); }); it("forces a grabbing cursor during document tab pointer drags", () => { @@ -811,7 +846,7 @@ describe("editor stylesheet", () => { expect(labelStyles).toContain("left: -28px"); expect(labelStyles).toContain("margin: 0"); expect(labelStyles).toContain( - "top: calc(50% + var(--markra-heading-toggle-center-offset))", + "top: calc(50% + var(--markra-heading-control-center-offset))", ); expect(labelStyles).toContain("transform: translateY(-50%)"); expect(labelStyles).toContain(".markdown-paper .markra-heading-level-list");