Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion client/components/editor/editor-asciidoc.vue
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/fold/foldcode.js'
import 'codemirror/addon/fold/foldgutter.js'
import 'codemirror/addon/fold/foldgutter.css'
import { getCodeMirrorInputStyle } from '../../helpers/codemirror'
import cmFold from './common/cmFold'

// ========================================
Expand Down Expand Up @@ -403,7 +404,7 @@ export default {
annotateScrollbar: true
},
viewportMargin: 50,
inputStyle: 'contenteditable',
inputStyle: getCodeMirrorInputStyle(),
allowDropFileTypes: ['image/jpg', 'image/png', 'image/svg', 'image/jpeg', 'image/gif'],
direction: siteConfig.rtl ? 'rtl' : 'ltr',
foldGutter: true,
Expand Down
3 changes: 2 additions & 1 deletion client/components/editor/editor-code.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import 'codemirror/addon/display/fullscreen.js'
import 'codemirror/addon/display/fullscreen.css'
import 'codemirror/addon/selection/mark-selection.js'
import 'codemirror/addon/search/searchcursor.js'
import { getCodeMirrorInputStyle } from '../../helpers/codemirror'

// ========================================
// INIT
Expand Down Expand Up @@ -193,7 +194,7 @@ export default {
annotateScrollbar: true
},
viewportMargin: 50,
inputStyle: 'contenteditable',
inputStyle: getCodeMirrorInputStyle(),
allowDropFileTypes: ['image/jpg', 'image/png', 'image/svg', 'image/jpeg', 'image/gif']
})
this.cm.setValue(this.$store.get('editor/content'))
Expand Down
3 changes: 2 additions & 1 deletion client/components/editor/editor-markdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/fold/foldcode.js'
import 'codemirror/addon/fold/foldgutter.js'
import 'codemirror/addon/fold/foldgutter.css'
import { getCodeMirrorInputStyle } from '../../helpers/codemirror'

// Markdown-it
import MarkdownIt from 'markdown-it'
Expand Down Expand Up @@ -751,7 +752,7 @@ export default {
annotateScrollbar: true
},
viewportMargin: 50,
inputStyle: 'contenteditable',
inputStyle: getCodeMirrorInputStyle(),
allowDropFileTypes: ['image/jpg', 'image/png', 'image/svg', 'image/jpeg', 'image/gif'],
direction: siteConfig.rtl ? 'rtl' : 'ltr',
foldGutter: true,
Expand Down
3 changes: 2 additions & 1 deletion client/components/editor/editor-modal-properties.vue
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ import CodeMirror from 'codemirror'
import 'codemirror/lib/codemirror.css'
import 'codemirror/mode/htmlmixed/htmlmixed.js'
import 'codemirror/mode/css/css.js'
import { getCodeMirrorInputStyle } from '../../helpers/codemirror'

/* global siteLangs, siteConfig */
const filenamePattern = /^(?![\#\/\.\$\^\=\*\;\:\&\?\(\)\[\]\{\}\"\'\>\<\,\@\!\%\`\~\s])(?!.*[\#\/\.\$\^\=\*\;\:\&\?\(\)\[\]\{\}\"\'\>\<\,\@\!\%\`\~\s]$)[^\#\.\$\^\=\*\;\:\&\?\(\)\[\]\{\}\"\'\>\<\,\@\!\%\`\~\s]*$/
Expand Down Expand Up @@ -367,7 +368,7 @@ export default {
line: true,
styleActiveLine: true,
viewportMargin: 50,
inputStyle: 'contenteditable',
inputStyle: getCodeMirrorInputStyle(),
direction: 'ltr'
})
switch (mode) {
Expand Down
21 changes: 21 additions & 0 deletions client/helpers/codemirror.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const DEFAULT_CODEMIRROR_INPUT_STYLE = 'contenteditable'

export function isIOSDevice({ userAgent = '', platform = '', maxTouchPoints = 0 } = {}) {
return /iP(?:ad|hone|od)/.test(userAgent) || (platform === 'MacIntel' && maxTouchPoints > 1)
}

export function getCodeMirrorInputStyle(nav = (typeof navigator !== 'undefined' ? navigator : null)) {
if (!nav) {
return DEFAULT_CODEMIRROR_INPUT_STYLE
}

// iOS Korean IME uses a fragile contenteditable/beforeinput path in Safari/WebKit.
// Falling back to the textarea input style avoids broken Hangul composition.
return isIOSDevice({
userAgent: nav.userAgent,
platform: nav.platform,
maxTouchPoints: nav.maxTouchPoints || 0
}) ? 'textarea' : DEFAULT_CODEMIRROR_INPUT_STYLE
}

export default getCodeMirrorInputStyle
27 changes: 27 additions & 0 deletions client/helpers/codemirror.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { getCodeMirrorInputStyle, isIOSDevice } from './codemirror'

describe('helpers/codemirror', () => {
it('detects classic iOS user agents', () => {
expect(isIOSDevice({
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1',
platform: 'iPhone',
maxTouchPoints: 5
})).toBe(true)
})

it('detects iPadOS devices that expose a desktop platform', () => {
expect(getCodeMirrorInputStyle({
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1',
platform: 'MacIntel',
maxTouchPoints: 5
})).toBe('textarea')
})

it('keeps the existing contenteditable path on desktop browsers', () => {
expect(getCodeMirrorInputStyle({
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36',
platform: 'MacIntel',
maxTouchPoints: 0
})).toBe('contenteditable')
})
})