diff --git a/package-lock.json b/package-lock.json index bf8c04c424..167eba46b7 100644 --- a/package-lock.json +++ b/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", diff --git a/services/web/app/src/Features/Project/ProjectController.js b/services/web/app/src/Features/Project/ProjectController.js index c048adb317..ae3dae9332 100644 --- a/services/web/app/src/Features/Project/ProjectController.js +++ b/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, diff --git a/services/web/app/views/project/editor.pug b/services/web/app/views/project/editor.pug index 1c1191ca99..39553d877a 100644 --- a/services/web/app/views/project/editor.pug +++ b/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) diff --git a/services/web/app/views/project/editor/editor-pane.pug b/services/web/app/views/project/editor/editor-pane.pug index 66fb1217c0..3b53302ac6 100644 --- a/services/web/app/views/project/editor/editor-pane.pug +++ b/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 diff --git a/services/web/app/views/project/editor/meta.pug b/services/web/app/views/project/editor/meta.pug index 9c4b1b9fd1..20a41878a0 100644 --- a/services/web/app/views/project/editor/meta.pug +++ b/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) diff --git a/services/web/app/views/project/editor/source-editor.pug b/services/web/app/views/project/editor/source-editor.pug index 472ab598ad..ea5e115bd5 100644 --- a/services/web/app/views/project/editor/source-editor.pug +++ b/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", diff --git a/services/web/app/views/project/editor/toolbar.pug b/services/web/app/views/project/editor/toolbar.pug new file mode 100644 index 0000000000..39b2778a84 --- /dev/null +++ b/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 diff --git a/services/web/app/views/source-editor/source-editor.pug b/services/web/app/views/source-editor/source-editor.pug index 0356df65d3..0ae753f4d6 100644 --- a/services/web/app/views/source-editor/source-editor.pug +++ b/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" ) diff --git a/services/web/frontend/js/features/outline/controllers/outline-controller.js b/services/web/frontend/js/features/outline/controllers/outline-controller.js index d0da5affc2..4f012cc5df 100644 --- a/services/web/frontend/js/features/outline/controllers/outline-controller.js +++ b/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) { diff --git a/services/web/frontend/js/features/outline/outline-manager.js b/services/web/frontend/js/features/outline/outline-manager.js index 9a04ef7426..b49299718f 100644 --- a/services/web/frontend/js/features/outline/outline-manager.js +++ b/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 } diff --git a/services/web/frontend/js/features/source-editor/components/cm6-switch-away-survey.tsx b/services/web/frontend/js/features/source-editor/components/cm6-switch-away-survey.tsx index b058180740..009ac5193e 100644 --- a/services/web/frontend/js/features/source-editor/components/cm6-switch-away-survey.tsx +++ b/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('disabled') const [newSourceEditor] = useScopeValue('editor.newSourceEditor') - const [richText] = useScopeValue('editor.showRichText') - const initialRichTextPreference = useRef(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 = () => { diff --git a/services/web/frontend/js/features/source-editor/components/editor-switch-legacy.tsx b/services/web/frontend/js/features/source-editor/components/editor-switch-legacy.tsx index a16d2ef28a..a53417deef 100644 --- a/services/web/frontend/js/features/source-editor/components/editor-switch-legacy.tsx +++ b/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 ( diff --git a/services/web/frontend/js/features/source-editor/components/editor-switch.tsx b/services/web/frontend/js/features/source-editor/components/editor-switch.tsx index 9f1a6f828c..73f0dcbf1c 100644 --- a/services/web/frontend/js/features/source-editor/components/editor-switch.tsx +++ b/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 ( diff --git a/services/web/frontend/js/features/source-editor/components/grammarly-warning.tsx b/services/web/frontend/js/features/source-editor/components/grammarly-warning.tsx index cd837aa513..c6bb400d40 100644 --- a/services/web/frontend/js/features/source-editor/components/grammarly-warning.tsx +++ b/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 @@ -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) diff --git a/services/web/frontend/js/features/source-editor/extensions/realtime.ts b/services/web/frontend/js/features/source-editor/extensions/realtime.ts index 8d5be212c2..548316a764 100644 --- a/services/web/frontend/js/features/source-editor/extensions/realtime.ts +++ b/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 diff --git a/services/web/frontend/js/ide.js b/services/web/frontend/js/ide.js index f410e26394..3358f12d0e 100644 --- a/services/web/frontend/js/ide.js +++ b/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' diff --git a/services/web/frontend/js/ide/editor/Document.js b/services/web/frontend/js/ide/editor/Document.js index e2f6555ce2..acf89abe9a 100644 --- a/services/web/frontend/js/ide/editor/Document.js +++ b/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) { diff --git a/services/web/frontend/js/ide/editor/EditorManager.js b/services/web/frontend/js/ide/editor/EditorManager.js index 61c47906c0..946b45512f 100644 --- a/services/web/frontend/js/ide/editor/EditorManager.js +++ b/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' diff --git a/services/web/frontend/js/ide/editor/ShareJsDoc.js b/services/web/frontend/js/ide/editor/ShareJsDoc.js index 6de8a00bed..fd8691d92b 100644 --- a/services/web/frontend/js/ide/editor/ShareJsDoc.js +++ b/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) diff --git a/services/web/frontend/js/ide/editor/directives/formattingButtons.js b/services/web/frontend/js/ide/editor/directives/formattingButtons.js new file mode 100644 index 0000000000..0508f8280a --- /dev/null +++ b/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', +})) diff --git a/services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js b/services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js index a4533dad24..9c68717b86 100644 --- a/services/web/frontend/js/ide/review-panel/controllers/ReviewPanelController.js +++ b/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', () => diff --git a/services/web/frontend/js/ide/toolbar/EditorLoaderController.js b/services/web/frontend/js/ide/toolbar/EditorLoaderController.js new file mode 100644 index 0000000000..0bad5f847a --- /dev/null +++ b/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' + ) + }) +}) diff --git a/services/web/frontend/js/ide/toolbar/EditorToolbarController.js b/services/web/frontend/js/ide/toolbar/EditorToolbarController.js new file mode 100644 index 0000000000..892d6ffd5f --- /dev/null +++ b/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 +}) diff --git a/services/web/frontend/js/ide/toolbar/index.js b/services/web/frontend/js/ide/toolbar/index.js new file mode 100644 index 0000000000..ce97241d2f --- /dev/null +++ b/services/web/frontend/js/ide/toolbar/index.js @@ -0,0 +1,2 @@ +import './EditorLoaderController' +import './EditorToolbarController' diff --git a/services/web/frontend/js/vendor/libs/sharejs.js b/services/web/frontend/js/vendor/libs/sharejs.js index 082f71bf26..f6c9cf82ba 100644 --- a/services/web/frontend/js/vendor/libs/sharejs.js +++ b/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; }); diff --git a/services/web/frontend/stories/pdf-preview-messages.stories.js b/services/web/frontend/stories/pdf-preview-messages.stories.js index 267f65d3a8..a857e61a05 100644 --- a/services/web/frontend/stories/pdf-preview-messages.stories.js +++ b/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 (
diff --git a/services/web/frontend/stylesheets/app/editor.less b/services/web/frontend/stylesheets/app/editor.less index e3930c8fe8..1d0d4146fa 100644 --- a/services/web/frontend/stylesheets/app/editor.less +++ b/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; } diff --git a/services/web/frontend/stylesheets/app/editor/review-panel.less b/services/web/frontend/stylesheets/app/editor/review-panel.less index 1404dfda9b..516214f6ff 100644 --- a/services/web/frontend/stylesheets/app/editor/review-panel.less +++ b/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; diff --git a/services/web/frontend/stylesheets/app/editor/rich-text.less b/services/web/frontend/stylesheets/app/editor/rich-text.less deleted file mode 100644 index cae88bcbea..0000000000 --- a/services/web/frontend/stylesheets/app/editor/rich-text.less +++ /dev/null @@ -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; - } -} diff --git a/services/web/frontend/stylesheets/ieee-style.less b/services/web/frontend/stylesheets/ieee-style.less index a191e8a560..513fe2ac54 100644 --- a/services/web/frontend/stylesheets/ieee-style.less +++ b/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; diff --git a/services/web/frontend/stylesheets/light-style.less b/services/web/frontend/stylesheets/light-style.less index afb2e4cd24..1d89269421 100644 --- a/services/web/frontend/stylesheets/light-style.less +++ b/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; diff --git a/services/web/frontend/stylesheets/main-ieee-style.less b/services/web/frontend/stylesheets/main-ieee-style.less index 2e59e92231..26a18c44e3 100644 --- a/services/web/frontend/stylesheets/main-ieee-style.less +++ b/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; diff --git a/services/web/frontend/stylesheets/main-light-style.less b/services/web/frontend/stylesheets/main-light-style.less index c2bef13a83..d9db0bd688 100644 --- a/services/web/frontend/stylesheets/main-light-style.less +++ b/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; diff --git a/services/web/frontend/stylesheets/main-style.less b/services/web/frontend/stylesheets/main-style.less index 5ea245528f..d0a059daf3 100644 --- a/services/web/frontend/stylesheets/main-style.less +++ b/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 diff --git a/services/web/frontend/stylesheets/style.less b/services/web/frontend/stylesheets/style.less index 4b7c293bb8..1d6ac54712 100644 --- a/services/web/frontend/stylesheets/style.less +++ b/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 diff --git a/services/web/package.json b/services/web/package.json index c3c84a798d..c08f29bf0d 100644 --- a/services/web/package.json +++ b/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", diff --git a/services/web/test/frontend/features/source-editor/components/grammarly-warning.test.js b/services/web/test/frontend/features/source-editor/components/grammarly-warning.test.js index 69207b53f3..276408931f 100644 --- a/services/web/test/frontend/features/source-editor/components/grammarly-warning.test.js +++ b/services/web/test/frontend/features/source-editor/components/grammarly-warning.test.js @@ -104,27 +104,6 @@ describe('', function () { }) }) - it('does not show warning when user have rich text as their preference', async function () { - grammarlyStub.returns(true) - - renderWithEditorContext(, { - 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)