Merge pull request #35898 from overleaf/dp-visual-markdown-editor-setting

[web] Track markdown editor mode separately and default to visual

GitOrigin-RevId: 7195189c683618b3e0c735f91193fb662078b558
This commit is contained in:
David
2026-09-17 11:39:45 +00:00
committed by Copybot
parent f0b9ab322f
commit b77187404b
4 changed files with 61 additions and 10 deletions
@@ -14,7 +14,10 @@ import getMeta from '@/utils/meta'
import { useUnstableStoreSync } from '@/shared/hooks/use-unstable-store-sync'
import { sendMB } from '@/infrastructure/event-tracking'
import { useEditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context'
import { getVisualEditorStorageKey } from '@/features/source-editor/utils/visual-editor'
import {
getVisualEditorDefault,
getVisualEditorStorageKey,
} from '@/features/source-editor/utils/visual-editor'
// Context value type
export type EditorPropertiesContextValue = {
@@ -60,7 +63,7 @@ export function showVisualForFile(filename: string): boolean {
if (key === 'editor.lastUsedMode') {
return migrateTexVisualMode()
}
return false
return getVisualEditorDefault(filename)
}
export const EditorPropertiesProvider: FC<PropsWithChildren> = ({
@@ -24,6 +24,7 @@ import { sendMB } from '@/infrastructure/event-tracking'
import { FileRef } from '../../../../../types/file-ref'
import { useLayoutContext } from '@/shared/context/layout-context'
import { isVisualEditorAvailable } from '@/features/source-editor/utils/visual-editor'
import { isMarkdownFile } from '@/features/source-editor/utils/file'
const FileTreeOpenContext = createContext<
| {
@@ -108,6 +109,12 @@ export const FileTreeOpenProvider: FC<React.PropsWithChildren> = ({
editorMode,
})
}
if (isMarkdownFile(selected.entity.name)) {
sendMB('open-md-file', {
projectOwner,
editorMode,
})
}
}
// Keep openFile scope value in sync with the file tree
const openFile =
@@ -48,6 +48,9 @@ export const filterFolders = filterByType('folder')
const IMAGE_FILE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'pdf', 'eps', 'svg']
// File extensions handled by the markdown visual editor.
export const MARKDOWN_FILE_EXTENSIONS = ['md', 'markdown']
export const getFileExtension = (filename: string): string | null => {
const parts = filename.split('.')
if (parts.length < 2) {
@@ -65,6 +68,11 @@ export const isSvgFile = (filename: string): boolean => {
return getFileExtension(filename) === 'svg'
}
export const isMarkdownFile = (filename: string): boolean => {
const extension = getFileExtension(filename)
return extension !== null && MARKDOWN_FILE_EXTENSIONS.includes(extension)
}
export function isImageFile(file: File) {
return hasImageExtension(file.name)
}
@@ -5,9 +5,43 @@ import { getFileExtension } from './file'
const visualEditorProviders = importOverleafModules('visualEditorProviders')
const cmVisualEditorProviders: Array<{
import: { getExtensions: (ext: string) => Extension }
import: {
getExtensions: (ext: string) => Extension
id?: string
defaultVisual?: boolean
}
}> = importOverleafModules('sourceEditorVisualExtensions')
/**
* Find the visual editor provider (if any) that claims a file, so that
* per-file-type metadata such as the storage key id and default mode can be
* read from it.
*/
function getVisualEditorProvider(
filename: string
): { id?: string; defaultVisual?: boolean } | null {
// Module-provided visual editors that render their own component (e.g. .bib)
for (const provider of visualEditorProviders) {
if (provider.import.isVisualEditorAvailable(filename)) {
return provider.import
}
}
// CodeMirror-extension based visual editors (e.g. markdown)
const extension = getFileExtension(filename)
if (extension !== null) {
for (const provider of cmVisualEditorProviders) {
const result = provider.import.getExtensions(extension)
const extensions = Array.isArray(result) ? result : [result]
if (extensions.length > 0) {
return provider.import
}
}
}
return null
}
/**
* This currently covers LaTeX and Markdown. Other file
* types (e.g. .bib) use module-provided visual editors that render their own
@@ -62,11 +96,10 @@ export function getVisualEditorComponent(filename: string) {
}
export function getVisualEditorStorageKey(filename: string): string {
for (const provider of visualEditorProviders) {
if (provider.import.isVisualEditorAvailable(filename)) {
const id = provider.import.id
return id != null ? `editor.lastUsedMode.${id}` : 'editor.lastUsedMode'
}
}
return 'editor.lastUsedMode'
const id = getVisualEditorProvider(filename)?.id
return id != null ? `editor.lastUsedMode.${id}` : 'editor.lastUsedMode'
}
export function getVisualEditorDefault(filename: string): boolean {
return getVisualEditorProvider(filename)?.defaultVisual ?? false
}