• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

EcrituresNumeriques / stylo / 14855831858

06 May 2025 09:07AM UTC coverage: 37.734% (+0.008%) from 37.726%
14855831858

push

github

web-flow
fix: force la séquence de fin de ligne à LF sur le model Monaco (#1485)

532 of 760 branches covered (70.0%)

Branch coverage included in aggregate %.

0 of 3 new or added lines in 1 file covered. (0.0%)

5239 of 14534 relevant lines covered (36.05%)

2.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/front/src/components/collaborative/CollaborativeTextEditor.jsx
1
import Editor from '@monaco-editor/react'
×
2
import clsx from 'clsx'
×
3
import throttle from 'lodash.throttle'
×
4
import React, { useCallback, useEffect, useMemo, useRef } from 'react'
×
5
import { shallowEqual, useDispatch, useSelector } from 'react-redux'
×
6
import { MonacoBinding } from 'y-monaco'
×
7
import { useArticleVersion, useEditableArticle } from '../../hooks/article.js'
×
8
import { useBibliographyCompletion } from '../../hooks/bibliography.js'
×
9
import { useCollaboration } from '../../hooks/collaboration.js'
×
10
import Alert from '../molecules/Alert.jsx'
×
11

12
import Loading from '../molecules/Loading.jsx'
×
13
import defaultEditorOptions from '../Write/providers/monaco/options.js'
×
14
import CollaborativeEditorStatus from './CollaborativeEditorStatus.jsx'
×
15
import CollaborativeEditorWebSocketStatus from './CollaborativeEditorWebSocketStatus.jsx'
×
16

17
import styles from './CollaborativeTextEditor.module.scss'
×
18
import MonacoEditor from '../molecules/MonacoEditor.jsx'
×
19

20
/**
21
 * @param {object} props
22
 * @param {string} props.articleId
23
 * @param {string|undefined} props.versionId
24
 * @returns {Element}
25
 */
26
export default function CollaborativeTextEditor({ articleId, versionId }) {
×
27
  const { yText, awareness, websocketStatus, dynamicStyles } = useCollaboration(
×
28
    { articleId, versionId }
×
29
  )
×
30
  const { version, error, isLoading } = useArticleVersion({ versionId })
×
31
  const { provider: bibliographyCompletionProvider } =
×
32
    useBibliographyCompletion()
×
33
  const { bibliography } = useEditableArticle({
×
34
    articleId,
×
35
    versionId,
×
36
  })
×
37
  const dispatch = useDispatch()
×
38
  const editorRef = useRef(null)
×
39
  const editorCursorPosition = useSelector(
×
40
    (state) => state.editorCursorPosition,
×
41
    shallowEqual
×
42
  )
×
43

44
  const hasVersion = useMemo(() => !!versionId, [versionId])
×
45

46
  const options = useMemo(
×
47
    () => ({
×
48
      ...defaultEditorOptions,
×
49
      contextmenu: hasVersion ? false : websocketStatus === 'connected',
×
50
      readOnly: hasVersion ? true : websocketStatus !== 'connected',
×
51
    }),
×
52
    [websocketStatus, hasVersion]
×
53
  )
×
54

55
  const handleUpdateArticleStructureAndStats = throttle(
×
56
    ({ text }) => {
×
57
      dispatch({ type: 'UPDATE_ARTICLE_STATS', md: text })
×
58
      dispatch({ type: 'UPDATE_ARTICLE_STRUCTURE', md: text })
×
59
    },
×
60
    250,
×
61
    { leading: false, trailing: true }
×
62
  )
×
63

64
  const handleCollaborativeEditorDidMount = useCallback(
×
65
    (editor, monaco) => {
×
66
      editorRef.current = editor
×
67
      const completionProvider = bibliographyCompletionProvider.register(monaco)
×
68
      editor.onDidDispose(() => completionProvider.dispose())
×
NEW
69
      const model = editor.getModel()
×
70
      // Set EOL to LF otherwise it causes synchronization issues due to inconsistent EOL between Windows and Linux.
71
      // https://github.com/yjs/y-monaco/issues/27
NEW
72
      model.setEOL(monaco.editor.EndOfLineSequence.LF)
×
73
      if (yText && awareness) {
×
NEW
74
        new MonacoBinding(yText, model, new Set([editor]), awareness)
×
75
      }
×
76
    },
×
77
    [yText, awareness]
×
78
  )
×
79

80
  const handleEditorDidMount = useCallback((editor) => {
×
81
    editorRef.current = editor
×
82
  }, [])
×
83

84
  let timeoutId
×
85
  useEffect(() => {
×
86
    if (yText) {
×
87
      yText.observe(function (yTextEvent, transaction) {
×
88
        console.debug('yText.observe', {
×
89
          yTextEvent,
×
90
          transaction,
×
91
          text: yText.toString(),
×
92
          delta: yText.toDelta(),
×
93
        })
×
94
        dispatch({
×
95
          type: 'UPDATE_ARTICLE_WORKING_COPY_STATUS',
×
96
          status: 'syncing',
×
97
        })
×
98
        if (timeoutId) {
×
99
          clearTimeout(timeoutId)
×
100
        }
×
101
        timeoutId = setTimeout(() => {
×
102
          dispatch({
×
103
            type: 'UPDATE_ARTICLE_WORKING_COPY_STATUS',
×
104
            status: 'synced',
×
105
          })
×
106
        }, 4000)
×
107

108
        handleUpdateArticleStructureAndStats({ text: yText.toString() })
×
109
      })
×
110
    }
×
111
  }, [yText])
×
112

113
  useEffect(() => {
×
114
    if (version) {
×
115
      dispatch({ type: 'UPDATE_ARTICLE_STATS', md: version.md })
×
116
      dispatch({ type: 'UPDATE_ARTICLE_STRUCTURE', md: version.md })
×
117
    }
×
118
  }, [version])
×
119

120
  useEffect(() => {
×
121
    if (bibliography) {
×
122
      bibliographyCompletionProvider.bibTeXEntries = bibliography.entries
×
123
    }
×
124
  }, [bibliography])
×
125

126
  useEffect(() => {
×
127
    const line = editorCursorPosition.lineNumber
×
128
    const editor = editorRef.current
×
129
    editor?.focus()
×
130
    const endOfLineColumn = editor?.getModel()?.getLineMaxColumn(line + 1)
×
131
    editor?.setPosition({ lineNumber: line + 1, column: endOfLineColumn })
×
132
    editor?.revealLineNearTop(line + 1, 1) // smooth
×
133
  }, [editorRef, editorCursorPosition])
×
134

135
  if (!yText && !version) {
×
136
    return <Loading />
×
137
  }
×
138

139
  if (isLoading) {
×
140
    return <Loading />
×
141
  }
×
142

143
  if (error) {
×
144
    return <Alert message={error.message} />
×
145
  }
×
146

147
  return (
×
148
    <>
×
149
      <style>{dynamicStyles}</style>
×
150
      <CollaborativeEditorStatus versionId={versionId} />
×
151
      <div className={styles.inlineStatus}>
×
152
        <CollaborativeEditorWebSocketStatus status={websocketStatus} />
×
153
      </div>
×
154
      {version && (
×
155
        <MonacoEditor
×
156
          width={'100%'}
×
157
          height={'auto'}
×
158
          value={version.md}
×
159
          options={options}
×
160
          className={styles.editor}
×
161
          defaultLanguage="markdown"
×
162
          onMount={handleEditorDidMount}
×
163
        />
×
164
      )}
165
      <div
×
166
        className={clsx(styles.collaborativeEditor, versionId && styles.hidden)}
×
167
      >
168
        <MonacoEditor
×
169
          width={'100%'}
×
170
          height={'auto'}
×
171
          options={options}
×
172
          className={styles.editor}
×
173
          defaultLanguage="markdown"
×
174
          onMount={handleCollaborativeEditorDidMount}
×
175
        />
×
176
      </div>
×
177
    </>
×
178
  )
179
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc