|
@@ -22,20 +22,20 @@ import { commandTooltip } from '../command-tooltip'
|
|
|
import { tableGeneratorTheme } from './table-generator'
|
|
import { tableGeneratorTheme } from './table-generator'
|
|
|
import { debugConsole } from '@/utils/debugging'
|
|
import { debugConsole } from '@/utils/debugging'
|
|
|
import { PreviewPath } from '../../../../../../types/preview-path'
|
|
import { PreviewPath } from '../../../../../../types/preview-path'
|
|
|
|
|
+import { getFileExtension } from '../../utils/file'
|
|
|
|
|
|
|
|
type Options = {
|
|
type Options = {
|
|
|
visual: boolean
|
|
visual: boolean
|
|
|
previewByPath: (path: string) => PreviewPath | null
|
|
previewByPath: (path: string) => PreviewPath | null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Language-specific visual editor extensions provided by modules (e.g. the
|
|
|
|
|
-// markdown visual editor). They live in the visual bundle so they are only
|
|
|
|
|
-// active in visual mode and react to switching editor modes.
|
|
|
|
|
-const visualEditorExtensions: Array<(options: Options) => Extension> =
|
|
|
|
|
- importOverleafModules('sourceEditorMarkdownExtensions').map(
|
|
|
|
|
- (item: { import: { extension: (options: Options) => Extension } }) =>
|
|
|
|
|
- item.import.extension
|
|
|
|
|
- )
|
|
|
|
|
|
|
+// Module-provided visual editors registered via the
|
|
|
|
|
+// `sourceEditorVisualExtensions` hook. Module exposes `getExtensions(ext)`,
|
|
|
|
|
+// returning the visual-mode extensions for that file extension. A module match takes precedence
|
|
|
|
|
+// over the LaTeX fallback
|
|
|
|
|
+const moduleVisualExtensionProviders: Array<{
|
|
|
|
|
+ import: { getExtensions: (ext: string) => Extension }
|
|
|
|
|
+}> = importOverleafModules('sourceEditorVisualExtensions')
|
|
|
|
|
|
|
|
const visualConf = new Compartment()
|
|
const visualConf = new Compartment()
|
|
|
|
|
|
|
@@ -56,12 +56,16 @@ const visualState = StateField.define<boolean>({
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const configureVisualExtensions = (options: Options) =>
|
|
const configureVisualExtensions = (options: Options) =>
|
|
|
- options.visual ? extension(options) : []
|
|
|
|
|
|
|
+ options.visual ? sharedVisualExtensions() : []
|
|
|
|
|
+
|
|
|
|
|
+export const visual = (docName: string, options: Options): Extension => {
|
|
|
|
|
+ const extensions =
|
|
|
|
|
+ visualModuleExtensions(docName) ?? latexVisualExtensions(options)
|
|
|
|
|
|
|
|
-export const visual = (options: Options): Extension => {
|
|
|
|
|
return [
|
|
return [
|
|
|
visualState.init(() => options.visual),
|
|
visualState.init(() => options.visual),
|
|
|
visualConf.of(configureVisualExtensions(options)),
|
|
visualConf.of(configureVisualExtensions(options)),
|
|
|
|
|
+ visualOnly(options.visual, extensions),
|
|
|
]
|
|
]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -78,9 +82,17 @@ export const setVisual = (options: Options): TransactionSpec => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-export const sourceOnly = (visual: boolean, extension: Extension) => {
|
|
|
|
|
|
|
+// Loads `extension` only while the editor is in a particular mode, reacting to
|
|
|
|
|
+// mode switches via `toggleVisualEffect`. `activeWhenVisual` selects which mode:
|
|
|
|
|
+// `true` for visual-only, `false` for source-only.
|
|
|
|
|
+const modeOnly = (
|
|
|
|
|
+ activeWhenVisual: boolean,
|
|
|
|
|
+ visual: boolean,
|
|
|
|
|
+ extension: Extension
|
|
|
|
|
+) => {
|
|
|
const conf = new Compartment()
|
|
const conf = new Compartment()
|
|
|
- const configure = (visual: boolean) => (visual ? [] : extension)
|
|
|
|
|
|
|
+ const configure = (visual: boolean) =>
|
|
|
|
|
+ visual === activeWhenVisual ? extension : []
|
|
|
return [
|
|
return [
|
|
|
conf.of(configure(visual)),
|
|
conf.of(configure(visual)),
|
|
|
|
|
|
|
@@ -98,6 +110,12 @@ export const sourceOnly = (visual: boolean, extension: Extension) => {
|
|
|
]
|
|
]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+export const visualOnly = (visual: boolean, extension: Extension) =>
|
|
|
|
|
+ modeOnly(true, visual, extension)
|
|
|
|
|
+
|
|
|
|
|
+export const sourceOnly = (visual: boolean, extension: Extension) =>
|
|
|
|
|
+ modeOnly(false, visual, extension)
|
|
|
|
|
+
|
|
|
const parsedAttributesConf = new Compartment()
|
|
const parsedAttributesConf = new Compartment()
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -175,19 +193,39 @@ const scrollJumpAdjuster = EditorState.transactionExtender.of(tr => {
|
|
|
return {}
|
|
return {}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const extension = (options: Options) => [
|
|
|
|
|
|
|
+const sharedVisualExtensions = () => [
|
|
|
visualTheme,
|
|
visualTheme,
|
|
|
visualHighlightStyle,
|
|
visualHighlightStyle,
|
|
|
mousedown,
|
|
mousedown,
|
|
|
|
|
+ scrollJumpAdjuster,
|
|
|
|
|
+ showContentWhenParsed,
|
|
|
|
|
+ EditorView.contentAttributes.of({ 'aria-label': 'Visual Editor editing' }),
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+const latexVisualExtensions = (options: Options): Extension => [
|
|
|
listItemMarker,
|
|
listItemMarker,
|
|
|
atomicDecorations(options),
|
|
atomicDecorations(options),
|
|
|
- visualEditorExtensions.map(extension => extension(options)),
|
|
|
|
|
markDecorations, // NOTE: must be after atomicDecorations, so that mark decorations wrap inline widgets
|
|
markDecorations, // NOTE: must be after atomicDecorations, so that mark decorations wrap inline widgets
|
|
|
visualKeymap,
|
|
visualKeymap,
|
|
|
commandTooltip,
|
|
commandTooltip,
|
|
|
- scrollJumpAdjuster,
|
|
|
|
|
- showContentWhenParsed,
|
|
|
|
|
pasteHtml,
|
|
pasteHtml,
|
|
|
tableGeneratorTheme,
|
|
tableGeneratorTheme,
|
|
|
- EditorView.contentAttributes.of({ 'aria-label': 'Visual Editor editing' }),
|
|
|
|
|
]
|
|
]
|
|
|
|
|
+
|
|
|
|
|
+// Returns the visual-mode extensions provided by a module for the active document
|
|
|
|
|
+const visualModuleExtensions = (docName: string): Extension | null => {
|
|
|
|
|
+ const fileExt = getFileExtension(docName)
|
|
|
|
|
+ if (!fileExt) {
|
|
|
|
|
+ return null
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for (const provider of moduleVisualExtensionProviders) {
|
|
|
|
|
+ const extensions = provider.import.getExtensions(fileExt)
|
|
|
|
|
+ if (Array.isArray(extensions) && extensions.length === 0) {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ return extensions
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return null
|
|
|
|
|
+}
|