Kaynağa Gözat

Remove CM5 Rich Text module (#11776)

GitOrigin-RevId: 812d3b5f1df7e769c8be732ccb31653e8e9a8aa3
Alf Eaton 3 yıl önce
ebeveyn
işleme
c8f0885316
37 değiştirilmiş dosya ile 129 ekleme ve 587 silme
  1. 0 12
      package-lock.json
  2. 1 9
      services/web/app/src/Features/Project/ProjectController.js
  3. 0 2
      services/web/app/views/project/editor.pug
  4. 3 6
      services/web/app/views/project/editor/editor-pane.pug
  5. 1 3
      services/web/app/views/project/editor/meta.pug
  6. 0 1
      services/web/app/views/project/editor/source-editor.pug
  7. 46 0
      services/web/app/views/project/editor/toolbar.pug
  8. 0 1
      services/web/app/views/source-editor/source-editor.pug
  9. 0 7
      services/web/frontend/js/features/outline/controllers/outline-controller.js
  10. 0 12
      services/web/frontend/js/features/outline/outline-manager.js
  11. 3 18
      services/web/frontend/js/features/source-editor/components/cm6-switch-away-survey.tsx
  12. 5 13
      services/web/frontend/js/features/source-editor/components/editor-switch-legacy.tsx
  13. 5 15
      services/web/frontend/js/features/source-editor/components/editor-switch.tsx
  14. 2 12
      services/web/frontend/js/features/source-editor/components/grammarly-warning.tsx
  15. 1 1
      services/web/frontend/js/features/source-editor/extensions/realtime.ts
  16. 1 0
      services/web/frontend/js/ide.js
  17. 0 26
      services/web/frontend/js/ide/editor/Document.js
  18. 1 16
      services/web/frontend/js/ide/editor/EditorManager.js
  19. 0 13
      services/web/frontend/js/ide/editor/ShareJsDoc.js
  20. 17 0
      services/web/frontend/js/ide/editor/directives/formattingButtons.js
  21. 4 8
      services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js
  22. 17 0
      services/web/frontend/js/ide/toolbar/EditorLoaderController.js
  23. 19 0
      services/web/frontend/js/ide/toolbar/EditorToolbarController.js
  24. 2 0
      services/web/frontend/js/ide/toolbar/index.js
  25. 0 114
      services/web/frontend/js/vendor/libs/sharejs.js
  26. 0 7
      services/web/frontend/stories/pdf-preview-messages.stories.js
  27. 1 3
      services/web/frontend/stylesheets/app/editor.less
  28. 0 6
      services/web/frontend/stylesheets/app/editor/review-panel.less
  29. 0 254
      services/web/frontend/stylesheets/app/editor/rich-text.less
  30. 0 1
      services/web/frontend/stylesheets/ieee-style.less
  31. 0 1
      services/web/frontend/stylesheets/light-style.less
  32. 0 1
      services/web/frontend/stylesheets/main-ieee-style.less
  33. 0 1
      services/web/frontend/stylesheets/main-light-style.less
  34. 0 1
      services/web/frontend/stylesheets/main-style.less
  35. 0 1
      services/web/frontend/stylesheets/style.less
  36. 0 1
      services/web/package.json
  37. 0 21
      services/web/test/frontend/features/source-editor/components/grammarly-warning.test.js

+ 0 - 12
package-lock.json

@@ -20065,11 +20065,6 @@
         "node": ">=0.12.0"
       }
     },
-    "node_modules/codemirror": {
-      "version": "5.33.0",
-      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.33.0.tgz",
-      "integrity": "sha512-HT6PKVqkwpzwB3jl5hXFoQteEWXbSWMzG3Z8RVYlx8hZwCOLCy4NU7vkSB3dYX3e6ORwRfGw4uFOXaw4rn/a9Q=="
-    },
     "node_modules/coffee-script": {
       "version": "1.12.7",
       "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz",
@@ -45222,7 +45217,6 @@
         "chartjs-adapter-moment": "^1.0.1",
         "chartjs-plugin-datalabels": "^2.2.0",
         "classnames": "^2.2.6",
-        "codemirror": "~5.33.0",
         "connect-redis": "^6.1.3",
         "content-disposition": "^0.5.0",
         "contentful": "^6.1.1",
@@ -54014,7 +54008,6 @@
         "chartjs-plugin-datalabels": "^2.2.0",
         "cheerio": "^1.0.0-rc.3",
         "classnames": "^2.2.6",
-        "codemirror": "~5.33.0",
         "connect-redis": "^6.1.3",
         "content-disposition": "^0.5.0",
         "contentful": "^6.1.1",
@@ -63599,11 +63592,6 @@
       "resolved": "https://registry.npmjs.org/co-use/-/co-use-1.1.0.tgz",
       "integrity": "sha1-xrs83xDLc17Kqdru2kbXJclKTmI="
     },
-    "codemirror": {
-      "version": "5.33.0",
-      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.33.0.tgz",
-      "integrity": "sha512-HT6PKVqkwpzwB3jl5hXFoQteEWXbSWMzG3Z8RVYlx8hZwCOLCy4NU7vkSB3dYX3e6ORwRfGw4uFOXaw4rn/a9Q=="
-    },
     "coffee-script": {
       "version": "1.12.7",
       "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz",

+ 1 - 9
services/web/app/src/Features/Project/ProjectController.js

@@ -662,6 +662,7 @@ const ProjectController = {
             }
           )
         },
+        // this is only needed until the survey link is removed from the toolbar
         richTextAssignment(cb) {
           SplitTestHandler.getAssignment(
             req,
@@ -755,7 +756,6 @@ const ProjectController = {
           brandVariation,
           pdfjsAssignment,
           editorLeftMenuAssignment,
-          richTextAssignment,
           sourceEditorToolbarAssigment,
           historyViewAssignment,
           reviewPanelAssignment,
@@ -880,13 +880,6 @@ const ProjectController = {
                 ? 'project/editor_detached'
                 : 'project/editor'
 
-            let richTextVariant
-            if (!Features.hasFeature('saas')) {
-              richTextVariant = 'cm6'
-            } else {
-              richTextVariant = richTextAssignment.variant
-            }
-
             res.render(template, {
               title: project.name,
               priority_title: true,
@@ -961,7 +954,6 @@ const ProjectController = {
               fixedSizeDocument: true,
               useOpenTelemetry: Settings.useOpenTelemetryClient,
               showCM6SwitchAwaySurvey: Settings.showCM6SwitchAwaySurvey,
-              richTextVariant,
               historyViewReact: historyViewAssignment.variant === 'react',
               isReviewPanelReact: reviewPanelAssignment.variant === 'react',
               showPersonalAccessToken,

+ 0 - 2
services/web/app/views/project/editor.pug

@@ -124,7 +124,5 @@ block foot-scripts
 	each file in (useOpenTelemetry ? entrypointScripts("tracing") : [])
 		script(type="text/javascript", nonce=scriptNonce, src=file)
 	script(type="text/javascript", nonce=scriptNonce, src=(wsUrl || '/socket.io') + '/socket.io.js')
-	if (richTextVariant !== 'cm6')
-		script(type="text/javascript", nonce=scriptNonce, src=mathJaxPath)
 	each file in entrypointScripts("ide")
 		script(type="text/javascript", nonce=scriptNonce, src=file)

+ 3 - 6
services/web/app/views/project/editor/editor-pane.pug

@@ -1,7 +1,6 @@
 .ui-layout-center(
 		ng-controller="ReviewPanelController",
 		ng-class="{\
-			'rp-unsupported': editor.showRichText,\
 			'rp-state-current-file': (reviewPanel.subView === SubViews.CUR_FILE),\
 			'rp-state-current-file-expanded': (reviewPanel.subView === SubViews.CUR_FILE && ui.reviewPanelOpen),\
 			'rp-state-current-file-mini': (reviewPanel.subView === SubViews.CUR_FILE && !ui.reviewPanelOpen),\
@@ -36,7 +35,6 @@
 
 				.loading-panel(
 					ng-show="(!editor.sharejs_doc || editor.opening) && !editor.error_state",
-					style=showRichText ? "top: 32px" : "",
 				)
 					span(ng-show="editor.open_doc_id")
 						i.fa.fa-spin.fa-refresh
@@ -45,10 +43,9 @@
 						i.fa.fa-arrow-left
 						|   #{translate("open_a_file_on_the_left")}
 
-				if moduleIncludesAvailable('editor:main')
-					!= moduleIncludes('editor:main', locals)
-				else
-					.toolbar.toolbar-editor
+				div(ng-controller="EditorLoaderController")
+					if (!showSourceToolbar)
+						include ./toolbar
 
 				div(ng-if="editor.newSourceEditor")
 					include ../../source-editor/source-editor

+ 1 - 3
services/web/app/views/project/editor/meta.pug

@@ -38,12 +38,10 @@ meta(name="ol-useOpenTelemetry" data-type="boolean" content=useOpenTelemetry)
 meta(name="ol-showSupport", data-type="boolean" content=showSupport)
 meta(name="ol-showTemplatesServerPro", data-type="boolean" content=showTemplatesServerPro)
 meta(name="ol-showCM6SwitchAwaySurvey", data-type="boolean" content=showCM6SwitchAwaySurvey)
-meta(name="ol-richTextVariant" content=richTextVariant)
 meta(name="ol-showPersonalAccessToken", data-type="boolean" content=showPersonalAccessToken)
 meta(name="ol-isReviewPanelReact", data-type="boolean" content=isReviewPanelReact)
 meta(name="ol-hasTrackChangesFeature", data-type="boolean" content=hasTrackChangesFeature)
-if (richTextVariant === 'cm6')
-	meta(name="ol-mathJax3Path" content=mathJax3Path)
+meta(name="ol-mathJax3Path" content=mathJax3Path)
 
 - var fileActionI18n = ['edited', 'renamed', 'created', 'deleted'].reduce((acc, i) => {acc[i] = translate('file_action_' + i); return acc}, {})
 meta(name="ol-fileActionI18n" data-type="json" content=fileActionI18n)

+ 0 - 1
services/web/app/views/project/editor/source-editor.pug

@@ -1,6 +1,5 @@
 #editor(
 	ace-editor="editor",
-	ng-if="!editor.showRichText",
 	ng-show="!!editor.sharejs_doc && !editor.opening && multiSelectedCount === 0 && !editor.error_state",
 	theme="settings.editorTheme",
 	keybindings="settings.mode",

+ 46 - 0
services/web/app/views/project/editor/toolbar.pug

@@ -0,0 +1,46 @@
+.toolbar.toolbar-editor(ng-controller="EditorToolbarController")
+	.toggle-wrapper
+		editor-switch
+
+	div(
+		formatting-buttons
+		ng-cloak
+		ng-if="!editor.showVisual"
+		buttons="editorButtons"
+		opening="editor.opening"
+		resize-on="layout:main:resize,layout:pdf:resize,layout:review:resize,review-panel:toggle"
+		is-fullscreen-editor="ui.view == 'editor' && ui.pdfLayout == 'flat'"
+		class="formatting-buttons"
+	)
+	div(
+		formatting-buttons
+		ng-cloak
+		ng-if="editor.showVisual"
+		buttons="[]"
+		opening="editor.opening"
+		resize-on="layout:main:resize,layout:pdf:resize,layout:review:resize,review-panel:toggle"
+		is-fullscreen-editor="ui.view == 'editor' && ui.pdfLayout == 'flat'"
+		class="formatting-buttons"
+	)
+
+	.toolbar-pdf-right
+		switch-to-pdf-button()
+		detacher-synctex-control()
+		editor-compile-button()
+
+	script(type="text/ng-template", id="formattingButtonsTpl")
+		.formatting-buttons-wrapper
+			|  
+			button.btn.formatting-btn.formatting-btn--icon(
+				ng-repeat="button in shownButtons"
+				ng-click="button.handleClick()"
+				ng-class="{ active: button.active }",
+				aria-label="{{button.title}}"
+				tooltip="{{button.title}}"
+				tooltip-placement="bottom"
+				tooltip-append-to-body="true"
+			)
+				i(class="{{button.iconClass}}") {{button.iconText}}
+
+			if moduleIncludesAvailable('editor:galileo')
+				galileo-toolbar-button

+ 0 - 1
services/web/app/views/source-editor/source-editor.pug

@@ -1,5 +1,4 @@
 source-editor#editor(
 	ng-class="{ 'review-panel-react': reviewPanel.isReact }"
-	ng-if="!editor.showRichText" 
 	ng-show="!!editor.sharejs_doc && !editor.opening && multiSelectedCount === 0 && !editor.error_state"
 )

+ 0 - 7
services/web/frontend/js/features/outline/controllers/outline-controller.js

@@ -9,9 +9,6 @@ App.controller('OutlineController', function ($scope, ide, eventTracking) {
   $scope.eventTracking = eventTracking
 
   function shouldShowOutline() {
-    if ($scope.editor.showRichText) {
-      return true
-    }
     return !$scope.editor.newSourceEditor
   }
 
@@ -21,10 +18,6 @@ App.controller('OutlineController', function ($scope, ide, eventTracking) {
     $scope.show = shouldShowOutline()
   })
 
-  $scope.$watch('editor.showRichText', function () {
-    $scope.show = shouldShowOutline()
-  })
-
   $scope.$on('outline-manager:outline-changed', onOutlineChange)
 
   function onOutlineChange(e, outlineInfo) {

+ 0 - 12
services/web/frontend/js/features/outline/outline-manager.js

@@ -68,21 +68,9 @@ class OutlineManager {
 
       this.updateHighlightedLine(middleVisibleRow + 1)
     })
-
-    scope.$watch('editor.showRichText', () => {
-      this.ignoreNextScroll = true
-      this.ignoreNextCursorUpdate = true
-      if (this.shouldShowOutline()) {
-        this.updateOutline()
-        this.broadcastChangeEvent()
-      }
-    })
   }
 
   shouldShowOutline() {
-    if (this.scope.editor.showRichText) {
-      return true
-    }
     return !this.scope.editor.newSourceEditor
   }
 

+ 3 - 18
services/web/frontend/js/features/source-editor/components/cm6-switch-away-survey.tsx

@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useRef, useState } from 'react'
+import { useCallback, useEffect, useState } from 'react'
 import { Button } from 'react-bootstrap'
 import useScopeValue from '../../../shared/hooks/use-scope-value'
 import {
@@ -12,33 +12,18 @@ type CM6SwitchAwaySurveyState = 'disabled' | 'enabled' | 'shown'
 export default function CM6SwitchAwaySurvey() {
   const [state, setState] = useState<CM6SwitchAwaySurveyState>('disabled')
   const [newSourceEditor] = useScopeValue('editor.newSourceEditor')
-  const [richText] = useScopeValue('editor.showRichText')
-  const initialRichTextPreference = useRef<boolean>(richText)
 
   useEffect(() => {
     // If the user has previously seen any switch-away survey, then don't show
     // the current one
     if (hasSeenCM6SwitchAwaySurvey()) return
 
-    if (initialRichTextPreference.current) {
-      if (!richText && newSourceEditor) {
-        // If user change from rich text to cm6, we remove the rich text
-        // preference so if user use rich text -> cm6 -> ace, we will show the
-        // current survey
-        initialRichTextPreference.current = false
-      }
-
-      // If the user loaded rich text initially, then don't show the survey
-      // (we are assuming that they will not have used CM6 as much)
-      return
-    }
-
-    if (!newSourceEditor && !richText) {
+    if (!newSourceEditor) {
       setState('enabled')
     } else {
       setState('disabled')
     }
-  }, [newSourceEditor, richText])
+  }, [newSourceEditor])
 
   useEffect(() => {
     const handleKeyDown = () => {

+ 5 - 13
services/web/frontend/js/features/source-editor/components/editor-switch-legacy.tsx

@@ -48,12 +48,12 @@ function EditorSwitch() {
   const [newSourceEditor, setNewSourceEditor] = useScopeValue(
     'editor.newSourceEditor'
   )
-  const [richText, setRichText] = useScopeValue('editor.showRichText')
   const [visual, setVisual] = useScopeValue('editor.showVisual')
 
   const [docName] = useScopeValue('editor.open_doc_name')
   const richTextAvailable = isValidTeXFile(docName)
-  const richTextOrVisual = richText || (richTextAvailable && visual)
+  // TODO: rename this after legacy & toolbar split tests are complete
+  const richTextOrVisual = richTextAvailable && visual
 
   const handleChange = useCallback(
     event => {
@@ -61,33 +61,25 @@ function EditorSwitch() {
 
       switch (editorType) {
         case 'ace':
-          setRichText(false)
           setVisual(false)
           setNewSourceEditor(false)
           break
 
         case 'cm6':
-          setRichText(false)
           setVisual(false)
           setNewSourceEditor(true)
           break
 
         case 'rich-text':
-          if (getMeta('ol-richTextVariant') === 'cm6') {
-            setRichText(false)
-            setVisual(true)
-            setNewSourceEditor(true)
-          } else {
-            setRichText(true)
-            setVisual(false)
-          }
+          setVisual(true)
+          setNewSourceEditor(true)
 
           break
       }
 
       sendMB('editor-switch-change', { editorType })
     },
-    [setRichText, setVisual, setNewSourceEditor]
+    [setVisual, setNewSourceEditor]
   )
 
   return (

+ 5 - 15
services/web/frontend/js/features/source-editor/components/editor-switch.tsx

@@ -2,7 +2,6 @@ import { ChangeEvent, FC, memo, useCallback } from 'react'
 import useScopeValue from '../../../shared/hooks/use-scope-value'
 import Tooltip from '../../../shared/components/tooltip'
 import { sendMB } from '../../../infrastructure/event-tracking'
-import getMeta from '../../../utils/meta'
 import isValidTeXFile from '../../../main/is-valid-tex-file'
 import { useTranslation } from 'react-i18next'
 import SplitTestBadge from '../../../shared/components/split-test-badge'
@@ -12,12 +11,12 @@ function EditorSwitch() {
   const [newSourceEditor, setNewSourceEditor] = useScopeValue(
     'editor.newSourceEditor'
   )
-  const [richText, setRichText] = useScopeValue('editor.showRichText')
   const [visual, setVisual] = useScopeValue('editor.showVisual')
 
   const [docName] = useScopeValue('editor.open_doc_name')
   const richTextAvailable = isValidTeXFile(docName)
-  const richTextOrVisual = richText || (richTextAvailable && visual)
+  // TODO: rename this after legacy & toolbar split tests are complete
+  const richTextOrVisual = richTextAvailable && visual
 
   const handleChange = useCallback(
     event => {
@@ -25,33 +24,24 @@ function EditorSwitch() {
 
       switch (editorType) {
         case 'ace':
-          setRichText(false)
           setVisual(false)
           setNewSourceEditor(false)
           break
 
         case 'cm6':
-          setRichText(false)
           setVisual(false)
           setNewSourceEditor(true)
           break
 
         case 'rich-text':
-          if (getMeta('ol-richTextVariant') === 'cm6') {
-            setRichText(false)
-            setVisual(true)
-            setNewSourceEditor(true)
-          } else {
-            setRichText(true)
-            setVisual(false)
-          }
-
+          setVisual(true)
+          setNewSourceEditor(true)
           break
       }
 
       sendMB('editor-switch-change', { editorType })
     },
-    [setRichText, setVisual, setNewSourceEditor]
+    [setVisual, setNewSourceEditor]
   )
 
   return (

+ 2 - 12
services/web/frontend/js/features/source-editor/components/grammarly-warning.tsx

@@ -12,7 +12,6 @@ type GrammarlyWarningProps = {
 export default function GrammarlyWarning({ delay }: GrammarlyWarningProps) {
   const [show, setShow] = useState(false)
   const [newSourceEditor] = useScopeValue('editor.newSourceEditor')
-  const [showRichText] = useScopeValue('editor.showRichText')
   const grammarly = grammarlyExtensionPresent()
   const hasDismissedGrammarlyWarning = customLocalStorage.getItem(
     'editor.has_dismissed_grammarly_warning'
@@ -20,10 +19,7 @@ export default function GrammarlyWarning({ delay }: GrammarlyWarningProps) {
 
   useEffect(() => {
     const showGrammarlyWarning =
-      !hasDismissedGrammarlyWarning &&
-      grammarly &&
-      newSourceEditor &&
-      !showRichText
+      !hasDismissedGrammarlyWarning && grammarly && newSourceEditor
 
     let timeoutID: Nullable<number>
 
@@ -41,13 +37,7 @@ export default function GrammarlyWarning({ delay }: GrammarlyWarningProps) {
         clearTimeout(timeoutID)
       }
     }
-  }, [
-    grammarly,
-    hasDismissedGrammarlyWarning,
-    newSourceEditor,
-    showRichText,
-    delay,
-  ])
+  }, [grammarly, hasDismissedGrammarlyWarning, newSourceEditor, delay])
 
   const handleClose = useCallback(() => {
     setShow(false)

+ 1 - 1
services/web/frontend/js/features/source-editor/extensions/realtime.ts

@@ -106,7 +106,7 @@ export class EditorFacade extends EventEmitter {
   // as new transactions.
   // This is a broad immitation of helper functions supplied in
   // the sharejs library. (See vendor/libs/sharejs, in particular
-  // the 'attach_cm' and 'attach_ace' helpers)
+  // the 'attach_ace' helper)
   attachShareJs(shareDoc: ShareDoc, maxDocLength?: number) {
     this.shareDoc = shareDoc
     this.maxDocLength = maxDocLength

+ 1 - 0
services/web/frontend/js/ide.js

@@ -37,6 +37,7 @@ import './ide/clone/index'
 import './ide/file-view/index'
 import './ide/hotkeys/index'
 import './ide/wordcount/index'
+import './ide/toolbar/index'
 import './ide/directives/layout'
 import './ide/directives/validFile'
 import './ide/directives/verticalResizablePanes'

+ 0 - 26
services/web/frontend/js/ide/editor/Document.js

@@ -85,7 +85,6 @@ export default Document = (function () {
       this.joined = false
       this.wantToBeJoined = false
       this._checkAceConsistency = () => this._checkConsistency(this.ace)
-      this._checkCMConsistency = () => this._checkConsistency(this.cm)
       this._checkCM6Consistency = () => this._checkConsistency(this.cm6)
       this._bindToEditorEvents()
       this._bindToSocketEvents()
@@ -96,8 +95,6 @@ export default Document = (function () {
         return 'ace'
       } else if (this.cm6) {
         return 'cm6'
-      } else if (this.cm) {
-        return 'cm-rich-text'
       } else {
         return null
       }
@@ -127,29 +124,6 @@ export default Document = (function () {
       return this.ide.$scope.$emit('document:closed', this.doc)
     }
 
-    attachToCM(cm) {
-      this.cm = cm
-      if (this.doc != null) {
-        this.doc.attachToCM(this.cm)
-      }
-      if (this.cm != null) {
-        this.cm.on('change', this._checkCMConsistency)
-      }
-      return this.ide.$scope.$emit('document:opened', this.doc)
-    }
-
-    detachFromCM() {
-      if (this.doc != null) {
-        this.doc.detachFromCM()
-      }
-      if (this.cm != null) {
-        this.cm.off('change', this._checkCMConsistency)
-      }
-      delete this.cm
-      this.clearChaosMonkey()
-      return this.ide.$scope.$emit('document:closed', this.doc)
-    }
-
     attachToCM6(cm6) {
       this.cm6 = cm6
       if (this.doc != null) {

+ 1 - 16
services/web/frontend/js/ide/editor/EditorManager.js

@@ -17,6 +17,7 @@ import _ from 'lodash'
 import Document from './Document'
 import './components/spellMenu'
 import './directives/aceEditor'
+import './directives/formattingButtons'
 import './directives/toggleSwitch'
 import './controllers/SavingNotificationController'
 import './controllers/CompileButton'
@@ -45,7 +46,6 @@ export default EditorManager = (function () {
         trackChanges: false,
         wantTrackChanges: false,
         docTooLongErrorShown: false,
-        showRichText: this.showRichText(),
         showVisual: this.showVisual(),
         newSourceEditor: this.newSourceEditor(),
         showSymbolPalette: false,
@@ -184,22 +184,7 @@ export default EditorManager = (function () {
       return editorType
     }
 
-    showRichText() {
-      if (getMeta('ol-richTextVariant') === 'cm6') {
-        return false
-      }
-
-      return (
-        this.localStorage(`editor.mode.${this.$scope.project_id}`) ===
-        'rich-text'
-      )
-    }
-
     showVisual() {
-      if (getMeta('ol-richTextVariant') !== 'cm6') {
-        return false
-      }
-
       return (
         this.localStorage(`editor.mode.${this.$scope.project_id}`) ===
         'rich-text'

+ 0 - 13
services/web/frontend/js/ide/editor/ShareJsDoc.js

@@ -366,19 +366,6 @@ export default ShareJsDoc = (function () {
         : undefined
     }
 
-    attachToCM(cm) {
-      this._attachToEditor('CM', cm, () => {
-        this._doc.attach_cm(cm, window.maxDocLength)
-      })
-    }
-
-    detachFromCM() {
-      this._maybeDetachEditorWatchdogManager()
-      return typeof this._doc.detach_cm === 'function'
-        ? this._doc.detach_cm()
-        : undefined
-    } // If we're waiting for the project to join, try again in 0.5 seconds
-
     attachToCM6(cm6) {
       this._attachToEditor('CM6', cm6, () => {
         cm6.attachShareJs(this._doc, window.maxDocLength)

+ 17 - 0
services/web/frontend/js/ide/editor/directives/formattingButtons.js

@@ -0,0 +1,17 @@
+import App from '../../../base'
+
+export default App.directive('formattingButtons', () => ({
+  scope: {
+    buttons: '=',
+    opening: '=',
+    isFullscreenEditor: '=',
+  },
+
+  link(scope, element, attrs) {
+    scope.showMore = false
+    scope.shownButtons = scope.buttons
+    scope.overflowedButtons = []
+  },
+
+  templateUrl: 'formattingButtonsTpl',
+}))

+ 4 - 8
services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js

@@ -285,8 +285,7 @@ export default App.controller(
     function updateScrollbar() {
       if (
         scrollbar.isVisible &&
-        ide.$scope.reviewPanel.subView === $scope.SubViews.CUR_FILE &&
-        !$scope.editor.showRichText
+        ide.$scope.reviewPanel.subView === $scope.SubViews.CUR_FILE
       ) {
         return $reviewPanelEl.css('right', `${scrollbar.scrollbarWidth}px`)
       } else {
@@ -554,13 +553,10 @@ export default App.controller(
       const doc_id = $scope.editor.open_doc_id
       const entries = updateEntries(doc_id)
 
-      // For now, not worrying about entry panels for rich text
-      if (!$scope.editor.showRichText) {
-        $scope.$broadcast('review-panel:recalculate-screen-positions')
-        dispatchReviewPanelEvent('recalculate-screen-positions', entries)
+      $scope.$broadcast('review-panel:recalculate-screen-positions')
+      dispatchReviewPanelEvent('recalculate-screen-positions', entries)
 
-        return ide.$scope.$broadcast('review-panel:layout')
-      }
+      return ide.$scope.$broadcast('review-panel:layout')
     })
 
     $scope.$on('editor:track-changes:visibility_changed', () =>

+ 17 - 0
services/web/frontend/js/ide/toolbar/EditorLoaderController.js

@@ -0,0 +1,17 @@
+import App from '../../base'
+
+App.controller('EditorLoaderController', function ($scope, localStorage) {
+  $scope.$watch('editor.showVisual', function (val) {
+    localStorage(
+      `editor.mode.${$scope.project_id}`,
+      val === true ? 'rich-text' : 'source'
+    )
+  })
+
+  $scope.$watch('editor.newSourceEditor', function (val) {
+    localStorage(
+      `editor.source_editor.${$scope.project_id}`,
+      val === true ? 'cm6' : 'ace'
+    )
+  })
+})

+ 19 - 0
services/web/frontend/js/ide/toolbar/EditorToolbarController.js

@@ -0,0 +1,19 @@
+import App from '../../base'
+import importOverleafModules from '../../../macros/import-overleaf-module.macro'
+
+const eModules = importOverleafModules('editorToolbarButtons')
+const editorToolbarButtons = eModules.map(item => item.import.default)
+
+export default App.controller('EditorToolbarController', ($scope, ide) => {
+  const editorButtons = []
+
+  for (const editorToolbarButton of editorToolbarButtons) {
+    const button = editorToolbarButton.button($scope, ide)
+
+    if (editorToolbarButton.source) {
+      editorButtons.push(button)
+    }
+  }
+
+  $scope.editorButtons = editorButtons
+})

+ 2 - 0
services/web/frontend/js/ide/toolbar/index.js

@@ -0,0 +1,2 @@
+import './EditorLoaderController'
+import './EditorToolbarController'

+ 0 - 114
services/web/frontend/js/vendor/libs/sharejs.js

@@ -1584,119 +1584,5 @@ define(['ace/ace','crypto-js/sha1'], function (_ignore, CryptoJSSHA1) {
     };
   });
 
-  // This is some utility code to connect a CodeMirror editor
-  // to a sharejs document.
-  // It is heavily inspired from the Ace editor hook.
-
-  // Convert a CodeMirror delta into an op understood by share.js
-  var applyCMToShareJS = function applyCMToShareJS(editorDoc, delta, doc, fromUndo) {
-    // CodeMirror deltas give a text replacement.
-    // I tuned this operation a little bit, for speed.
-    var startPos = 0; // Get character position from # of chars in each line.
-    var i = 0; // i goes through all lines.
-    // Compute the position from the shareJS snapshot because we are in the CodeMirror
-    // change event, where the change has already been applied to the editorDoc
-    var docLines = doc.snapshot.split('\n', delta.from.line) // only split the document as far as we need to
-    while (i < delta.from.line) {
-      startPos += docLines[i].length + 1; // Add 1 for '\n'
-      i++;
-    }
-    startPos += delta.from.ch;
-
-    // NOTE: Keep in sync with EditorWatchdogManager.
-    if (delta.removed) {
-      doc.del(startPos, delta.removed.join('\n').length, fromUndo);
-    }
-    if (delta.text) {
-      return doc.insert(startPos, delta.text.join('\n'), fromUndo);
-    }
-  };
-
-  // Attach a CodeMirror editor to the document. The editor's contents are replaced
-  // with the document's contents.
-  // NOTE: When upgrading CM, make sure to check for new special cases of
-  //        origin prefixes as documented for `doc.setSelection`. We are using
-  //        a custom `origin: 'remote'` which may conflict.
-  //       Perma link of the docs at the time of writing this note:
-  // https://web.archive.org/web/20201029163528/https://codemirror.net/doc/manual.html#selection_origin
-  window.sharejs.extendDoc('attach_cm', function (editor, maxDocLength) {
-    if (!this.provides.text) {
-      throw new Error('Only text documents can be attached to CodeMirror2');
-    }
-
-    var sharedoc = this;
-    var editorDoc = editor.getDoc();
-
-    function check() {
-      return window.setTimeout(function () {
-        var editorText = editor.getValue();
-        var otText = sharedoc.getText();
-
-        if (editorText !== otText) {
-          sharedoc.emit('error','Text does not match in CodeMirror')
-          console.error('Text does not match!');
-          console.error('editor: ' + editorText);
-          return console.error('ot:     ' + otText);
-        }
-      }
-      // Removed editor.setValue here as it would cause recursive loops if
-      // consistency check failed - because setting the value would trigger
-      // the change event
-      , 0);
-    };
-
-    onDelete(0, editor.getValue());
-    onInsert(0, sharedoc.getText());
-
-    check();
-
-    // Listen for edits in CodeMirror.
-    function editorListener(ed, change) {
-      if (change.origin === 'remote') {
-        // this change has been injected via sharejs
-        return;
-      }
-      if (maxDocLength != null && editorDoc.getValue().length >= maxDocLength) {
-        sharedoc.emit('error', new Error('document length is greater than maxDocLength'));
-        return;
-      }
-      var fromUndo = (change.origin === 'undo')
-      applyCMToShareJS(editorDoc, change, sharedoc, fromUndo);
-      return check();
-    };
-
-    editorDoc.on('change', editorListener);
-
-    function onInsert(pos, text) {
-      // All the primitives we need are already in CM's API.
-      // call signature: editor.replaceRange(text, from, to, origin)
-      editor.replaceRange(text, editor.posFromIndex(pos), undefined, 'remote');
-      // Clear CM's undo/redo history on remote edit. This prevents issues where
-      // a user can accidentally remove another user's edits
-      editor.clearHistory();
-      return check();
-    };
-
-    function onDelete(pos, text) {
-      var from = editor.posFromIndex(pos);
-      var to = editor.posFromIndex(pos + text.length);
-      editor.replaceRange('', from, to, 'remote');
-      // Clear CM's undo/redo history on remote edit. This prevents issues where
-      // a user can accidentally remove another user's edits
-      editor.clearHistory()
-      return check();
-    };
-
-    this.on('insert', onInsert);
-    this.on('delete', onDelete);
-
-    this.detach_cm = function () {
-      this.removeListener('insert', onInsert);
-      this.removeListener('delete', onDelete);
-      editorDoc.off('change', editorListener);
-      return delete this.detach_cm;
-    };
-  });
-
   return window.sharejs;
 });

+ 0 - 7
services/web/frontend/stories/pdf-preview-messages.stories.js

@@ -2,7 +2,6 @@ import { ScopeDecorator } from './decorators/scope'
 import { useLocalCompileContext } from '../js/shared/context/local-compile-context'
 import { useEffect } from 'react'
 import { PdfPreviewMessages } from '../js/features/pdf-preview/components/pdf-preview-messages'
-import { useScope } from './hooks/use-scope'
 import CompileTimeWarning from '../js/features/pdf-preview/components/compile-time-warning'
 
 export default {
@@ -18,12 +17,6 @@ export const Dismissible = () => {
     setShowCompileTimeWarning(true)
   }, [setShowCompileTimeWarning])
 
-  useScope({
-    editor: {
-      showRichText: true,
-    },
-  })
-
   return (
     <div style={{ width: 800, position: 'relative' }}>
       <PdfPreviewMessages>

+ 1 - 3
services/web/frontend/stylesheets/app/editor.less

@@ -13,7 +13,6 @@
 @import './editor/online-users.less';
 @import './editor/hotkeys.less';
 @import './editor/review-panel.less';
-@import './editor/rich-text.less';
 @import './editor/publish-modal.less';
 @import './editor/outline.less';
 @import './editor/logs.less';
@@ -93,8 +92,7 @@
   }
 }
 
-#editor,
-#editor-rich-text {
+#editor {
   .full-size;
 }
 

+ 0 - 6
services/web/frontend/stylesheets/app/editor/review-panel.less

@@ -858,12 +858,6 @@
   right: 0;
 }
 
-#editor-rich-text {
-  .rp-size-expanded & {
-    right: @review-panel-width;
-  }
-}
-
 .rp-unsupported-msg-wrapper {
   display: none;
 

+ 0 - 254
services/web/frontend/stylesheets/app/editor/rich-text.less

@@ -1,254 +0,0 @@
-@rt-font-family-serif: 'Palatino Linotype', 'Book Antiqua', Palatino, serif;
-@rt-line-padding: 8%;
-
-.rich-text .CodeMirror {
-  font-family: @rt-font-family-serif;
-  font-size: 1.15em;
-
-  pre {
-    font-family: @rt-font-family-serif;
-  }
-
-  .CodeMirror-line {
-    // Add horizontal padding, to emulate a manuscript more closely
-    padding: 0 @rt-line-padding;
-  }
-
-  .CodeMirror-linenumber {
-    font-size: 0.8em;
-  }
-
-  // TODO: Change prefix away from wl- ?
-
-  /****************************************************************************/
-
-  .preamble h1 {
-    text-align: center;
-    font-size: 2em;
-    color: @text-color;
-  }
-
-  .preamble ul.authors {
-    margin-left: 0;
-    padding-left: 0;
-    list-style: none;
-    text-align: center;
-  }
-
-  .preamble ul.authors li {
-    padding-bottom: 5px;
-  }
-
-  /****************************************************************************/
-
-  // wl-indent-X is used to add extra left padding to nested itemize/enumerate
-  // environments, so that the inner list appears more indented than the outer
-  .wl-indent-0 {
-    padding-left: calc(~'2.5em + @{rt-line-padding}') !important;
-  }
-
-  .wl-indent-1 {
-    padding-left: calc(~'3.5em + @{rt-line-padding}') !important;
-  }
-
-  .wl-indent-2 {
-    padding-left: calc(~'4.5em + @{rt-line-padding}') !important;
-  }
-
-  .wl-indent-3 {
-    padding-left: calc(~'5.5em + @{rt-line-padding}') !important;
-  }
-
-  .wl-indent-4 {
-    padding-left: calc(~'6.5em + @{rt-line-padding}') !important;
-  }
-
-  // wl-indent-env-X is used to add extra left padding to empty nested itemize/
-  // enumerate environments
-  .wl-indent-env-0 {
-    padding-left: calc(~'4px + @{rt-line-padding}') !important;
-  }
-
-  .wl-indent-env-1 {
-    padding-left: calc(~'1.5em + @{rt-line-padding}') !important;
-  }
-
-  .wl-indent-env-2 {
-    padding-left: calc(~'2.5em + @{rt-line-padding}') !important;
-  }
-
-  .wl-indent-env-3 {
-    padding-left: calc(~'3.5em + @{rt-line-padding}') !important;
-  }
-
-  .wl-indent-env-4 {
-    padding-left: calc(~'4.5em + @{rt-line-padding}') !important;
-  }
-
-  .wl-enumerate-item-open {
-    text-align: right;
-    width: 1.5em;
-    display: inline-block;
-    padding-right: 0.2em;
-  }
-
-  .wl-item-open {
-    text-align: right;
-    width: 1.5em;
-    display: inline-block;
-    padding-right: 0.2em;
-  }
-
-  .wl-input {
-    font-style: oblique;
-  }
-
-  /****************************************************************************/
-
-  .wl-abstract-open,
-  .wl-abstract-close {
-    border-top: 1px solid #999;
-    font-size: large;
-    font-weight: bold;
-    width: 100%;
-  }
-
-  .wl-figure {
-    max-height: 120px;
-    width: auto;
-    margin: 0 auto;
-  }
-
-  .wl-figure-wrap {
-    padding: 10px 0;
-    background-color: #f5f5f5;
-    box-shadow: 1.3px 2px 2px #dfdfdf;
-    width: 96%;
-    margin: 0 auto;
-    text-align: center;
-  }
-
-  .wl-figure-caption {
-    padding: 3px 0 4px;
-    font-size: small;
-    margin: 0 auto;
-    text-align: center;
-  }
-
-  /****************************************************************************/
-
-  .wl-chapter,
-  .wl-chapter-open,
-  .wl-chapter-close {
-    font-size: 2.2em;
-    font-weight: bold;
-  }
-
-  .wl-chapter-open,
-  .wl-chapter-close {
-    color: #999;
-  }
-
-  /****************************************************************************/
-
-  .wl-section,
-  .wl-section-open,
-  .wl-section-close {
-    font-size: 1.8em;
-    font-weight: bold;
-  }
-
-  .wl-section-open,
-  .wl-section-close {
-    color: #999;
-  }
-
-  /****************************************************************************/
-  .wl-subsection,
-  .wl-subsection-open,
-  .wl-subsection-close {
-    font-size: 1.5em;
-    font-weight: bold;
-  }
-
-  .wl-subsection-open,
-  .wl-subsection-close {
-    color: #999;
-  }
-
-  /****************************************************************************/
-  .wl-subsubsection,
-  .wl-subsubsection-open,
-  .wl-subsubsection-close {
-    font-size: 1.1em;
-    font-weight: bold;
-  }
-
-  .wl-subsubsection-open,
-  .wl-subsubsection-close {
-    color: #999;
-  }
-
-  /****************************************************************************/
-  .wl-textbf {
-    font-weight: bold;
-  }
-
-  .wl-textbf-open {
-    font-weight: bold;
-    color: #999;
-  }
-
-  .wl-textbf-close {
-    font-weight: bold;
-    color: #999;
-  }
-
-  .wl-label-bracket {
-    font-weight: bold;
-    color: #999;
-  }
-
-  .wl-label-open,
-  .wl-input-link {
-    .wl-icon {
-      padding-left: 5px;
-      padding-right: 2px;
-      vertical-align: middle;
-      // @include wl-icon-size(inherit);
-    }
-  }
-
-  .wl-img-default {
-    width: 0.9em;
-    padding: 0 1px 1px;
-  }
-
-  .wl-label-close {
-    background-color: #f7f7f9;
-    border: 1px solid #e1e1e8;
-    border-radius: 4px;
-    font-size: small;
-  }
-
-  /****************************************************************************/
-  .wl-textit {
-    font-style: italic;
-  }
-
-  .wl-textit-open {
-    font-style: italic;
-    color: #999;
-  }
-
-  .wl-textit-close {
-    font-style: italic;
-    color: #999;
-  }
-
-  .spelling-error {
-    background-image: url(../../../../public/img/spellcheck-underline.png);
-    background-repeat: repeat-x;
-    background-position: bottom;
-  }
-}

+ 0 - 1
services/web/frontend/stylesheets/ieee-style.less

@@ -2,7 +2,6 @@
 @import 'core/ol-ieee-variables.less';
 
 @is-overleaf-light: false;
-@show-rich-text: true;
 
 @ieee-wedge: 30px;
 

+ 0 - 1
services/web/frontend/stylesheets/light-style.less

@@ -2,4 +2,3 @@
 @import 'core/ol-light-variables.less';
 
 @is-overleaf-light: true;
-@show-rich-text: true;

+ 0 - 1
services/web/frontend/stylesheets/main-ieee-style.less

@@ -2,7 +2,6 @@
 @import 'variables/themes/ieee.less';
 
 @is-overleaf-light: false;
-@show-rich-text: true;
 
 @ieee-wedge: 30px;
 

+ 0 - 1
services/web/frontend/stylesheets/main-light-style.less

@@ -2,4 +2,3 @@
 @import 'variables/themes/light.less';
 
 @is-overleaf-light: true;
-@show-rich-text: true;

+ 0 - 1
services/web/frontend/stylesheets/main-style.less

@@ -6,7 +6,6 @@
 @import (less) '../fonts/material-symbols.css';
 
 @is-overleaf-light: false;
-@show-rich-text: true;
 @is-new-css: true;
 
 // Core variables and mixins

+ 0 - 1
services/web/frontend/stylesheets/style.less

@@ -6,7 +6,6 @@
 @import (less) '../fonts/material-symbols.css';
 
 @is-overleaf-light: false;
-@show-rich-text: true;
 @is-new-css: false;
 
 // Core variables and mixins

+ 0 - 1
services/web/package.json

@@ -146,7 +146,6 @@
     "chartjs-adapter-moment": "^1.0.1",
     "chartjs-plugin-datalabels": "^2.2.0",
     "classnames": "^2.2.6",
-    "codemirror": "~5.33.0",
     "connect-redis": "^6.1.3",
     "content-disposition": "^0.5.0",
     "contentful": "^6.1.1",

+ 0 - 21
services/web/test/frontend/features/source-editor/components/grammarly-warning.test.js

@@ -104,27 +104,6 @@ describe('<GrammarlyWarning />', function () {
     })
   })
 
-  it('does not show warning when user have rich text as their preference', async function () {
-    grammarlyStub.returns(true)
-
-    renderWithEditorContext(<GrammarlyWarning delay={100} />, {
-      scope: {
-        editor: {
-          newSourceEditor: true,
-          showRichText: true,
-        },
-      },
-    })
-
-    await waitFor(() => {
-      expect(
-        screen.queryByText(
-          'A browser extension, for example Grammarly, may be slowing down Overleaf.'
-        )
-      ).to.not.exist
-    })
-  })
-
   it('hides warning if close button is pressed', async function () {
     grammarlyStub.returns(true)