diff --git a/services/web/frontend/js/features/source-editor/components/codemirror-view.tsx b/services/web/frontend/js/features/source-editor/components/codemirror-view.tsx index 2e57a4e51a..666c2dd14f 100644 --- a/services/web/frontend/js/features/source-editor/components/codemirror-view.tsx +++ b/services/web/frontend/js/features/source-editor/components/codemirror-view.tsx @@ -1,10 +1,13 @@ import { memo, useCallback, useEffect } from 'react' import { useCodeMirrorViewContext } from './codemirror-editor' import useCodeMirrorScope from '../hooks/use-codemirror-scope' +import useScopeValueSetterOnly from '@/shared/hooks/use-scope-value-setter-only' function CodeMirrorView() { const view = useCodeMirrorViewContext() + const [, setView] = useScopeValueSetterOnly('editor.view') + // append the editor view dom to the container node when mounted const containerRef = useCallback( node => { @@ -22,6 +25,11 @@ function CodeMirrorView() { } }, [view]) + // add the editor view to the scope value store, so it can be accessed by external extensions + useEffect(() => { + setView(view) + }, [setView, view]) + useCodeMirrorScope(view) return
diff --git a/services/web/frontend/js/shared/context/ide-context.tsx b/services/web/frontend/js/shared/context/ide-context.tsx index 4ece0bf762..14b4b3db5e 100644 --- a/services/web/frontend/js/shared/context/ide-context.tsx +++ b/services/web/frontend/js/shared/context/ide-context.tsx @@ -1,4 +1,4 @@ -import { createContext, FC, useContext, useMemo } from 'react' +import { createContext, FC, useContext, useEffect, useMemo } from 'react' import { ScopeValueStore } from '../../../../types/ide/scope-value-store' import { Scope } from '../../../../types/angular/scope' import getMeta from '@/utils/meta' @@ -23,6 +23,25 @@ export const IdeProvider: FC<{ scopeStore: ScopeValueStore scopeEventEmitter: ScopeEventEmitter }> = ({ ide, scopeStore, scopeEventEmitter, children }) => { + /** + * Expose scopeStore via `window.overleaf.unstable.store`, so it can be accessed by external extensions. + * + * These properties are expected to be available: + * - `editor.view` + * - `project.spellcheckLanguage` + * - `editor.open_doc_name`, + * - `editor.open_doc_id`, + */ + useEffect(() => { + window.overleaf = { + ...window.overleaf, + unstable: { + ...window.overleaf?.unstable, + store: scopeStore, + }, + } + }, [scopeStore]) + const value = useMemo(() => { return { ...ide, diff --git a/services/web/types/window.ts b/services/web/types/window.ts index 033e15580c..adfc0a476f 100644 --- a/services/web/types/window.ts +++ b/services/web/types/window.ts @@ -4,6 +4,7 @@ import { OverallThemeMeta } from './project-settings' import { User } from './user' import 'recurly__recurly-js' import { UserSettings } from './user-settings' +import { ScopeValueStore } from './ide/scope-value-store' declare global { // eslint-disable-next-line no-unused-vars @@ -44,5 +45,10 @@ declare global { expectingLinkedFileRefreshedSocketFor?: string | null writefull?: any io?: any + overleaf: { + unstable: { + store: ScopeValueStore + } + } } }