diff --git a/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal.tsx b/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal.tsx
index 146c8fd0df..95d973564b 100644
--- a/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal.tsx
+++ b/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal.tsx
@@ -21,6 +21,7 @@ import {
import { ensureEmptyLine } from '../../extensions/toolbar/commands'
import { useTranslation } from 'react-i18next'
import useEventListener from '../../../../shared/hooks/use-event-listener'
+import { prepareLines } from '../../utils/prepare-lines'
export const FigureModal = memo(function FigureModal() {
return (
@@ -138,8 +139,8 @@ const FigureModalContent = () => {
dispatch({ error: String(error) })
return
}
- const labelCommand = includeLabel ? '\n\\label{fig:enter-label}' : ''
- const captionCommand = includeCaption ? '\n\\caption{Enter Caption}' : ''
+ const labelCommand = includeLabel ? '\\label{fig:enter-label}' : ''
+ const captionCommand = includeCaption ? '\\caption{Enter Caption}' : ''
if (figure) {
// Updating existing figure
@@ -150,14 +151,19 @@ const FigureModalContent = () => {
// We should insert a caption
changes.push({
from: figure.graphicsCommand.to,
- insert: captionCommand,
+ insert: prepareLines(
+ ['', captionCommand],
+ view.state,
+ figure.graphicsCommand.to
+ ),
})
}
if (!hadLabelBefore && includeLabel) {
+ const from = figure.caption?.to ?? figure.graphicsCommand.to
// We should insert a label
changes.push({
- from: figure.caption?.to ?? figure.graphicsCommand.to,
- insert: labelCommand,
+ from,
+ insert: prepareLines(['', labelCommand], view.state, from),
})
}
if (hadCaptionBefore && !includeCaption) {
@@ -203,11 +209,19 @@ const FigureModalContent = () => {
const { pos, suffix } = ensureEmptyLine(view.state, range)
const widthArgument =
width !== undefined ? `[width=${width}\\linewidth]` : ''
- const graphicxCommand = `\\includegraphics${widthArgument}{${path}}`
const changes: ChangeSpec = view.state.changes({
- insert: `\\begin{figure}\n\\centering\n${graphicxCommand}${captionCommand}${labelCommand}${
- labelCommand || captionCommand ? '\n' : '' // Add an extra newline if we've added a caption or label
- }\\end{figure}${suffix}`,
+ insert: prepareLines(
+ [
+ '\\begin{figure}',
+ '\t\\centering',
+ `\t\\includegraphics${widthArgument}{${path}}`,
+ `\t${captionCommand}` || null,
+ `\t${labelCommand}` || null,
+ `\\end{figure}${suffix}`,
+ ],
+ view.state,
+ pos
+ ),
from: pos,
})
diff --git a/services/web/frontend/js/features/source-editor/extensions/toolbar/commands.ts b/services/web/frontend/js/features/source-editor/extensions/toolbar/commands.ts
index 067e29c68d..50b8ade9b6 100644
--- a/services/web/frontend/js/features/source-editor/extensions/toolbar/commands.ts
+++ b/services/web/frontend/js/features/source-editor/extensions/toolbar/commands.ts
@@ -33,7 +33,8 @@ export const ensureEmptyLine = (state: EditorState, range: SelectionRange) => {
let suffix = ''
const line = state.doc.lineAt(pos)
- if (line.length) {
+
+ if (line.text.trim().length) {
pos = Math.min(line.to + 1, state.doc.length)
const nextLine = state.doc.lineAt(pos)
diff --git a/services/web/frontend/js/features/source-editor/utils/prepare-lines.ts b/services/web/frontend/js/features/source-editor/utils/prepare-lines.ts
new file mode 100644
index 0000000000..aa4d916906
--- /dev/null
+++ b/services/web/frontend/js/features/source-editor/utils/prepare-lines.ts
@@ -0,0 +1,33 @@
+/**
+ * Adapted from CodeMirror 6 (@codemirror/autocomplete), licensed under the MIT license:
+ * https://github.com/codemirror/autocomplete/blob/08f63add9f470a032d3802a4599caa86c75de5cb/src/snippet.ts#L29-L45
+ */
+
+import { indentUnit } from '@codemirror/language'
+import { EditorState } from '@codemirror/state'
+
+// apply correct indentation to passed lines
+export function prepareLines(
+ lines: (string | null)[],
+ state: EditorState,
+ pos: number
+) {
+ const text = []
+ const lineStart = [pos]
+ const lineObj = state.doc.lineAt(pos)
+ const baseIndent = /^\s*/.exec(lineObj.text)![0]
+ for (let line of lines) {
+ if (line === null) continue
+ if (text.length) {
+ let indent = baseIndent
+ const tabs = /^\t*/.exec(line)![0].length
+ for (let i = 0; i < tabs; i++) indent += state.facet(indentUnit)
+ lineStart.push(pos + indent.length - tabs)
+ line = indent + line.slice(tabs)
+ }
+ text.push(line)
+ pos += line.length + 1
+ }
+
+ return text.join('\n')
+}
diff --git a/services/web/test/frontend/features/source-editor/components/figure-modal.spec.tsx b/services/web/test/frontend/features/source-editor/components/figure-modal.spec.tsx
index b4d30c7f9f..34e0e2c0b4 100644
--- a/services/web/test/frontend/features/source-editor/components/figure-modal.spec.tsx
+++ b/services/web/test/frontend/features/source-editor/components/figure-modal.spec.tsx
@@ -94,7 +94,7 @@ describe('', function () {
cy.get('.cm-content').should(
'have.text',
- '\\begin{figure}\\centeringEnter Caption🏷fig:enter-label\\end{figure}'
+ '\\begin{figure} \\centering Enter Caption 🏷fig:enter-label\\end{figure}'
)
})
@@ -140,7 +140,7 @@ describe('', function () {
cy.findByText('Insert figure').click()
cy.get('.cm-content').should(
'have.text',
- '\\begin{figure}\\centeringEnter Caption🏷fig:enter-label\\end{figure}'
+ '\\begin{figure} \\centering Enter Caption 🏷fig:enter-label\\end{figure}'
)
})
})
@@ -214,7 +214,7 @@ describe('', function () {
cy.get('.cm-content').should(
'have.text',
- '\\begin{figure}\\centeringEnter Caption🏷fig:enter-label\\end{figure}'
+ '\\begin{figure} \\centering Enter Caption 🏷fig:enter-label\\end{figure}'
)
})
@@ -242,7 +242,7 @@ describe('', function () {
cy.get('.cm-content').should(
'have.text',
- '\\begin{figure}\\centeringEnter Caption🏷fig:enter-label\\end{figure}'
+ '\\begin{figure} \\centering Enter Caption 🏷fig:enter-label\\end{figure}'
)
})
})
@@ -292,7 +292,7 @@ describe('', function () {
cy.get('.cm-content').should(
'have.text',
- '\\begin{figure}\\centeringEnter Caption🏷fig:enter-label\\end{figure}'
+ '\\begin{figure} \\centering Enter Caption 🏷fig:enter-label\\end{figure}'
)
})
})