Parcourir la source

[web] add BibTeX visual editor module (#30726)

* [web] add BibTeX visual editor module

* Make the visual editor hook open to extension

Instead of looking specifically for the bibtex visual editor, allow any
extension to provide a visual editor.

* Fix stylelint error

---------

Co-authored-by: Eric Mc Sween <5454374+emcsween@users.noreply.github.com>
GitOrigin-RevId: c85c27a2b119c826e3d00cbd475a2a21f1508091
Domagoj Kriskovic il y a 7 mois
Parent
commit
4e2ea44b61

+ 1 - 0
services/web/app/src/Features/Project/ProjectController.mjs

@@ -432,6 +432,7 @@ const _ProjectController = {
     }
 
     const splitTests = [
+      'bibtex-visual-editor',
       'compile-log-events',
       'visual-preview',
       'external-socket-heartbeat',

+ 1 - 0
services/web/config/settings.defaults.js

@@ -1015,6 +1015,7 @@ module.exports = {
     ssoCertificateInfo: [],
     v1ImportDataScreen: [],
     snapshotUtils: [],
+    visualEditorProviders: [],
     usGovBanner: [],
     rollingBuildsUpdatedAlert: [],
     offlineModeToolbarButtons: [],

+ 12 - 0
services/web/frontend/js/features/source-editor/components/codemirror-editor.tsx

@@ -17,9 +17,12 @@ import {
   CodeMirrorViewContext,
 } from './codemirror-context'
 import MathPreviewTooltip from './math-preview-tooltip'
+import { getVisualEditorComponent } from '../utils/visual-editor'
 import { useToolbarMenuBarEditorCommands } from '@/features/ide-redesign/hooks/use-toolbar-menu-editor-commands'
 import { useProjectContext } from '@/shared/context/project-context'
 import { useFeatureFlag } from '@/shared/context/split-test-context'
+import { useEditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context'
+import { useEditorPropertiesContext } from '@/features/ide-react/context/editor-properties-context'
 
 // TODO: remove this when definitely no longer used
 export * from './codemirror-context'
@@ -69,6 +72,13 @@ function CodeMirrorEditor() {
 function CodeMirrorEditorComponents() {
   useToolbarMenuBarEditorCommands()
   const { features } = useProjectContext()
+  const { openDocName } = useEditorOpenDocContext()
+  const { showVisual } = useEditorPropertiesContext()
+
+  const VisualEditor =
+    showVisual && openDocName != null
+      ? getVisualEditorComponent(openDocName)
+      : null
 
   return (
     <ReviewPanelProviders>
@@ -88,6 +98,8 @@ function CodeMirrorEditorComponents() {
           <Component key={path} />
         )
       )}
+
+      {VisualEditor && <VisualEditor />}
     </ReviewPanelProviders>
   )
 }

+ 4 - 2
services/web/frontend/js/features/source-editor/components/editor-switch.tsx

@@ -1,10 +1,10 @@
 import { ChangeEvent, FC, memo, useCallback } from 'react'
 import OLTooltip from '@/shared/components/ol/ol-tooltip'
 import { sendMB } from '../../../infrastructure/event-tracking'
-import { isValidTeXFile } from '../../../main/is-valid-tex-file'
 import { useTranslation } from 'react-i18next'
 import { useEditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context'
 import { useEditorPropertiesContext } from '@/features/ide-react/context/editor-properties-context'
+import { isVisualEditorAvailable } from '../utils/visual-editor'
 
 function EditorSwitch() {
   const { t } = useTranslation()
@@ -12,7 +12,9 @@ function EditorSwitch() {
     useEditorPropertiesContext()
   const { openDocName } = useEditorOpenDocContext()
 
-  const richTextAvailable = openDocName ? isValidTeXFile(openDocName) : false
+  const richTextAvailable = openDocName
+    ? isVisualEditorAvailable(openDocName)
+    : false
 
   const handleChange = useCallback(
     (event: ChangeEvent<HTMLInputElement>) => {

+ 3 - 2
services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts

@@ -37,7 +37,6 @@ import { setVisual } from '../extensions/visual/visual'
 import { useFileTreePathContext } from '@/features/file-tree/contexts/file-tree-path'
 import { useUserSettingsContext } from '@/shared/context/user-settings-context'
 import { setDocName } from '@/features/source-editor/extensions/doc-name'
-import { isValidTeXFile } from '@/main/is-valid-tex-file'
 import { captureException } from '@/infrastructure/error-reporter'
 import grammarlyExtensionPresent from '@/shared/utils/grammarly'
 import { debugConsole } from '@/utils/debugging'
@@ -62,6 +61,7 @@ import { beforeChangeDocEffect } from '@/features/source-editor/extensions/befor
 import { useActiveOverallTheme } from '@/shared/hooks/use-active-overall-theme'
 import { useEditorSelectionContext } from '@/shared/context/editor-selection-context'
 import { useActiveEditorTheme } from '@/shared/hooks/use-active-editor-theme'
+import { isVisualEditorAvailable } from '../utils/visual-editor'
 
 function useCodeMirrorScope(view: EditorView) {
   const { fileTreeData } = useFileTreeData()
@@ -273,7 +273,8 @@ function useCodeMirrorScope(view: EditorView) {
 
   const { previewByPath } = useFileTreePathContext()
 
-  const showVisual = visual && !!openDocName && isValidTeXFile(openDocName)
+  const showVisual =
+    visual && !!openDocName && isVisualEditorAvailable(openDocName)
 
   const visualRef = useRef({
     previewByPath,

+ 29 - 0
services/web/frontend/js/features/source-editor/utils/visual-editor.ts

@@ -0,0 +1,29 @@
+import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
+import { isValidTeXFile } from '../../../main/is-valid-tex-file'
+
+const visualEditorProviders = importOverleafModules('visualEditorProviders')
+
+export function isVisualEditorAvailable(filename: string): boolean {
+  // Core LaTeX visual editor
+  if (isValidTeXFile(filename)) {
+    return true
+  }
+
+  // Visual editors provided by modules
+  for (const provider of visualEditorProviders) {
+    if (provider.import.isVisualEditorAvailable(filename)) {
+      return true
+    }
+  }
+  return false
+}
+
+export function getVisualEditorComponent(filename: string) {
+  for (const provider of visualEditorProviders) {
+    const component = provider.import.getVisualEditorComponent(filename)
+    if (component != null) {
+      return component
+    }
+  }
+  return null
+}