aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/formattedText
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-01-30 00:40:43 -0500
committerbobzel <zzzman@gmail.com>2024-01-30 00:40:43 -0500
commit8ac814bbb81b690a6a10f5a07aa5ce0e8cafe283 (patch)
treeffb2b2b275e14aeeb8436effbd8aaae7cdf1e7fb /src/client/views/nodes/formattedText
parent1a32884f5084d9c39190e44bd9331e94590322e5 (diff)
changed dropConverter to keep title of dropped Doc. added paintFunc node/ checkbox view to formatted text. changed paintFunc to be computed based on layouytfieldkey being text in a freeformview. changed some inputRules to apply to code blocks. changed : contextmenu to allow regular note to be created. changed experimental tools to be user tmeplate tools. fixed focus on search bar when opening context menu
Diffstat (limited to 'src/client/views/nodes/formattedText')
-rw-r--r--src/client/views/nodes/formattedText/DashDocCommentView.tsx39
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx7
-rw-r--r--src/client/views/nodes/formattedText/PaintButtonView.tsx113
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts164
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts12
5 files changed, 271 insertions, 64 deletions
diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
index b7d2a24c2..a72ed1813 100644
--- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx
@@ -3,6 +3,8 @@ import * as ReactDOM from 'react-dom/client';
import { Doc } from '../../../../fields/Doc';
import { DocServer } from '../../../DocServer';
import * as React from 'react';
+import { IReactionDisposer, computed, reaction } from 'mobx';
+import { NumCast } from '../../../../fields/Types';
// creates an inline comment in a note when '>>' is typed.
// the comment sits on the right side of the note and vertically aligns with its anchor in the text.
@@ -10,8 +12,10 @@ import * as React from 'react';
export class DashDocCommentView {
dom: HTMLDivElement; // container for label and value
root: any;
+ node: any;
constructor(node: any, view: any, getPos: any) {
+ this.node = node;
this.dom = document.createElement('div');
this.dom.style.width = node.attrs.width;
this.dom.style.height = node.attrs.height;
@@ -32,10 +36,14 @@ export class DashDocCommentView {
};
this.root = ReactDOM.createRoot(this.dom);
- this.root.render(<DashDocCommentViewInternal view={view} getPos={getPos} docId={node.attrs.docId} />);
+ this.root.render(<DashDocCommentViewInternal view={view} getPos={getPos} setHeight={this.setHeight} docId={node.attrs.docId} />);
(this as any).dom = this.dom;
}
+ setHeight = (hgt: number) => {
+ !this.node.attrs.reflow && DocServer.GetRefField(this.node.attrs.docId).then(doc => doc instanceof Doc && (this.dom.style.height = hgt + ''));
+ };
+
destroy() {
this.root.unmount();
}
@@ -51,9 +59,15 @@ interface IDashDocCommentViewInternal {
docId: string;
view: any;
getPos: any;
+ setHeight: (height: number) => void;
}
export class DashDocCommentViewInternal extends React.Component<IDashDocCommentViewInternal> {
+ _reactionDisposer: IReactionDisposer | undefined;
+
+ @computed get _dashDoc() {
+ return DocServer.GetRefField(this.props.docId);
+ }
constructor(props: any) {
super(props);
this.onPointerLeaveCollapsed = this.onPointerLeaveCollapsed.bind(this);
@@ -61,15 +75,32 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
this.onPointerUpCollapsed = this.onPointerUpCollapsed.bind(this);
this.onPointerDownCollapsed = this.onPointerDownCollapsed.bind(this);
}
+ componentDidMount(): void {
+ this._reactionDisposer?.();
+ this._dashDoc.then(
+ doc =>
+ doc instanceof Doc &&
+ (this._reactionDisposer = reaction(
+ () => NumCast((doc as Doc)._height),
+ hgt => this.props.setHeight(hgt),
+ {
+ fireImmediately: true,
+ }
+ ))
+ );
+ }
+ componentWillUnmount(): void {
+ this._reactionDisposer?.();
+ }
onPointerLeaveCollapsed(e: any) {
- DocServer.GetRefField(this.props.docId).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowUnhighlight());
+ this._dashDoc.then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowUnhighlight());
e.preventDefault();
e.stopPropagation();
}
onPointerEnterCollapsed(e: any) {
- DocServer.GetRefField(this.props.docId).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc, false));
+ this._dashDoc.then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc, false));
e.preventDefault();
e.stopPropagation();
}
@@ -82,7 +113,7 @@ export class DashDocCommentViewInternal extends React.Component<IDashDocCommentV
const tr = this.props.view.state.tr.setNodeMarkup(target.pos, undefined, { ...target.node.attrs, hidden: target.node.attrs.hidden ? false : true });
this.props.view.dispatch(tr.setSelection(TextSelection.create(tr.doc, this.props.getPos() + (expand ? 2 : 1)))); // update the attrs
setTimeout(() => {
- expand && DocServer.GetRefField(this.props.docId).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
+ expand && this._dashDoc.then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc));
try {
this.props.view.dispatch(this.props.view.state.tr.setSelection(TextSelection.create(this.props.view.state.tr.doc, this.props.getPos() + (expand ? 2 : 1))));
} catch (e) {}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 0b857794b..973f90501 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -68,6 +68,7 @@ import { RichTextRules } from './RichTextRules';
import { schema } from './schema_rts';
import { SummaryView } from './SummaryView';
import { CollectionView } from '../../collections/CollectionView';
+import { PaintButtonView } from './PaintButtonView';
// import * as applyDevTools from 'prosemirror-dev-tools';
@observer
export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
@@ -1410,6 +1411,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
dashField(node: any, view: any, getPos: any) {
return new DashFieldView(node, view, getPos, self);
},
+ paintButton(node: any, view: any, getPos: any) {
+ return new PaintButtonView(node, view, getPos, self);
+ },
equation(node: any, view: any, getPos: any) {
return new EquationView(node, view, getPos, self);
},
@@ -1538,15 +1542,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this._downX = e.clientX;
this._downY = e.clientY;
this._downTime = Date.now();
- this._hadDownFocus = this.ProseRef?.children[0].className.includes('focused') ?? false;
FormattedTextBoxComment.textBox = this;
if (e.button === 0 && this._props.rootSelected?.() && !e.altKey && !e.ctrlKey && !e.metaKey) {
if (e.clientX < this.ProseRef!.getBoundingClientRect().right) {
// stop propagation if not in sidebar, otherwise nested boxes will lose focus to outer boxes.
e.stopPropagation(); // if the text box's content is active, then it consumes all down events
document.addEventListener('pointerup', this.onSelectEnd);
+ (this.ProseRef?.children?.[0] as any).focus();
}
}
+ this._hadDownFocus = this.ProseRef?.children[0].className.includes('focused') ?? false;
if (e.button === 2 || (e.button === 0 && e.ctrlKey)) {
e.preventDefault();
}
diff --git a/src/client/views/nodes/formattedText/PaintButtonView.tsx b/src/client/views/nodes/formattedText/PaintButtonView.tsx
new file mode 100644
index 000000000..74423c772
--- /dev/null
+++ b/src/client/views/nodes/formattedText/PaintButtonView.tsx
@@ -0,0 +1,113 @@
+import { action, computed, IReactionDisposer, makeObservable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import * as ReactDOM from 'react-dom/client';
+import { Doc } from '../../../../fields/Doc';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+import './DashFieldView.scss';
+import { FormattedTextBox } from './FormattedTextBox';
+import { CollectionViewType } from '../../../documents/DocumentTypes';
+import { CollectionView } from '../../collections/CollectionView';
+import { StrCast } from '../../../../fields/Types';
+
+export class PaintButtonView {
+ dom: HTMLDivElement; // container for label and value
+ root: any;
+ node: any;
+ tbox: FormattedTextBox;
+
+ constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
+ this.node = node;
+ this.tbox = tbox;
+ this.dom = document.createElement('div');
+ this.dom.style.width = node.attrs.width;
+ this.dom.style.height = node.attrs.height;
+ this.dom.style.position = 'relative';
+ this.dom.style.display = 'inline-block';
+ this.dom.onkeypress = function (e: any) {
+ e.stopPropagation();
+ };
+ this.dom.onkeydown = function (e: any) {
+ e.stopPropagation();
+ };
+ this.dom.onkeyup = function (e: any) {
+ e.stopPropagation();
+ };
+ this.dom.onmousedown = function (e: any) {
+ e.stopPropagation();
+ };
+
+ this.root = ReactDOM.createRoot(this.dom);
+ this.root.render(<PaintButtonViewInternal node={node} getPos={getPos} width={node.attrs.width} height={node.attrs.height} tbox={tbox} />);
+ }
+ destroy() {
+ setTimeout(() => {
+ try {
+ this.root.unmount();
+ } catch {}
+ });
+ }
+ deselectNode() {
+ this.dom.classList.remove('ProseMirror-selectednode');
+ }
+ selectNode() {
+ this.dom.classList.add('ProseMirror-selectednode');
+ }
+}
+
+interface IPaintButtonViewInternal {
+ tbox: FormattedTextBox;
+ width: number;
+ height: number;
+ node: any;
+ getPos: any;
+}
+
+@observer
+export class PaintButtonViewInternal extends ObservableReactComponent<IPaintButtonViewInternal> {
+ _reactionDisposer: IReactionDisposer | undefined;
+ _textBoxDoc: Doc;
+
+ constructor(props: IPaintButtonViewInternal) {
+ super(props);
+ makeObservable(this);
+ this._textBoxDoc = this._props.tbox.Document;
+ }
+
+ return100 = () => 100;
+ @computed get _checked() {
+ return this._props.tbox.Document.onClick ? true : false;
+ }
+
+ onCheckClick = () => {
+ const textView = this._props.tbox.DocumentView?.();
+ if (textView) {
+ const paintedField = 'layout_' + this._props.tbox.fieldKey + 'Painted';
+ const layoutFieldKey = StrCast(textView.layoutDoc.layout_fieldKey, 'layout');
+ if (textView.layoutDoc.onClick) {
+ textView.layoutDoc[paintedField] = undefined;
+ textView.layoutDoc.onClick = undefined;
+ } else {
+ textView.layoutDoc.type_collection = CollectionViewType.Freeform;
+ textView.dataDoc[paintedField] = CollectionView.LayoutString(this._props.tbox.fieldKey);
+ textView.layoutDoc.layout_fieldKey = paintedField;
+ textView.setToggleDetail(layoutFieldKey.replace('layout_', '').replace('layout', ''));
+ textView.layoutDoc.layout_fieldKey = layoutFieldKey;
+ }
+ }
+ };
+
+ render() {
+ return (
+ <div
+ className="dashFieldView"
+ style={{
+ width: this._props.width,
+ height: this._props.height,
+ pointerEvents: this._props.tbox._props.isSelected() || this._props.tbox.isAnyChildContentActive?.() ? undefined : 'none',
+ }}>
+ <input type="checkbox" value="paint" checked={this._checked} onChange={e => this.onCheckClick()} />
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index be32a2c4a..8f7bc5282 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -4,8 +4,7 @@ import { Doc, StrListCast } from '../../../../fields/Doc';
import { DocData } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
-import { ComputedField } from '../../../../fields/ScriptField';
-import { NumCast } from '../../../../fields/Types';
+import { NumCast, StrCast } from '../../../../fields/Types';
import { Utils } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { Docs, DocUtils } from '../../../documents/Documents';
@@ -15,6 +14,7 @@ import { RichTextMenu } from './RichTextMenu';
import { schema } from './schema_rts';
import { CollectionView } from '../../collections/CollectionView';
import { CollectionViewType } from '../../../documents/DocumentTypes';
+import { ContextMenu } from '../../ContextMenu';
export class RichTextRules {
public Document: Doc;
@@ -69,23 +69,39 @@ export class RichTextRules {
// ``` create code block
textblockTypeInputRule(/^```$/, schema.nodes.code_block),
-
- new InputRule(new RegExp(/^\^@paint/), (state, match, start, end) => {
- const { dataDoc, layoutDoc, fieldKey } = this.TextBox;
- layoutDoc.type_collection = CollectionViewType.Freeform;
- dataDoc.layout_painted = CollectionView.LayoutString('painted');
- const layoutFieldKey = layoutDoc.layout_fieldKey;
- layoutDoc.layout_fieldKey = 'layout_painted';
- this.TextBox.DocumentView?.().setToggleDetail();
- layoutDoc.layout_fieldKey = layoutFieldKey;
- dataDoc.paintFunc = ComputedField.MakeFunction(`toJavascriptString(this['${fieldKey}']?.Text)`);
- const comment = '/* this is now a paint func */';
- const tr = state.tr
- .deleteRange(start, end)
- .insertText(comment)
- .insert(start + comment.length, schema.nodes.code_block.create());
- return tr.setSelection(new TextSelection(tr.doc.resolve(start + comment.length + 2)));
- }),
+ new InputRule(
+ new RegExp(/(^|\n)\^@paint/), // for code blocks '^' means the beginning of the block, not the line, so need to test for \n
+ (state, match, start, end) => {
+ const { dataDoc, layoutDoc, fieldKey } = this.TextBox;
+ layoutDoc.type_collection = CollectionViewType.Freeform;
+ const paintedField = 'layout_' + this.TextBox.fieldKey + 'Painted';
+ dataDoc[paintedField] = CollectionView.LayoutString(this.TextBox.fieldKey);
+ const layoutFieldKey = StrCast(layoutDoc.layout_fieldKey);
+ layoutDoc.layout_fieldKey = paintedField;
+ this.TextBox.DocumentView?.().setToggleDetail(layoutFieldKey.replace('layout_', '').replace('layout', ''));
+ layoutDoc.layout_fieldKey = layoutFieldKey;
+ const comment = '/* enable as paint function ';
+ const endComment = ' */\n';
+ const inCode = state.tr.selection.$anchor.node().type === schema.nodes.code_block;
+ if (inCode) {
+ const tr = state.tr
+ .deleteRange(start, end)
+ .insertText(comment)
+ .insert(start + comment.length, schema.nodes.paintButton.create())
+ .insertText(endComment);
+ return tr.setSelection(new TextSelection(tr.doc.resolve(start + comment.length + endComment.length + 1)));
+ } else {
+ const tr = state.tr
+ .deleteRange(start, end)
+ .insertText(comment)
+ .insert(start + comment.length, schema.nodes.paintButton.create())
+ .insertText(endComment)
+ .insert(start + comment.length + endComment.length + 1, schema.nodes.code_block.create());
+ return tr.setSelection(new TextSelection(tr.doc.resolve(start + comment.length + endComment.length + 3)));
+ }
+ },
+ { inCode: true }
+ ),
// %<font-size> set the font size
new InputRule(new RegExp(/%([0-9]+)\s$/), (state, match, start, end) => {
@@ -94,6 +110,32 @@ export class RichTextRules {
}),
//Create annotation to a field on the text document
+ new InputRule(new RegExp(/>::$/), (state, match, start, end) => {
+ const creator = (doc: Doc) => {
+ const textDoc = this.Document[DocData];
+ const numInlines = NumCast(textDoc.inlineTextCount);
+ textDoc.inlineTextCount = numInlines + 1;
+ const node = (state.doc.resolve(start) as any).nodeAfter;
+ const newNode = schema.nodes.dashComment.create({ docId: doc[Id], reflow: false });
+ const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: 'dashDoc', docId: doc[Id], float: 'right' });
+ const sm = state.storedMarks || undefined;
+ this.TextBox.EditorView?.dispatch(
+ node
+ ? this.TextBox.EditorView.state.tr
+ .insert(start, newNode)
+ .replaceRangeWith(start + 1, end + 2, dashDoc)
+ .insertText(' ', start + 2)
+ .setStoredMarks([...node.marks, ...(sm ? sm : [])])
+ : this.TextBox.EditorView.state.tr
+ );
+ };
+ DocUtils.addDocumentCreatorMenuItems(creator, creator, 200, 200);
+ const cm = ContextMenu.Instance;
+ cm.displayMenu(200, 200, undefined, true);
+
+ return null;
+ }),
+ //Create annotation to a field on the text document
new InputRule(new RegExp(/>>$/), (state, match, start, end) => {
const textDoc = this.Document[DocData];
const numInlines = NumCast(textDoc.inlineTextCount);
@@ -117,7 +159,7 @@ export class RichTextRules {
textDoc[inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text
textDoc[inlineFieldKey] = ''; // set a default value for the annotation
const node = (state.doc.resolve(start) as any).nodeAfter;
- const newNode = schema.nodes.dashComment.create({ docId: textDocInline[Id] });
+ const newNode = schema.nodes.dashComment.create({ docId: textDocInline[Id], reflow: true });
const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: 'dashDoc', docId: textDocInline[Id], float: 'right' });
const sm = state.storedMarks || undefined;
const replaced = node
@@ -265,49 +307,53 @@ export class RichTextRules {
// [[fieldKey]] => show field
// [[fieldKey=value]] => show field and also set its value
// [[fieldKey:docTitle]] => show field of doc
- new InputRule(new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), (state, match, start, end) => {
- const fieldKey = match[1];
- const docTitle = match[3]?.replace(':', '');
- const value = match[2]?.substring(1);
- const linkToDoc = (target: Doc) => {
- const rstate = this.TextBox.EditorView?.state;
- const selection = rstate?.selection.$from.pos;
- if (rstate) {
- this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
- }
+ new InputRule(
+ new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/),
+ (state, match, start, end) => {
+ const fieldKey = match[1];
+ const docTitle = match[3]?.replace(':', '');
+ const value = match[2]?.substring(1);
+ const linkToDoc = (target: Doc) => {
+ const rstate = this.TextBox.EditorView?.state;
+ const selection = rstate?.selection.$from.pos;
+ if (rstate) {
+ this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3))));
+ }
- DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { link_relationship: 'portal to:portal from' });
+ DocUtils.MakeLink(this.TextBox.getAnchor(true), target, { link_relationship: 'portal to:portal from' });
- const fstate = this.TextBox.EditorView?.state;
- if (fstate && selection) {
- this.TextBox.EditorView?.dispatch(fstate.tr.setSelection(new TextSelection(fstate.doc.resolve(selection))));
- }
- };
- const getTitledDoc = (docTitle: string) => {
- if (!DocServer.FindDocByTitle(docTitle)) {
- Doc.AddToMyPublished(Docs.Create.TextDocument('', { title: docTitle, _width: 400, _layout_autoHeight: true }));
- }
- const titledDoc = DocServer.FindDocByTitle(docTitle);
- return titledDoc ? Doc.BestEmbedding(titledDoc) : titledDoc;
- };
- if (!fieldKey) {
- if (docTitle) {
- const target = getTitledDoc(docTitle);
- if (target) {
- setTimeout(() => linkToDoc(target));
- return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
+ const fstate = this.TextBox.EditorView?.state;
+ if (fstate && selection) {
+ this.TextBox.EditorView?.dispatch(fstate.tr.setSelection(new TextSelection(fstate.doc.resolve(selection))));
+ }
+ };
+ const getTitledDoc = (docTitle: string) => {
+ if (!DocServer.FindDocByTitle(docTitle)) {
+ Doc.AddToMyPublished(Docs.Create.TextDocument('', { title: docTitle, _width: 400, _layout_autoHeight: true }));
+ }
+ const titledDoc = DocServer.FindDocByTitle(docTitle);
+ return titledDoc ? Doc.BestEmbedding(titledDoc) : titledDoc;
+ };
+ if (!fieldKey) {
+ if (docTitle) {
+ const target = getTitledDoc(docTitle);
+ if (target) {
+ setTimeout(() => linkToDoc(target));
+ return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
+ }
}
+ return state.tr;
}
- return state.tr;
- }
- if (value !== '' && value !== undefined) {
- const num = value.match(/^[0-9.]$/);
- this.Document[DocData][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value;
- }
- const target = getTitledDoc(docTitle);
- const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false });
- return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
- }),
+ if (value !== '' && value !== undefined) {
+ const num = value.match(/^[0-9.]$/);
+ this.Document[DocData][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value;
+ }
+ const target = getTitledDoc(docTitle);
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false });
+ return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
+ },
+ { inCode: true }
+ ),
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
// wiki:title
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index 31f001b11..4706a97fa 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -179,6 +179,7 @@ export const nodes: { [index: string]: NodeSpec } = {
dashComment: {
attrs: {
docId: { default: '' },
+ reflow: { default: true },
},
inline: true,
group: 'inline',
@@ -275,6 +276,17 @@ export const nodes: { [index: string]: NodeSpec } = {
},
},
+ paintButton: {
+ inline: true,
+ attrs: {},
+ group: 'inline',
+ draggable: false,
+ toDOM(node) {
+ const attrs = { style: `width: ${node.attrs.width}, height: ${node.attrs.height}` };
+ return ['div', { ...node.attrs, ...attrs }];
+ },
+ },
+
video: {
inline: true,
attrs: {