Skip to content

Commit e69f2a4

Browse files
authored
fix: custom code editors first line issue (baserow#5311)
1 parent 3b56679 commit e69f2a4

3 files changed

Lines changed: 73 additions & 20 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "bug",
3+
"message": "Fix custom code editors when deleting the first line",
4+
"issue_origin": "github",
5+
"issue_number": 5310,
6+
"domain": "builder",
7+
"bullet_points": [],
8+
"created_at": "2026-05-05"
9+
}

web-frontend/modules/core/assets/scss/components/code_editor.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,8 @@
1616
padding: 0;
1717
overflow-y: auto;
1818
}
19+
20+
&__code-wrapper {
21+
margin: 2px 0;
22+
}
1923
}

web-frontend/modules/core/components/CodeEditor.vue

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
import { Editor, EditorContent } from '@tiptap/vue-3'
99
1010
import { Document } from '@tiptap/extension-document'
11-
import { Paragraph } from '@tiptap/extension-paragraph'
1211
import { Text } from '@tiptap/extension-text'
1312
13+
const CodeEditorDocument = Document.extend({
14+
content: 'codeBlock',
15+
})
16+
1417
export default {
1518
name: 'CodeEditor',
1619
components: {
@@ -36,13 +39,23 @@ export default {
3639
watch: {
3740
modelValue(newCode) {
3841
if (this.editor && newCode !== this.getCurrentCode()) {
39-
this.editor.commands.setContent(this.generateCodeBlock(newCode))
42+
this.editor.commands.setContent(
43+
this.generateCodeBlock(newCode),
44+
false,
45+
{
46+
preserveWhitespace: 'full',
47+
}
48+
)
4049
}
4150
},
4251
language() {
4352
if (this.editor) {
4453
this.editor.commands.setContent(
45-
this.generateCodeBlock(this.getCurrentCode())
54+
this.generateCodeBlock(this.getCurrentCode()),
55+
false,
56+
{
57+
preserveWhitespace: 'full',
58+
}
4659
)
4760
}
4861
},
@@ -60,17 +73,44 @@ export default {
6073
lowlight.register('javascript', javascript)
6174
lowlight.register('css', css)
6275
76+
const LockedCodeBlockLowlight = CodeBlockLowlight.extend({
77+
addKeyboardShortcuts() {
78+
const parentShortcuts = this.parent?.() || {}
79+
80+
return {
81+
...parentShortcuts,
82+
Backspace: () => {
83+
const { empty, $anchor } = this.editor.state.selection
84+
85+
if (!empty || $anchor.parent.type.name !== this.name) {
86+
return false
87+
}
88+
89+
if ($anchor.pos === 1 || !$anchor.parent.textContent.length) {
90+
return true
91+
}
92+
93+
return false
94+
},
95+
}
96+
},
97+
})
98+
6399
this.editor = new Editor({
64100
extensions: [
65-
Document,
66-
Paragraph,
101+
CodeEditorDocument,
67102
Text,
68-
CodeBlockLowlight.configure({
103+
LockedCodeBlockLowlight.configure({
69104
lowlight,
105+
exitOnTripleEnter: false,
106+
exitOnArrowDown: false,
107+
HTMLAttributes: {
108+
class: 'code-editor__code-wrapper',
109+
},
70110
}),
71111
],
72112
content: this.generateCodeBlock(this.modelValue),
73-
onUpdate: ({ editor }) => {
113+
onUpdate: () => {
74114
this.$emit('update:modelValue', this.getCurrentCode())
75115
},
76116
})
@@ -80,21 +120,21 @@ export default {
80120
},
81121
methods: {
82122
generateCodeBlock(code) {
83-
return `<pre class="code-editor__code-wrapper"><code class="code-editor__code code-editor__code--language-${
84-
this.language
85-
}">${this.escapeHtml(code)}</code></pre>`
123+
return {
124+
type: 'doc',
125+
content: [
126+
{
127+
type: 'codeBlock',
128+
attrs: {
129+
language: this.language,
130+
},
131+
content: code ? [{ type: 'text', text: code }] : [],
132+
},
133+
],
134+
}
86135
},
87136
getCurrentCode() {
88-
const codeNode = this.editor?.getJSON()?.content?.[0]?.content?.[0]
89-
return codeNode?.text || ''
90-
},
91-
escapeHtml(string) {
92-
return string
93-
.replace(/&/g, '&amp;')
94-
.replace(/</g, '&lt;')
95-
.replace(/>/g, '&gt;')
96-
.replace(/"/g, '&quot;')
97-
.replace(/'/g, '&#039;')
137+
return this.editor?.getText() || ''
98138
},
99139
},
100140
}

0 commit comments

Comments
 (0)