A local-first note taking app
0

Configure Feed

Select the types of activity you want to include in your feed.

Make code-block background seamless across lines

The cm-codeblock line decoration carried vertical margin, per-line
padding, and border-radius, so each code line rendered as its own rounded
box with the editor background showing through the gaps between lines.
Drop the vertical margin and give the block horizontal padding only, then
tag the first/last block lines (cm-codeblock-first / -last) to add the
vertical padding and rounded corners — the run now reads as one
continuous box.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

authored by

Ethan Graf
Claude Opus 4.8
and committed by
Tangled
(Jul 19, 2026, 5:59 PM +0300) 78eec6bf 8a4fb30d

+46 -4
+18 -3
src/editors/automerge/automergeEditor.css
··· 88 88 font-size: 0.9em; 89 89 } 90 90 91 + /* 92 + * Fenced code block. Applied as one line decoration per line, so the 93 + * background must be seamless: no vertical margin (which would show the 94 + * editor background through the gaps) and only horizontal padding here. 95 + * The first/last lines add the vertical padding and rounded corners so the 96 + * whole run reads as a single box. 97 + */ 91 98 .automerge-editor-container .cm-codeblock { 92 99 background: rgba(175, 184, 193, 0.1); 93 - border-radius: 6px; 94 - padding: 0.8em 1em; 95 - margin: 0.5em 0; 100 + padding: 0 1em; 96 101 font-family: 97 102 ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace; 98 103 font-size: 0.9em; 99 104 line-height: 1.5; 105 + } 106 + .automerge-editor-container .cm-codeblock-first { 107 + padding-top: 0.6em; 108 + border-top-left-radius: 6px; 109 + border-top-right-radius: 6px; 110 + } 111 + .automerge-editor-container .cm-codeblock-last { 112 + padding-bottom: 0.6em; 113 + border-bottom-left-radius: 6px; 114 + border-bottom-right-radius: 6px; 100 115 } 101 116 102 117 /* Lists */
+24 -1
src/editors/automerge/livePreview/codeDecoration.ts
··· 27 27 import { inlineFormattingHiddenMark } from './hiddenClasses'; 28 28 29 29 const inlineCodeMark = Decoration.mark({ class: 'cm-inline-code' }); 30 + // Per-line background. The first/last lines additionally get rounded corners 31 + // and vertical padding so the block reads as one continuous box (no vertical 32 + // margin between lines, which would show the editor background through gaps). 30 33 const codeBlockLine = Decoration.line({ class: 'cm-codeblock' }); 34 + const codeBlockLineFirst = Decoration.line({ 35 + class: 'cm-codeblock cm-codeblock-first', 36 + }); 37 + const codeBlockLineLast = Decoration.line({ 38 + class: 'cm-codeblock cm-codeblock-last', 39 + }); 40 + const codeBlockLineOnly = Decoration.line({ 41 + class: 'cm-codeblock cm-codeblock-first cm-codeblock-last', 42 + }); 43 + 44 + function codeBlockLineDeco(line: number, first: number, last: number) { 45 + if (line === first && line === last) return codeBlockLineOnly; 46 + if (line === first) return codeBlockLineFirst; 47 + if (line === last) return codeBlockLineLast; 48 + return codeBlockLine; 49 + } 31 50 32 51 export function buildCodeDecorations( 33 52 state: EditorState, ··· 74 93 } 75 94 for (let l = startLine; l <= endLine; l++) { 76 95 const line = state.doc.line(l); 77 - out.push(line.from, line.from, codeBlockLine); 96 + out.push( 97 + line.from, 98 + line.from, 99 + codeBlockLineDeco(l, startLine, endLine), 100 + ); 78 101 } 79 102 80 103 if (!isCursorOnBlockLine(state, node.from, node.to)) {
+4
src/editors/automerge/livePreview/livePreview.test.ts
··· 170 170 const state = makeState('```js\ncode\n```\n\nafter', 18); // in "after" 171 171 const list = decos(state, buildCodeDecorations(state)); 172 172 expect(withClass(list, 'cm-codeblock').length).toBeGreaterThanOrEqual(3); 173 + // First and last block lines are tagged so the box is seamless (one 174 + // continuous background, rounded only at the ends). 175 + expect(withClass(list, 'cm-codeblock-first')).toHaveLength(1); 176 + expect(withClass(list, 'cm-codeblock-last')).toHaveLength(1); 173 177 // Opening and closing fence rows hidden. 174 178 expect( 175 179 withClass(list, 'cm-inline-formatting-hidden').length,