diff --git a/dev/docs/javascript-public-events.md b/dev/docs/javascript-public-events.md
index 95300ddd3..4f68daaeb 100644
--- a/dev/docs/javascript-public-events.md
+++ b/dev/docs/javascript-public-events.md
@@ -253,3 +253,69 @@ window.addEventListener('library-cm6::configure-theme', event => {
});
```
+
+### `library-cm6::pre-init`
+
+This event is called just before any CodeMirror instances are initialised so that the instance configuration can be viewed and altered before the instance is created.
+
+#### Event Data
+
+- `usage` - A string label to identify the usage type of the CodeMirror instance in BookStack.
+- `editorViewConfig` - A reference to the [EditorViewConfig](https://codemirror.net/docs/ref/#view.EditorViewConfig) that will be used to create the instance.
+- `libEditorView` - The CodeMirror [EditorView](https://codemirror.net/docs/ref/#view.EditorView) class object, provided for convenience.
+- `libEditorState` - The CodeMirror [EditorState](https://codemirror.net/docs/ref/#state.EditorState) class object, provided for convenience.
+- `libCompartment` - The CodeMirror [Compartment](https://codemirror.net/docs/ref/#state.Compartment) class object, provided for convenience.
+
+##### Example
+
+The below shows how you'd enable the built-in line wrapping extension for page content code blocks:
+
+
+Show Example
+
+```javascript
+window.addEventListener('library-cm6::pre-init', event => {
+ const detail = event.detail;
+ const config = detail.editorViewConfig;
+ const EditorView = detail.libEditorView;
+
+ if (detail.usage === 'content-code-block') {
+ config.extensions.push(EditorView.lineWrapping);
+ }
+});
+```
+
+
+### `library-cm6::post-init`
+
+This event is called just after any CodeMirror instances are initialised so that you're able to gain a reference to the CodeMirror EditorView instance.
+
+#### Event Data
+
+- `usage` - A string label to identify the usage type of the CodeMirror instance in BookStack.
+- `editorView` - A reference to the [EditorView](https://codemirror.net/docs/ref/#view.EditorView) instance that has been created.
+- `editorViewConfig` - A reference to the [EditorViewConfig](https://codemirror.net/docs/ref/#view.EditorViewConfig) that was used to create the instance.
+- `libEditorView` - The CodeMirror [EditorView](https://codemirror.net/docs/ref/#view.EditorView) class object, provided for convenience.
+- `libEditorState` - The CodeMirror [EditorState](https://codemirror.net/docs/ref/#state.EditorState) class object, provided for convenience.
+- `libCompartment` - The CodeMirror [Compartment](https://codemirror.net/docs/ref/#state.Compartment) class object, provided for convenience.
+
+##### Example
+
+The below shows how you'd prepend some default text to all content (page) code blocks.
+
+
+Show Example
+
+```javascript
+window.addEventListener('library-cm6::post-init', event => {
+ const detail = event.detail;
+ const editorView = detail.editorView;
+
+ if (detail.usage === 'content-code-block') {
+ editorView.dispatch({
+ changes: {from: 0, to: 0, insert: 'Copyright 2023\n\n'}
+ });
+ }
+});
+```
+
\ No newline at end of file
diff --git a/resources/js/code/index.mjs b/resources/js/code/index.mjs
index e51472dc4..ada4529db 100644
--- a/resources/js/code/index.mjs
+++ b/resources/js/code/index.mjs
@@ -55,7 +55,7 @@ function highlightElem(elem) {
const wrapper = document.createElement('div');
elem.parentNode.insertBefore(wrapper, elem);
- const ev = createView({
+ const ev = createView('content-code-block', {
parent: wrapper,
doc: content,
extensions: viewerExtensions(wrapper),
@@ -99,7 +99,7 @@ export function highlight() {
* @returns {SimpleEditorInterface}
*/
export function wysiwygView(cmContainer, shadowRoot, content, language) {
- const ev = createView({
+ const ev = createView('content-code-block', {
parent: cmContainer,
doc: content,
extensions: viewerExtensions(cmContainer),
@@ -125,16 +125,11 @@ export function popupEditor(elem, modeSuggestion) {
doc: content,
extensions: [
...editorExtensions(elem.parentElement),
- EditorView.updateListener.of(v => {
- if (v.docChanged) {
- // textArea.value = v.state.doc.toString();
- }
- }),
],
};
// Create editor, hide original input
- const editor = new SimpleEditorInterface(createView(config));
+ const editor = new SimpleEditorInterface(createView('code-editor', config));
editor.setMode(modeSuggestion, content);
elem.style.display = 'none';
@@ -163,7 +158,7 @@ export function inlineEditor(textArea, mode) {
};
// Create editor view, hide original input
- const ev = createView(config);
+ const ev = createView('code-input', config);
const editor = new SimpleEditorInterface(ev);
editor.setMode(mode, content);
textArea.style.display = 'none';
@@ -198,7 +193,7 @@ export function markdownEditor(elem, onChange, domEventHandlers, keyBindings) {
window.$events.emitPublic(elem, 'editor-markdown-cm6::pre-init', {editorViewConfig: config});
// Create editor view, hide original input
- const ev = createView(config);
+ const ev = createView('markdown-editor', config);
(new SimpleEditorInterface(ev)).setMode('markdown', '');
elem.style.display = 'none';
diff --git a/resources/js/code/views.js b/resources/js/code/views.js
index 12148ca09..5599c35dd 100644
--- a/resources/js/code/views.js
+++ b/resources/js/code/views.js
@@ -1,4 +1,4 @@
-import {Compartment} from '@codemirror/state';
+import {Compartment, EditorState} from '@codemirror/state';
import {EditorView} from '@codemirror/view';
import {getLanguageExtension} from './languages';
@@ -7,17 +7,31 @@ const viewLangCompartments = new WeakMap();
/**
* Create a new editor view.
*
+ * @param {String} usageType
* @param {{parent: Element, doc: String, extensions: Array}} config
* @returns {EditorView}
*/
-export function createView(config) {
+export function createView(usageType, config) {
const langCompartment = new Compartment();
config.extensions.push(langCompartment.of([]));
- const ev = new EditorView(config);
+ const commonEventData = {
+ usage: usageType,
+ editorViewConfig: config,
+ libEditorView: EditorView,
+ libEditorState: EditorState,
+ libCompartment: Compartment,
+ };
+ // Emit a pre-init public event so the user can tweak the config before instance creation
+ window.$events.emitPublic(config.parent, 'library-cm6::pre-init', commonEventData);
+
+ const ev = new EditorView(config);
viewLangCompartments.set(ev, langCompartment);
+ // Emit a post-init public event so the user can gain a reference to the EditorView
+ window.$events.emitPublic(config.parent, 'library-cm6::post-init', {editorView: ev, ...commonEventData});
+
return ev;
}