@gravity-ui/markdown-editor 13.18.0 → 13.18.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ ![Markdown Editor](https://github.com/user-attachments/assets/40be2902-a683-4167-bd8d-ffd8bff26e69)
2
+
1
3
  # @gravity-ui/markdown-editor · [![npm package](https://img.shields.io/npm/v/@gravity-ui/markdown-editor)](https://www.npmjs.com/package/@gravity-ui/markdown-editor) [![CI](https://img.shields.io/github/actions/workflow/status/gravity-ui/markdown-editor/ci.yml?branch=main&label=CI)](https://github.com/gravity-ui/markdown-editor/actions/workflows/ci.yml?query=branch:main) [![Release](https://img.shields.io/github/actions/workflow/status/gravity-ui/markdown-editor/release.yml?branch=main&label=Release)](https://github.com/gravity-ui/markdown-editor/actions/workflows/release.yml?query=branch:main) [![storybook](https://img.shields.io/badge/Storybook-deployed-ff4685)](https://preview.gravity-ui.com/md-editor/)
2
4
 
3
5
  ## Markdown wysiwyg and markup editor
@@ -56,7 +58,7 @@ Read more:
56
58
  - [How to add Latex extension](docs/how-to-connect-latex-extension.md)
57
59
  - [How to add Mermaid extension](docs/how-to-connect-mermaid-extension.md)
58
60
  - [How to write extension](docs/how-to-create-extension.md)
59
- - [How to add gpt extension](docs/how-to-connect-gpt-extensions.md)
61
+ - [How to add GPT extension](docs/how-to-connect-gpt-extensions.md)
60
62
 
61
63
 
62
64
  ### i18n
@@ -92,7 +92,10 @@ const clipboard = ({ textParser, mdParser, serializer, pasteFileHandler, }) => {
92
92
  const codeType = (0, code_1.isInsideCode)(view.state);
93
93
  if (codeType) {
94
94
  const schema = view.state.schema;
95
- view.dispatch((0, core_1.trackTransactionMetrics)(view.state.tr.replaceSelectionWith(schema.text(codeType === 'inline' ? data.trim() : data)), 'paste', { clipboardDataFormat: dataFormat, code: codeType }));
95
+ const insideCodeData = e.clipboardData.getData(utils_1.DataTransferType.Text);
96
+ view.dispatch((0, core_1.trackTransactionMetrics)(view.state.tr.replaceSelectionWith(schema.text(codeType === 'inline'
97
+ ? insideCodeData.trim()
98
+ : insideCodeData)), 'paste', { clipboardDataFormat: utils_1.DataTransferType.Text, code: codeType }));
96
99
  isPasteHandled = true;
97
100
  }
98
101
  else {
@@ -1,5 +1,4 @@
1
1
  import type { NodeType } from 'prosemirror-model';
2
2
  import type { Command } from 'prosemirror-state';
3
3
  export declare function toList(listType: NodeType): Command;
4
- export declare const liftIfCursorIsAtBeginningOfItem: Command;
5
4
  export declare const joinPrevList: Command;
@@ -1,9 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.joinPrevList = exports.liftIfCursorIsAtBeginningOfItem = exports.toList = void 0;
3
+ exports.joinPrevList = exports.toList = void 0;
4
4
  const prosemirror_schema_list_1 = require("prosemirror-schema-list");
5
5
  const join_1 = require("../../../commands/join");
6
- const selection_1 = require("../../../utils/selection");
7
6
  const utils_1 = require("./utils");
8
7
  function toList(listType) {
9
8
  return (state, dispatch) => {
@@ -18,18 +17,6 @@ function toList(listType) {
18
17
  };
19
18
  }
20
19
  exports.toList = toList;
21
- const liftIfCursorIsAtBeginningOfItem = (state, dispatch) => {
22
- const $cursor = (0, selection_1.get$CursorAtBlockStart)(state.selection);
23
- if (!$cursor)
24
- return false;
25
- const { schema } = state;
26
- const parentBlock = $cursor.node($cursor.depth - 1);
27
- if (parentBlock.firstChild === $cursor.parent && (0, utils_1.isListItemNode)(parentBlock)) {
28
- return (0, prosemirror_schema_list_1.liftListItem)((0, utils_1.liType)(schema))(state, dispatch);
29
- }
30
- return false;
31
- };
32
- exports.liftIfCursorIsAtBeginningOfItem = liftIfCursorIsAtBeginningOfItem;
33
20
  exports.joinPrevList = (0, join_1.joinPreviousBlock)({
34
21
  checkPrevNode: utils_1.isListNode,
35
22
  skipNode: utils_1.isListOrItemNode,
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Lists = exports.olType = exports.liType = exports.blType = exports.ListsAttr = exports.ListNode = void 0;
4
- const prosemirror_commands_1 = require("prosemirror-commands");
5
4
  const prosemirror_schema_list_1 = require("prosemirror-schema-list");
6
5
  const keymap_1 = require("../../../utils/keymap");
7
6
  const ListsSpecs_1 = require("./ListsSpecs");
@@ -29,7 +28,7 @@ const Lists = (builder, opts) => {
29
28
  }, builder.Priority.High);
30
29
  builder.addKeymap(({ schema }) => ({
31
30
  Enter: (0, prosemirror_schema_list_1.splitListItem)((0, ListsSpecs_1.liType)(schema)),
32
- Backspace: (0, prosemirror_commands_1.chainCommands)(commands_1.liftIfCursorIsAtBeginningOfItem, commands_1.joinPrevList),
31
+ Backspace: commands_1.joinPrevList,
33
32
  }), builder.Priority.Low);
34
33
  builder.use(inputrules_1.ListsInputRulesExtension, { bulletListInputRule: opts === null || opts === void 0 ? void 0 : opts.ulInputRules });
35
34
  builder.addPlugin(MergeListsPlugin_1.mergeListsPlugin);
@@ -21,11 +21,15 @@ const mergeListsPlugin = () => new prosemirror_state_1.Plugin({
21
21
  });
22
22
  exports.mergeListsPlugin = mergeListsPlugin;
23
23
  function mergeAdjacentNodesWithSameType(tr, nodes) {
24
- for (let i = nodes.length - 1; i > 0; i--) {
25
- const prev = nodes[i - 1];
26
- const next = nodes[i];
27
- if (prev.node.type === next.node.type && prev.pos + prev.node.nodeSize === next.pos) {
28
- tr.join(next.pos);
24
+ const posAfterMap = {};
25
+ for (const item of nodes) {
26
+ const posBefore = item.pos;
27
+ const posAfter = posBefore + item.node.nodeSize;
28
+ posAfterMap[posAfter] = item.node;
29
+ const nodeBefore = posAfterMap[posBefore];
30
+ if ((nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.type) === item.node.type) {
31
+ tr.join(tr.mapping.map(posBefore));
32
+ posAfterMap[posBefore] = undefined;
29
33
  }
30
34
  }
31
35
  }
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LoadingScreen = exports.cnGptDialogLoadingScreen = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const react_1 = tslib_1.__importDefault(require("react"));
6
- const classname_1 = require("@bem-react/classname");
7
6
  const uikit_1 = require("@gravity-ui/uikit");
7
+ const classname_1 = require("../../../../../classname");
8
8
  const loading_1 = require("../../../../../i18n/gpt/loading");
9
9
  const GPTLoading_1 = tslib_1.__importDefault(require("../../../../../icons/GPTLoading"));
10
10
  const IconRefuge_1 = require("../../IconRefuge/IconRefuge");
@@ -13,7 +13,7 @@ exports.cnGptDialogPresetList = (0, classname_1.cn)('gpt-dialog-preset-list');
13
13
  const PresetItem = ({ preset, onPresetClick, disablePromptPresets, hotKey }) => {
14
14
  (0, useGptHotKeys_1.useGptHotKeys)(hotKey, () => {
15
15
  onPresetClick(preset.data);
16
- }, { enableOnFormTags: true });
16
+ }, { enableOnFormTags: true, enableOnContentEditable: true });
17
17
  return (react_1.default.createElement(uikit_1.ActionTooltip, { title: preset.display, hotkey: hotKey },
18
18
  react_1.default.createElement(uikit_1.Button, { className: (0, exports.cnGptDialogPresetList)('preset'), view: "normal", size: "m", disabled: disablePromptPresets, onClick: () => onPresetClick(preset.data) }, preset.display)));
19
19
  };
@@ -150,10 +150,6 @@ class GptWidgetDecoView {
150
150
  }
151
151
  exports.GptWidgetDecoView = GptWidgetDecoView;
152
152
  function Widget({ markup, anchorRef, answerRender, promptPresets, disablePromptPresets, customPromptPlaceholder, disabledPromptPlaceholder, onCustomPromptApply, onApplyResult, onPromptPresetClick, onTryAgain, onLike, onDislike, onClose, onUpdate, container, gptAlertProps, }) {
153
- (0, react_1.useEffect)(() => {
154
- // rerender the popup
155
- window.dispatchEvent(new CustomEvent('scroll'));
156
- }, [anchorRef]);
157
153
  (0, react_use_1.useMount)(() => {
158
154
  if ((anchorRef === null || anchorRef === void 0 ? void 0 : anchorRef.current) && 'scrollIntoView' in anchorRef.current) {
159
155
  anchorRef.current.scrollIntoView({
@@ -166,6 +162,6 @@ function Widget({ markup, anchorRef, answerRender, promptPresets, disablePromptP
166
162
  onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(result);
167
163
  }, [onUpdate]);
168
164
  return (react_1.default.createElement(react_1.default.Fragment, null,
169
- react_1.default.createElement(uikit_1.Popup, { className: (0, exports.cnGptPopup)(), contentClassName: (0, exports.cnGptPopup)('content'), open: true, anchorRef: anchorRef, placement: constants_1.gptPopupPlacement, onOutsideClick: onClose, focusTrap: true, strategy: "absolute", container: container, onEscapeKeyDown: onClose },
165
+ react_1.default.createElement(uikit_1.Popup, { className: (0, exports.cnGptPopup)(), contentClassName: (0, exports.cnGptPopup)('content'), open: true, anchorRef: anchorRef, placement: constants_1.gptPopupPlacement, onOutsideClick: onClose, focusTrap: true, autoFocus: !onCustomPromptApply && true, strategy: "absolute", container: container, onEscapeKeyDown: onClose },
170
166
  react_1.default.createElement(GptDialog_1.GptDialog, { markup: markup, answerRender: answerRender, promptPresets: promptPresets, disablePromptPresets: disablePromptPresets, customPromptPlaceholder: customPromptPlaceholder, disabledPromptPlaceholder: disabledPromptPlaceholder, onApplyResult: onApplyResult, onCustomPromptApply: onCustomPromptApply, onPromptPresetClick: onPromptPresetClick, onTryAgain: onTryAgain, onLike: onLike, onDislike: onDislike, onClose: onClose, onUpdate: handleUpdate, gptAlertProps: gptAlertProps }))));
171
167
  }
@@ -32,6 +32,13 @@ const useGpt = ({ markup, promptPresets, onCustomPromptApply, onPromptPresetClic
32
32
  }
33
33
  finally {
34
34
  setLoading(false);
35
+ setTimeout(() => {
36
+ // hack for popup rerender
37
+ // When a lot of text is entered into the GPT popup,
38
+ // it expands and goes beyond the boundaries. However,
39
+ // the popup does not handle height changes.
40
+ window.dispatchEvent(new CustomEvent('scroll'));
41
+ });
35
42
  onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(result);
36
43
  }
37
44
  }, [onUpdate]);
@@ -51,9 +51,14 @@ const useNodeResizing = ({ width, height, onResize, ref, delay = RESIZE_DELAY, t
51
51
  const newHeight = (startHeight / startWidth) * newWidth;
52
52
  setCurrentWidth(newWidth);
53
53
  setCurrentHeight(newHeight);
54
+ // If neither width nor height are provided, the width is set automatically.
55
+ const shouldSetWidth = !initialWidth &&
56
+ initialWidth !== 0 &&
57
+ !(initialWidth === null && initialHeight === null);
58
+ const shouldSetHeight = !initialHeight && initialHeight !== 0;
54
59
  onResize === null || onResize === void 0 ? void 0 : onResize({
55
- width: !initialWidth && initialWidth !== 0 ? undefined : newWidth,
56
- height: !initialHeight && initialHeight !== 0 ? undefined : newHeight,
60
+ width: shouldSetWidth ? undefined : newWidth,
61
+ height: shouldSetHeight ? undefined : newHeight,
57
62
  });
58
63
  }
59
64
  });
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
4
  /** During build process, the current version will be injected here */
5
- exports.VERSION = typeof '13.18.0' !== 'undefined' ? '13.18.0' : 'unknown';
5
+ exports.VERSION = typeof '13.18.2' !== 'undefined' ? '13.18.2' : 'unknown';
@@ -89,7 +89,10 @@ export const clipboard = ({ textParser, mdParser, serializer, pasteFileHandler,
89
89
  const codeType = isInsideCode(view.state);
90
90
  if (codeType) {
91
91
  const schema = view.state.schema;
92
- view.dispatch(trackTransactionMetrics(view.state.tr.replaceSelectionWith(schema.text(codeType === 'inline' ? data.trim() : data)), 'paste', { clipboardDataFormat: dataFormat, code: codeType }));
92
+ const insideCodeData = e.clipboardData.getData(DataTransferType.Text);
93
+ view.dispatch(trackTransactionMetrics(view.state.tr.replaceSelectionWith(schema.text(codeType === 'inline'
94
+ ? insideCodeData.trim()
95
+ : insideCodeData)), 'paste', { clipboardDataFormat: DataTransferType.Text, code: codeType }));
93
96
  isPasteHandled = true;
94
97
  }
95
98
  else {
@@ -1,5 +1,4 @@
1
1
  import type { NodeType } from 'prosemirror-model';
2
2
  import type { Command } from 'prosemirror-state';
3
3
  export declare function toList(listType: NodeType): Command;
4
- export declare const liftIfCursorIsAtBeginningOfItem: Command;
5
4
  export declare const joinPrevList: Command;
@@ -1,7 +1,6 @@
1
- import { liftListItem, wrapInList } from 'prosemirror-schema-list';
1
+ import { wrapInList } from 'prosemirror-schema-list';
2
2
  import { joinPreviousBlock } from '../../../commands/join';
3
- import { get$CursorAtBlockStart } from '../../../utils/selection';
4
- import { findAnyParentList, isListItemNode, isListNode, isListOrItemNode, liType } from './utils';
3
+ import { findAnyParentList, isListNode, isListOrItemNode } from './utils';
5
4
  export function toList(listType) {
6
5
  return (state, dispatch) => {
7
6
  const parentList = findAnyParentList(state.schema)(state.selection);
@@ -14,17 +13,6 @@ export function toList(listType) {
14
13
  return wrapInList(listType)(state, dispatch);
15
14
  };
16
15
  }
17
- export const liftIfCursorIsAtBeginningOfItem = (state, dispatch) => {
18
- const $cursor = get$CursorAtBlockStart(state.selection);
19
- if (!$cursor)
20
- return false;
21
- const { schema } = state;
22
- const parentBlock = $cursor.node($cursor.depth - 1);
23
- if (parentBlock.firstChild === $cursor.parent && isListItemNode(parentBlock)) {
24
- return liftListItem(liType(schema))(state, dispatch);
25
- }
26
- return false;
27
- };
28
16
  export const joinPrevList = joinPreviousBlock({
29
17
  checkPrevNode: isListNode,
30
18
  skipNode: isListOrItemNode,
@@ -1,9 +1,8 @@
1
- import { chainCommands } from 'prosemirror-commands';
2
1
  import { liftListItem, sinkListItem, splitListItem } from 'prosemirror-schema-list';
3
2
  import { withLogAction } from '../../../utils/keymap';
4
3
  import { ListsSpecs, blType, liType, olType } from './ListsSpecs';
5
4
  import { actions } from './actions';
6
- import { joinPrevList, liftIfCursorIsAtBeginningOfItem, toList } from './commands';
5
+ import { joinPrevList, toList } from './commands';
7
6
  import { ListAction } from './const';
8
7
  import { ListsInputRulesExtension } from './inputrules';
9
8
  import { mergeListsPlugin } from './plugins/MergeListsPlugin';
@@ -21,7 +20,7 @@ export const Lists = (builder, opts) => {
21
20
  }, builder.Priority.High);
22
21
  builder.addKeymap(({ schema }) => ({
23
22
  Enter: splitListItem(liType(schema)),
24
- Backspace: chainCommands(liftIfCursorIsAtBeginningOfItem, joinPrevList),
23
+ Backspace: joinPrevList,
25
24
  }), builder.Priority.Low);
26
25
  builder.use(ListsInputRulesExtension, { bulletListInputRule: opts === null || opts === void 0 ? void 0 : opts.ulInputRules });
27
26
  builder.addPlugin(mergeListsPlugin);
@@ -17,11 +17,15 @@ export const mergeListsPlugin = () => new Plugin({
17
17
  },
18
18
  });
19
19
  function mergeAdjacentNodesWithSameType(tr, nodes) {
20
- for (let i = nodes.length - 1; i > 0; i--) {
21
- const prev = nodes[i - 1];
22
- const next = nodes[i];
23
- if (prev.node.type === next.node.type && prev.pos + prev.node.nodeSize === next.pos) {
24
- tr.join(next.pos);
20
+ const posAfterMap = {};
21
+ for (const item of nodes) {
22
+ const posBefore = item.pos;
23
+ const posAfter = posBefore + item.node.nodeSize;
24
+ posAfterMap[posAfter] = item.node;
25
+ const nodeBefore = posAfterMap[posBefore];
26
+ if ((nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.type) === item.node.type) {
27
+ tr.join(tr.mapping.map(posBefore));
28
+ posAfterMap[posBefore] = undefined;
25
29
  }
26
30
  }
27
31
  }
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { cn } from '@bem-react/classname';
3
2
  import { Skeleton } from '@gravity-ui/uikit';
3
+ import { cn } from '../../../../../classname';
4
4
  import { i18n } from '../../../../../i18n/gpt/loading';
5
5
  import GPTLoading from '../../../../../icons/GPTLoading';
6
6
  import { IconRefuge } from '../../IconRefuge/IconRefuge';
@@ -10,7 +10,7 @@ export const cnGptDialogPresetList = cn('gpt-dialog-preset-list');
10
10
  const PresetItem = ({ preset, onPresetClick, disablePromptPresets, hotKey }) => {
11
11
  useGptHotKeys(hotKey, () => {
12
12
  onPresetClick(preset.data);
13
- }, { enableOnFormTags: true });
13
+ }, { enableOnFormTags: true, enableOnContentEditable: true });
14
14
  return (React.createElement(ActionTooltip, { title: preset.display, hotkey: hotKey },
15
15
  React.createElement(Button, { className: cnGptDialogPresetList('preset'), view: "normal", size: "m", disabled: disablePromptPresets, onClick: () => onPresetClick(preset.data) }, preset.display)));
16
16
  };
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect } from 'react';
1
+ import React, { useCallback } from 'react';
2
2
  import { Popup } from '@gravity-ui/uikit';
3
3
  import { Slice } from 'prosemirror-model';
4
4
  import { TextSelection } from 'prosemirror-state';
@@ -146,10 +146,6 @@ export class GptWidgetDecoView {
146
146
  }
147
147
  }
148
148
  function Widget({ markup, anchorRef, answerRender, promptPresets, disablePromptPresets, customPromptPlaceholder, disabledPromptPlaceholder, onCustomPromptApply, onApplyResult, onPromptPresetClick, onTryAgain, onLike, onDislike, onClose, onUpdate, container, gptAlertProps, }) {
149
- useEffect(() => {
150
- // rerender the popup
151
- window.dispatchEvent(new CustomEvent('scroll'));
152
- }, [anchorRef]);
153
149
  useMount(() => {
154
150
  if ((anchorRef === null || anchorRef === void 0 ? void 0 : anchorRef.current) && 'scrollIntoView' in anchorRef.current) {
155
151
  anchorRef.current.scrollIntoView({
@@ -162,6 +158,6 @@ function Widget({ markup, anchorRef, answerRender, promptPresets, disablePromptP
162
158
  onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(result);
163
159
  }, [onUpdate]);
164
160
  return (React.createElement(React.Fragment, null,
165
- React.createElement(Popup, { className: cnGptPopup(), contentClassName: cnGptPopup('content'), open: true, anchorRef: anchorRef, placement: gptPopupPlacement, onOutsideClick: onClose, focusTrap: true, strategy: "absolute", container: container, onEscapeKeyDown: onClose },
161
+ React.createElement(Popup, { className: cnGptPopup(), contentClassName: cnGptPopup('content'), open: true, anchorRef: anchorRef, placement: gptPopupPlacement, onOutsideClick: onClose, focusTrap: true, autoFocus: !onCustomPromptApply && true, strategy: "absolute", container: container, onEscapeKeyDown: onClose },
166
162
  React.createElement(GptDialog, { markup: markup, answerRender: answerRender, promptPresets: promptPresets, disablePromptPresets: disablePromptPresets, customPromptPlaceholder: customPromptPlaceholder, disabledPromptPlaceholder: disabledPromptPlaceholder, onApplyResult: onApplyResult, onCustomPromptApply: onCustomPromptApply, onPromptPresetClick: onPromptPresetClick, onTryAgain: onTryAgain, onLike: onLike, onDislike: onDislike, onClose: onClose, onUpdate: handleUpdate, gptAlertProps: gptAlertProps }))));
167
163
  }
@@ -29,6 +29,13 @@ export const useGpt = ({ markup, promptPresets, onCustomPromptApply, onPromptPre
29
29
  }
30
30
  finally {
31
31
  setLoading(false);
32
+ setTimeout(() => {
33
+ // hack for popup rerender
34
+ // When a lot of text is entered into the GPT popup,
35
+ // it expands and goes beyond the boundaries. However,
36
+ // the popup does not handle height changes.
37
+ window.dispatchEvent(new CustomEvent('scroll'));
38
+ });
32
39
  onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(result);
33
40
  }
34
41
  }, [onUpdate]);
@@ -47,9 +47,14 @@ export const useNodeResizing = ({ width, height, onResize, ref, delay = RESIZE_D
47
47
  const newHeight = (startHeight / startWidth) * newWidth;
48
48
  setCurrentWidth(newWidth);
49
49
  setCurrentHeight(newHeight);
50
+ // If neither width nor height are provided, the width is set automatically.
51
+ const shouldSetWidth = !initialWidth &&
52
+ initialWidth !== 0 &&
53
+ !(initialWidth === null && initialHeight === null);
54
+ const shouldSetHeight = !initialHeight && initialHeight !== 0;
50
55
  onResize === null || onResize === void 0 ? void 0 : onResize({
51
- width: !initialWidth && initialWidth !== 0 ? undefined : newWidth,
52
- height: !initialHeight && initialHeight !== 0 ? undefined : newHeight,
56
+ width: shouldSetWidth ? undefined : newWidth,
57
+ height: shouldSetHeight ? undefined : newHeight,
53
58
  });
54
59
  }
55
60
  });
@@ -1,2 +1,2 @@
1
1
  /** During build process, the current version will be injected here */
2
- export const VERSION = typeof '13.18.0' !== 'undefined' ? '13.18.0' : 'unknown';
2
+ export const VERSION = typeof '13.18.2' !== 'undefined' ? '13.18.2' : 'unknown';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/markdown-editor",
3
- "version": "13.18.0",
3
+ "version": "13.18.2",
4
4
  "description": "Markdown wysiwyg and markup editor",
5
5
  "license": "MIT",
6
6
  "repository": {