aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/formattedText
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2025-06-26 10:53:54 -0400
committerbobzel <zzzman@gmail.com>2025-06-26 10:53:54 -0400
commitbaae27b205356898c5866a0f095e4ec056e02459 (patch)
tree1b62de5579b8de8be81b6d342a9767f0f379bb91 /src/client/views/nodes/formattedText
parentccfdf905400cd4b81d8cde0f16bb0e15cd65621b (diff)
parent0093370a04348ef38b91252d02ab850f25d753b2 (diff)
Merge branch 'master' into agent-paper-main
Diffstat (limited to 'src/client/views/nodes/formattedText')
-rw-r--r--src/client/views/nodes/formattedText/DailyJournal.tsx248
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.scss6
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx1
-rw-r--r--src/client/views/nodes/formattedText/EquationEditor.scss10
-rw-r--r--src/client/views/nodes/formattedText/EquationEditor.tsx4
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss54
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx117
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.scss16
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.scss4
-rw-r--r--src/client/views/nodes/formattedText/TooltipTextMenu.scss8
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts14
11 files changed, 345 insertions, 137 deletions
diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index 871c556e6..564609494 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -9,14 +9,22 @@ import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT';
import { RichTextField } from '../../../../fields/RichTextField';
import { Plugin } from 'prosemirror-state';
import { RTFCast } from '../../../../fields/Types';
+import { Mark } from 'prosemirror-model';
+import { observer } from 'mobx-react';
export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@observable journalDate: string;
- @observable typingTimeout: NodeJS.Timeout | null = null; // Track typing delay
- @observable lastUserText: string = ''; // Store last user-entered text
+ @observable typingTimeout: NodeJS.Timeout | null = null; // track typing delay
+ @observable lastUserText: string = ''; // store last user-entered text
+ @observable isLoadingPrompts: boolean = false; // track if prompts are loading
+ @observable showPromptMenu = false;
+ @observable inlinePromptsEnabled = true;
+ @observable askPromptsEnabled = true;
+
_ref = React.createRef<FormattedTextBox>(); // reference to the formatted textbox
predictiveTextRange: { from: number; to: number } | null = null; // where predictive text starts and ends
private predictiveText: string | null = ' ... why?';
+ private prePredictiveMarks: Mark[] = [];
public static LayoutString(fieldStr: string) {
return FieldView.LayoutString(DailyJournal, fieldStr);
@@ -40,7 +48,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
month: 'long',
day: 'numeric',
});
- console.log('getFormattedDate():', date);
+ // console.log('getFormattedDate():', date);
return date;
}
@@ -49,15 +57,15 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
*/
@action
setDailyTitle() {
- console.log('setDailyTitle() called...');
- console.log('Current title before update:', this.dataDoc.title);
+ // console.log('setDailyTitle() called...');
+ // console.log('Current title before update:', this.dataDoc.title);
if (!this.dataDoc.title || this.dataDoc.title !== this.journalDate) {
- console.log('Updating title to:', this.journalDate);
+ // console.log('Updating title to:', this.journalDate);
this.dataDoc.title = this.journalDate;
}
- console.log('New title after update:', this.dataDoc.title);
+ // console.log('New title after update:', this.dataDoc.title);
}
/**
@@ -68,7 +76,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
const placeholderText = 'Start writing here...';
const dateText = `${this.journalDate}\n`;
- console.log('Checking if dataDoc has text field...');
+ // console.log('Checking if dataDoc has text field...');
this.dataDoc[this.fieldKey] = RichTextField.textToRtfFormat(
[
@@ -80,7 +88,63 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
placeholderText.length
);
- console.log('Current text field:', this.dataDoc[this.fieldKey]);
+ // console.log('Current text field:', this.dataDoc[this.fieldKey]);
+ }
+
+ /**
+ * Method to show/hide the prompts menu
+ */
+ @action.bound togglePromptMenu() {
+ this.showPromptMenu = !this.showPromptMenu;
+ }
+
+ /**
+ * Method to toggle on/off inline predictive prompts
+ */
+ @action.bound toggleInlinePrompts() {
+ this.inlinePromptsEnabled = !this.inlinePromptsEnabled;
+ }
+
+ /**
+ * Method to toggle on/off inline /ask prompts
+ */
+ @action.bound toggleAskPrompts() {
+ this.askPromptsEnabled = !this.askPromptsEnabled;
+ }
+
+ /**
+ * Method to handle click on document (to close prompt menu)
+ * @param e - a click on the document
+ */
+ @action.bound
+ handleDocumentClick(e: MouseEvent) {
+ const menu = document.getElementById('prompts-menu');
+ const button = document.getElementById('prompts-button');
+ if (this.showPromptMenu && menu && !menu.contains(e.target as Node) && button && !button.contains(e.target as Node)) {
+ this.showPromptMenu = false;
+ }
+ }
+
+ /**
+ * Method to set initial date of document in the calendar view
+ */
+
+ @action setInitialDateRange() {
+ if (!this.dataDoc.$task_dateRange && this.journalDate) {
+ const parsedDate = new Date(this.journalDate);
+ if (!isNaN(parsedDate.getTime())) {
+ const localStart = new Date(parsedDate.getFullYear(), parsedDate.getMonth(), parsedDate.getDate());
+ const localEnd = new Date(localStart); // same day
+
+ this.dataDoc.$task_dateRange = `${localStart.toISOString()}|${localEnd.toISOString()}`;
+ this.dataDoc.$task_allDay = true;
+ this.dataDoc.$task = ''; // needed only to make the keyvalue view look good.
+
+ // console.log('Set task_dateRange and task_allDay on journal (from local date):', this.dataDoc.$task_dateRange);
+ } else {
+ // console.log('Could not parse journalDate:', this.journalDate);
+ }
+ }
}
/**
@@ -94,8 +158,26 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
if (this.typingTimeout) clearTimeout(this.typingTimeout);
- this.typingTimeout = setTimeout(() => {
+ const { state } = editorView;
+ const cursorPos = state.selection.from;
+
+ // characters before cursor
+ const triggerText = state.doc.textBetween(Math.max(0, cursorPos - 4), cursorPos);
+
+ if (triggerText === '/ask' && this.askPromptsEnabled) {
+ // remove /ask text
+ const tr = state.tr.delete(cursorPos - 4, cursorPos);
+ editorView.dispatch(tr);
+
+ // insert predicted question
this.insertPredictiveQuestion();
+ return;
+ }
+
+ this.typingTimeout = setTimeout(() => {
+ if (this.inlinePromptsEnabled) {
+ this.insertPredictiveQuestion();
+ }
}, 3500);
};
@@ -129,28 +211,38 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
// Only insert if we're at end of node, or there's a newline node after
if (!isAtEndOfParent && !hasNewlineAfter) return;
- const fontSizeMark = schema.marks.pFontSize.create({ fontSize: '14px' });
+ // Save current marks at cursor
+ const currentMarks = state.storedMarks || resolvedPos.marks();
+ this.prePredictiveMarks = [...currentMarks];
+
+ // color and italics are preset for predictive question, font and size are adaptive
const fontColorMark = schema.marks.pFontColor.create({ fontColor: 'lightgray' });
const fontItalicsMark = schema.marks.em.create();
+ const fontSizeMark = this.prePredictiveMarks.find(m => m.type.name === 'pFontSize');
+ const fontFamilyMark = this.prePredictiveMarks.find(m => m.type.name === 'pFontFamily'); // if applicable
- this.predictiveText = ' ...'; // placeholder for now
+ this.predictiveText = ' ...'; // placeholder
const fullTextUpToCursor = state.doc.textBetween(0, state.selection.to, '\n', '\n');
- const gptPrompt = `Given the following incomplete journal entry, generate a single 2-5 word question that continues the user's thought:\n\n"${fullTextUpToCursor}"`;
+ const gptPrompt = `Given the following incomplete journal entry, generate a single 2-5 word reflective question that continues the user's thought:\n\n"${fullTextUpToCursor}"`;
const res = await gptAPICall(gptPrompt, GPTCallType.COMPLETION);
if (!res) return;
// styled text node
const text = ` ... ${res.trim()}`;
- const predictedText = schema.text(text, [fontSizeMark, fontColorMark, fontItalicsMark]);
+ const predictedText = schema.text(text, [fontColorMark, fontItalicsMark, ...(fontSizeMark ? [fontSizeMark] : []), ...(fontFamilyMark ? [fontFamilyMark] : [])]);
// Insert styled text at cursor position
- const transaction = state.tr.insert(insertPos, predictedText).setStoredMarks([state.schema.marks.pFontColor.create({ fontColor: 'gray' })]); // should probably instead inquire marks before predictive prompt
+ const transaction = state.tr.insert(insertPos, predictedText).setStoredMarks(this.prePredictiveMarks);
dispatch(transaction);
this.predictiveText = text;
};
+ /**
+ * Method to remove the predictive question upon type/click
+ * @returns - once predictive text is found, or all text has been checked
+ */
createPredictiveCleanupPlugin = () => {
return new Plugin({
view: () => {
@@ -168,15 +260,20 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
if (node.isText && node.text === textToRemove) {
const tr = state.tr.delete(pos, pos + node.nodeSize);
- // Set the desired default marks for future input
+ // default marks for input
const fontSizeMark = state.schema.marks.pFontSize.create({ fontSize: '14px' });
const fontColorMark = state.schema.marks.pFontColor.create({ fontColor: 'gray' });
tr.setStoredMarks([]);
- tr.setStoredMarks([fontSizeMark, fontColorMark]);
+ if (this.prePredictiveMarks.length > 0) {
+ tr.setStoredMarks(this.prePredictiveMarks);
+ } else {
+ tr.setStoredMarks([fontSizeMark, fontColorMark]);
+ }
dispatch(tr);
this.predictiveText = null;
+ this.prePredictiveMarks = [];
return false;
}
return true;
@@ -194,8 +291,9 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
};
componentDidMount(): void {
- console.log('componentDidMount() triggered...');
- console.log('Text: ' + RTFCast(this.Document.text)?.Text);
+ // console.log('componentDidMount() triggered...');
+ document.addEventListener('mousedown', this.handleDocumentClick);
+ // console.log('Text: ' + RTFCast(this.Document.text)?.Text);
const editorView = this._ref.current?.EditorView;
if (editorView) {
@@ -214,15 +312,17 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
const isDefaultTitle = isTitleString && currentTitle.includes('Untitled DailyJournal');
if (isTextEmpty && isDefaultTitle) {
- console.log('Journal title and text are default. Initializing...');
+ // console.log('Journal title and text are default. Initializing...');
this.setDailyTitle();
this.setDailyText();
+ this.setInitialDateRange();
} else {
- console.log('Journal already has content. Skipping initialization.');
+ // console.log('Journal already has content. Skipping initialization.');
}
}
componentWillUnmount(): void {
+ document.removeEventListener('mousedown', this.handleDocumentClick);
const editorView = this._ref.current?.EditorView;
if (editorView) {
editorView.dom.removeEventListener('input', this.onTextInput);
@@ -230,10 +330,20 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
if (this.typingTimeout) clearTimeout(this.typingTimeout);
}
+ /**
+ * Method to generate pormpts via GPT
+ * @returns - if failed
+ */
@action handleGeneratePrompts = async () => {
+ if (this.isLoadingPrompts) {
+ return;
+ }
+
+ this.isLoadingPrompts = true;
+
const rawText = RTFCast(this.Document.text)?.Text ?? '';
- console.log('Extracted Journal Text:', rawText);
- console.log('Before Update:', this.Document.text, 'Type:', typeof this.Document.text);
+ // console.log('Extracted Journal Text:', rawText);
+ // console.log('Before Update:', this.Document.text, 'Type:', typeof this.Document.text);
if (!rawText.trim()) {
alert('Journal is empty! Write something first.');
@@ -273,9 +383,15 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
}
} catch (err) {
console.error('Error calling GPT:', err);
+ } finally {
+ this.isLoadingPrompts = false;
}
};
+ /**
+ * Method to render the styled DailyJournal
+ * @returns - the HTML component for the journal
+ */
render() {
return (
<div
@@ -296,6 +412,7 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
}}>
{/* GPT Button */}
<button
+ id="prompts-button"
style={{
position: 'absolute',
bottom: '5px',
@@ -308,9 +425,88 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
cursor: 'pointer',
zIndex: 10,
}}
- onClick={this.handleGeneratePrompts}>
+ onClick={this.togglePromptMenu}>
Prompts
</button>
+ {this.showPromptMenu && (
+ <div
+ id="prompts-menu"
+ style={{
+ position: 'absolute',
+ bottom: '45px',
+ right: '5px',
+ backgroundColor: 'white',
+ border: '1px solid #ccc',
+ borderRadius: '4px',
+ padding: '10px',
+ boxShadow: '0 2px 6px rgba(0,0,0,0.2)',
+ zIndex: 20,
+ minWidth: '170px',
+ maxWidth: 'fit-content',
+ overflow: 'auto',
+ }}>
+ <div
+ style={{
+ display: 'flex',
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ marginBottom: '10px',
+ }}>
+ <label
+ style={{
+ display: 'flex',
+ alignItems: 'center',
+ gap: '6px',
+ fontSize: '14px',
+ justifyContent: 'flex-end',
+ width: '100%',
+ }}>
+ /ask
+ <input type="checkbox" checked={this.askPromptsEnabled} onChange={this.toggleAskPrompts} style={{ margin: 0 }} />
+ </label>
+ </div>
+
+ <div
+ style={{
+ display: 'flex',
+ justifyContent: 'flex-end',
+ alignItems: 'center',
+ marginBottom: '10px',
+ }}>
+ <label
+ style={{
+ display: 'flex',
+ alignItems: 'center',
+ gap: '6px',
+ fontSize: '14px',
+ justifyContent: 'flex-end',
+ width: '100%',
+ }}>
+ Inline Prompting
+ <input type="checkbox" checked={this.inlinePromptsEnabled} onChange={this.toggleInlinePrompts} style={{ margin: 0 }} />
+ </label>
+ </div>
+
+ <button
+ onClick={() => {
+ this.showPromptMenu = false;
+ this.handleGeneratePrompts();
+ }}
+ disabled={this.isLoadingPrompts}
+ style={{
+ backgroundColor: '#9EAD7C',
+ color: 'white',
+ border: 'none',
+ borderRadius: '4px',
+ cursor: this.isLoadingPrompts ? 'not-allowed' : 'pointer',
+ opacity: this.isLoadingPrompts ? 0.6 : 1,
+ padding: '5px 10px',
+ float: 'right',
+ }}>
+ Generate Prompts
+ </button>
+ </div>
+ )}
<FormattedTextBox ref={this._ref} {...this._props} fieldKey={'text'} Document={this.Document} TemplateDataDocument={undefined} />
</div>
@@ -318,8 +514,10 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
}
}
+const ObservedDailyJournal = observer(DailyJournal);
+
Docs.Prototypes.TemplateMap.set(DocumentType.JOURNAL, {
- layout: { view: DailyJournal, dataField: 'text' },
+ layout: { view: ObservedDailyJournal, dataField: 'text' },
options: {
acl: '',
_height: 35,
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index 3734ad9cc..3ef3c2cef 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -17,7 +17,7 @@
min-width: 12px;
position: relative;
display: inline-block;
- margin: 0;
+ margin: 0px;
transform: scale(0.7);
background-color: rgba(155, 155, 155, 0.24);
}
@@ -53,7 +53,7 @@
.dashFieldView,
.dashFieldView-active {
.dashFieldView-select {
- height: 10p;
+ height: 100%;
font-size: 12px;
background: transparent;
opacity: 0;
@@ -61,7 +61,7 @@
}
}
-.dashFieldView {
+.dashFieldView-active {
&:hover {
.dashFieldView-select {
opacity: unset;
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 7ea5d1fcf..6e3bdc5e8 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -262,6 +262,7 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
render() {
return (
<div
+ // eslint-disable-next-line no-use-before-define
className={`dashFieldView${this.isRowActive() ? '-active' : ''}`}
ref={this._fieldRef}
style={{
diff --git a/src/client/views/nodes/formattedText/EquationEditor.scss b/src/client/views/nodes/formattedText/EquationEditor.scss
index b0c17e56e..602135a30 100644
--- a/src/client/views/nodes/formattedText/EquationEditor.scss
+++ b/src/client/views/nodes/formattedText/EquationEditor.scss
@@ -32,7 +32,7 @@
margin-left: -1px;
position: relative;
z-index: 1;
- padding: 0;
+ padding: 0px;
display: -moz-inline-box;
display: inline-block;
}
@@ -128,8 +128,8 @@
.mq-math-mode * {
font-size: inherit;
line-height: inherit;
- margin: 0;
- padding: 0;
+ margin: 0px;
+ padding: 0px;
border-color: black;
-webkit-user-select: none;
-moz-user-select: none;
@@ -178,7 +178,7 @@
margin-left: 0.1em;
}
.mq-math-mode .mq-roman var.mq-f {
- margin: 0;
+ margin: 0px;
}
.mq-math-mode big {
font-size: 200%;
@@ -323,7 +323,7 @@
padding: 0.1em;
}
.mq-math-mode .mq-sqrt-prefix {
- padding-top: 0;
+ padding-top: 0px;
position: relative;
top: 0.1em;
vertical-align: top;
diff --git a/src/client/views/nodes/formattedText/EquationEditor.tsx b/src/client/views/nodes/formattedText/EquationEditor.tsx
index 48efa6e63..23d273523 100644
--- a/src/client/views/nodes/formattedText/EquationEditor.tsx
+++ b/src/client/views/nodes/formattedText/EquationEditor.tsx
@@ -72,6 +72,10 @@ class EquationEditor extends Component<EquationEditorProps> {
this.mathField.latex(value || '');
}
+ componentDidUpdate(prevProps: Readonly<EquationEditorProps>): void {
+ !prevProps.value && this.mathField.latex(this.props.value || '');
+ }
+
render() {
return <span ref={this.element} style={{ border: '0px', boxShadow: 'None' }} />;
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 547a2efa8..d5e566226 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -46,7 +46,7 @@
}
audiotag {
- left: 0;
+ left: 0px;
position: absolute;
cursor: pointer;
border-radius: 10px;
@@ -62,7 +62,7 @@ audiotag:hover {
.formattedTextBox {
touch-action: none;
background: inherit;
- padding: 0;
+ padding: 0px;
border-width: 0px;
border-color: global.$medium-gray;
box-sizing: border-box;
@@ -77,14 +77,14 @@ audiotag:hover {
width: 100%;
position: relative;
transform-origin: left top;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
}
.formattedTextBox-cont {
touch-action: none;
background: inherit;
- padding: 0;
+ padding: 0px;
border-width: 0px;
border-radius: inherit;
border-color: global.$medium-gray;
@@ -111,7 +111,7 @@ audiotag:hover {
.answer-tooltip {
font-size: 15px;
padding: 2px;
- max-width: 150;
+ max-width: 150px;
line-height: 150%;
position: relative;
}
@@ -122,10 +122,10 @@ audiotag:hover {
position: absolute;
color: white;
background: black;
- right: 0;
- bottom: 0;
- width: 15;
- height: 22;
+ right: 0px;
+ bottom: 0px;
+ width: 15px;
+ height: 22px;
cursor: default;
}
@@ -139,8 +139,8 @@ audiotag:hover {
.formattedTextBox-sidebar-handle {
position: absolute;
- top: 0;
- right: 0;
+ top: 0px;
+ right: 0px;
width: 20px;
height: 20px;
font-size: 11px;
@@ -168,7 +168,7 @@ audiotag:hover {
height: 100%;
display: inline-block;
position: absolute;
- right: 0;
+ right: 0px;
overflow: hidden;
.collectionfreeformview-container {
@@ -302,8 +302,8 @@ footnote::before {
position: absolute;
top: -0.5em;
content: ' ';
- height: 0;
- width: 0;
+ height: 0px;
+ width: 0px;
}
.formattedTextBox-inlineComment {
@@ -346,7 +346,7 @@ footnote::before {
.prosemirror-linkBtn {
background: unset;
color: unset;
- padding: 0;
+ padding: 0px;
text-transform: unset;
letter-spacing: unset;
font-size: unset;
@@ -357,7 +357,7 @@ footnote::before {
background-color: dimgray;
margin-top: 1.5em;
z-index: 1;
- padding: 5;
+ padding: 5px;
border-radius: 2px;
}
.prosemirror-hrefoptions {
@@ -396,7 +396,7 @@ footnote::before {
blockquote {
padding: 10px 10px;
font-size: smaller;
- margin: 0;
+ margin: 0px;
font-style: italic;
background: lightgray;
border-left: solid 2px dimgray;
@@ -415,7 +415,7 @@ footnote::before {
p {
font-family: inherit;
}
- margin-left: 0;
+ margin-left: 0px;
}
.bullet1 {
p {
@@ -439,7 +439,7 @@ footnote::before {
display: inline-block;
font-family: inherit;
}
- margin-left: 0;
+ margin-left: 0px;
background-color: inherit;
}
.decimal2-ol {
@@ -506,7 +506,7 @@ footnote::before {
display: inline-block;
font-family: inherit;
}
- margin-left: 0;
+ margin-left: 0px;
padding-left: 1.2em;
background-color: inherit;
}
@@ -661,7 +661,7 @@ footnote::before {
.formattedTextBox-cont {
touch-action: none;
background: inherit;
- padding: 0;
+ padding: 0px;
border-width: 0px;
border-radius: inherit;
border-color: global.$medium-gray;
@@ -706,7 +706,7 @@ footnote::before {
height: 100%;
display: inline-block;
position: absolute;
- right: 0;
+ right: 0px;
.collectionfreeformview-container {
position: relative;
@@ -832,8 +832,8 @@ footnote::before {
position: absolute;
top: -0.5em;
content: ' ';
- height: 0;
- width: 0;
+ height: px;
+ width: 0px;
}
.formattedTextBox-inlineComment {
@@ -892,7 +892,7 @@ footnote::before {
display: inline;
font-family: inherit;
}
- margin-left: 0;
+ margin-left: 0px;
}
.decimal2-ol {
counter-reset: deci2;
@@ -952,7 +952,7 @@ footnote::before {
display: inline;
font-family: inherit;
}
- margin-left: 0;
+ margin-left: 0px;
padding-left: 1.2em;
}
.multi2-ol {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 57720baae..255ee1afe 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -13,7 +13,7 @@ import { EditorState, NodeSelection, Plugin, Selection, TextSelection, Transacti
import { EditorView, NodeViewConstructor } from 'prosemirror-view';
import * as React from 'react';
import { BsMarkdownFill } from 'react-icons/bs';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, removeStyleSheet, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, removeStyleSheet, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils';
import { DateField } from '../../../../fields/DateField';
import { CreateLinkToActiveAudio, Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
@@ -98,7 +98,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
public static PasteOnLoad: ClipboardEvent | undefined;
public static SelectOnLoadChar = '';
- public static LiveTextUndo: UndoManager.Batch | undefined; // undo batch when typing a new text note into a collection
+ public static LiveTextUndo: UndoManager.Batch | undefined; // undo batch request when typing a new text note into a collection
+ private _liveTextUndo: UndoManager.Batch | undefined; // captured undo batch when typing a new text note into a collection
private static _nodeViews: (self: FormattedTextBox) => { [key: string]: NodeViewConstructor };
private _curHighlights = new ObservableSet<string>(['Audio Tags']);
@@ -270,13 +271,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
e.preventDefault();
e.stopPropagation();
const targetCreator = (annotationOn?: Doc) => {
- const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn);
+ const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
+ target.layout_fitWidth = true;
DocumentView.SetSelectOnLoad(target);
return target;
};
+ const sourceAnchorCreator = () => this.getAnchor(true);
+
const docView = this.DocumentView?.();
- docView && DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, () => this.getAnchor(true), targetCreator), e.pageX, e.pageY);
+ docView && DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY);
});
AnchorMenu.Instance.AddDrawingAnnotation = (drawing: Doc) => {
@@ -304,6 +308,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
};
+ autoTag = () => {
+ const rawText = RTFCast(this.Document[this.fieldKey])?.Text ?? StrCast(this.Document[this.fieldKey]);
+ if (rawText && !this.Document.$tags_chat) {
+ const callType = rawText.includes('[placeholder]') ? GPTCallType.CLASSIFYTEXTMINIMAL : GPTCallType.CLASSIFYTEXTFULL;
+
+ gptAPICall(rawText, callType).then(
+ action(desc => {
+ // Split GPT response into tokens and push individually & clear existing tags
+ this.Document.$tags_chat = new List<string>(desc.trim().split(/\s+/));
+ this.Document._layout_showTags = true;
+ })
+ );
+ }
+ };
+
leafText = (node: Node) => {
if (node.type === this.EditorView?.state.schema.nodes.dashField) {
const refDoc = !node.attrs.docId ? this.rootDoc : (DocServer.GetCachedRefField(node.attrs.docId as string) as Doc);
@@ -365,6 +384,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
dataDoc[this.fieldKey] =
numstring !== undefined ? Number(newText) : newText || (DocCast(dataDoc.proto)?.[this.fieldKey] === undefined && this.layoutDoc[this.fieldKey] === undefined) ? new RichTextField(newJson, newText) : undefined;
textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.Document, text: newText });
+ if (textChange) this.dataDoc.$tags_chat = undefined;
this.ApplyingChange = ''; // turning this off here allows a Doc to retrieve data from template if noTemplate below is changed to false
unchanged = false;
}
@@ -425,10 +445,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const newAutoLinks = new Set<Doc>();
const oldAutoLinks = Doc.Links(this.Document).filter(
link =>
- ((!Doc.isTemplateForField(this.Document) &&
- ((DocCast(link.link_anchor_1) && !Doc.isTemplateForField(DocCast(link.link_anchor_1)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) &&
- ((DocCast(link.link_anchor_2) && !Doc.isTemplateForField(DocCast(link.link_anchor_2)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_2), this.Document))) ||
- (Doc.isTemplateForField(this.Document) && (link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document))) &&
+ ((!Doc.IsTemplateForField(this.Document) &&
+ ((DocCast(link.link_anchor_1) && !Doc.IsTemplateForField(DocCast(link.link_anchor_1)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) &&
+ ((DocCast(link.link_anchor_2) && !Doc.IsTemplateForField(DocCast(link.link_anchor_2)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_2), this.Document))) ||
+ (Doc.IsTemplateForField(this.Document) && (link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document))) &&
link.link_relationship === LinkManager.AutoKeywords
); // prettier-ignore
if (this.EditorView?.state.doc.textContent) {
@@ -1068,17 +1088,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return anchorDoc ?? this.Document;
}
+ showBorderRounding = returnTrue;
getView = (doc: Doc, options: FocusViewOptions) => {
if (DocListCast(this.dataDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
- if (!this.SidebarShown) {
- this.toggleSidebar(false);
- options.didMove = true;
- }
- setTimeout(() => this._sidebarRef?.current?.makeDocUnfiltered(doc));
+ return SidebarAnnos.getView(this._sidebarRef.current, this.SidebarShown, () => this.toggleSidebar(false), doc, options);
}
- return new Promise<Opt<DocumentView>>(res => {
- DocumentView.addViewRenderedCb(doc, dv => res(dv));
- });
+ return new Promise<Opt<DocumentView>>(res => DocumentView.addViewRenderedCb(doc, res));
};
focus = (textAnchor: Doc, options: FocusViewOptions) => {
const focusSpeed = options.zoomTime ?? 500;
@@ -1145,13 +1160,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return undefined;
};
- // if the scroll height has changed and we're in layout_autoHeight mode, then we need to update the textHeight component of the doc.
- // Since we also monitor all component height changes, this will update the document's height.
- resetNativeHeight = action((scrollHeight: number) => {
- this.layoutDoc['_' + this.fieldKey + '_height'] = scrollHeight;
- if (!this.layoutDoc.isTemplateForField && NumCast(this.layoutDoc._nativeHeight)) this.layoutDoc._nativeHeight = scrollHeight;
- });
-
addPlugin = (plugin: Plugin) => {
const editorView = this.EditorView;
if (editorView) {
@@ -1182,21 +1190,31 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this._disposers.width = reaction(this._props.PanelWidth, this.tryUpdateScrollHeight);
this._disposers.scrollHeight = reaction(
() => ({ scrollHeight: this.scrollHeight, layoutAutoHeight: this.layout_autoHeight, width: NumCast(this.layoutDoc._width) }),
- ({ width, scrollHeight, layoutAutoHeight }) => width && layoutAutoHeight && this.resetNativeHeight(scrollHeight),
+ ({ width, scrollHeight, layoutAutoHeight }) => width && layoutAutoHeight && (this.layoutDoc['_' + this.fieldKey + '_height'] = scrollHeight),
{ fireImmediately: true }
);
this._disposers.componentHeights = reaction(
// set the document height when one of the component heights changes and layout_autoHeight is on
- () => ({ border: this._props.PanelHeight(), sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, layoutAutoHeight: this.layout_autoHeight, marginsHeight: this.layout_autoHeightMargins }),
- ({ border, sidebarHeight, textHeight, layoutAutoHeight, marginsHeight }) => {
+ () => ({
+ border: this._props.PanelHeight(),
+ scrollHeight: NumCast(this.layoutDoc['_' + this.fieldKey + '_height']),
+ sidebarHeight: this.sidebarHeight,
+ textHeight: this.textHeight,
+ layoutAutoHeight: this.layout_autoHeight,
+ marginsHeight: this.layout_autoHeightMargins,
+ }),
+ ({ border, sidebarHeight, scrollHeight, textHeight, layoutAutoHeight, marginsHeight }) => {
const newHeight = this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight));
if (
(!Array.from(this._curHighlights).includes('Bold Text') || this._props.isSelected()) && //
layoutAutoHeight &&
newHeight &&
- (newHeight !== this.layoutDoc.height || border < NumCast(this.layoutDoc.height)) &&
+ (newHeight !== this.layoutDoc.height || border < NumCast(this.layoutDoc.height) || this.layoutDoc._nativeHeight !== scrollHeight) &&
!this._props.dontRegisterView
) {
+ if (NumCast(this.layoutDoc.nativeHeight)) {
+ this.layoutDoc._nativeHeight = scrollHeight;
+ }
this._props.setHeight?.(newHeight);
}
},
@@ -1219,7 +1237,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const protoTime = protoData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.dataDoc.proto)?.[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
const recentData = dataTime >= layoutTime ? (protoTime >= dataTime ? protoData : dataData) : layoutTime >= protoTime ? layoutData : protoData;
const whichData = recentData ?? (this.layoutDoc.isTemplateDoc ? layoutData : protoData) ?? protoData;
- return !whichData ? undefined : { data: RTFCast(whichData), str: Field.toString(DocCast(whichData) ?? StrCast(whichData)) };
+ return !whichData ? undefined : { data: RTFCast(whichData), str: Field.toString(DocCast(whichData) ?? NumCast(whichData)?.toString() ?? StrCast(whichData)) };
},
incomingValue => {
if (this.EditorView && this.ApplyingChange !== this.fieldKey) {
@@ -1525,7 +1543,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
if (this.EditorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
const { $from } = this.EditorView.state.selection;
- const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
+ const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail() });
const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
if (selLoadChar === 'Enter') {
@@ -1536,6 +1554,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
}
if (selectOnLoad) {
+ this._liveTextUndo = FormattedTextBox.LiveTextUndo;
+ FormattedTextBox.LiveTextUndo = undefined;
this.EditorView!.focus();
}
if (this._props.isContentActive()) this.prepareForTyping();
@@ -1552,7 +1572,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const { text, paragraph } = schema.nodes;
const selNode = this.EditorView.state.selection.$anchor.node();
if (this.EditorView.state.selection.from === 1 && this.EditorView.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
- const docDefaultMarks = [schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) })];
+ const docDefaultMarks = [schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail() })];
this.EditorView.state.selection.empty && this.EditorView.state.selection.from === 1 && this.EditorView?.dispatch(this.EditorView?.state.tr.setStoredMarks(docDefaultMarks).removeStoredMark(schema.marks.pFontColor));
}
}
@@ -1565,8 +1585,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
removeStyleSheet(this._userStyleSheetElement);
Object.values(this._disposers).forEach(disposer => disposer?.());
this.endUndoTypingBatch();
- FormattedTextBox.LiveTextUndo?.end();
- FormattedTextBox.LiveTextUndo = undefined;
+ this._liveTextUndo?.end();
this.unhighlightSearchTerms();
this.EditorView?.destroy();
RichTextMenu.Instance?.TextView === this && RichTextMenu.Instance.updateMenu(undefined, undefined, undefined, undefined);
@@ -1688,7 +1707,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if (e.clientX > boundsRect.left && e.clientX < boundsRect.right && e.clientY > boundsRect.bottom) {
// if we clicked below the last prosemirror div, then set the selection to be the end of the document
editorView.focus();
- editorView.dispatch(editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, editorView.state.doc.content.size)));
+ // editorView.dispatch(editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, editorView.state.doc.content.size)));
}
} else if (node && [editorView.state.schema.nodes.ordered_list, editorView.state.schema.nodes.listItem].includes(node.type) && node !== (editorView.state.selection as NodeSelection)?.node && pcords) {
editorView.dispatch(editorView.state.tr.setSelection(NodeSelection.create(editorView.state.doc, pcords.pos)));
@@ -1770,7 +1789,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if (this.ProseRef?.children[0] !== e.nativeEvent.target) return;
if (!(this.EditorView?.state.selection instanceof NodeSelection) || this.EditorView.state.selection.node.type !== this.EditorView.state.schema.nodes.footnote) {
const stordMarks = this.EditorView?.state.storedMarks?.slice();
- if (!(this.EditorView?.state.selection instanceof NodeSelection)) {
+ if (!(this.EditorView?.state.selection instanceof NodeSelection) && typeof this.dataDoc[this.fieldKey] !== 'number') {
this.autoLink();
if (this.EditorView?.state.tr) {
const tr = stordMarks?.reduce((tr2, m) => {
@@ -1795,8 +1814,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this.endUndoTypingBatch();
- FormattedTextBox.LiveTextUndo?.end();
- FormattedTextBox.LiveTextUndo = undefined;
+ this._liveTextUndo?.end();
// if the text box blurs and none of its contents are focused(), then pass the blur along
setTimeout(() => !this.ProseRef?.contains(document.activeElement) && this._props.onBlur?.());
@@ -1817,7 +1835,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return;
}
if (this._enteringStyle && 'tix!'.includes(e.key)) {
- const tag = e.key === 't' ? 'todo' : e.key === 'i' ? 'ignore' : e.key === 'x' ? 'disagree' : e.key === '!' ? 'important' : '??';
const node = state.selection.$from.nodeAfter;
const start = state.selection.from;
const end = state.selection.to;
@@ -1826,9 +1843,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
StopEvent(e);
_editorView.dispatch(
state.tr
- .removeMark(start, end, schema.marks.user_mark)
- .addMark(start, end, schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) }))
- .addMark(start, end, schema.marks.user_tag.create({ userid: ClientUtils.CurrentUserEmail(), tag, modified: Math.round(Date.now() / 1000 / 60) }))
+ .removeMark(start, end, schema.marks.user_mark) //
+ .addMark(start, end, schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail() }))
);
return;
}
@@ -1861,9 +1877,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
break;
default:
if ([AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.Document))) {
- const modified = Math.floor(Date.now() / 1000);
- const mark = state.selection.$to.marks().find(m => m.type === schema.marks.user_mark && m.attrs.modified === modified);
- _editorView.dispatch(state.tr.removeStoredMark(schema.marks.user_mark).addStoredMark(mark ?? schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified })));
+ const mark = state.selection.$to.marks().find(m => m.type === schema.marks.user_mark);
+ _editorView.dispatch(
+ state.tr
+ .removeStoredMark(schema.marks.user_mark) //
+ .addStoredMark(mark ?? schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail() }))
+ );
}
break;
}
@@ -1887,14 +1906,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const margins = 2 * NumCast(this.layoutDoc._yMargin, this._props.yMargin || 0);
const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
if (this.EditorView && children && !SnappingManager.IsDragging) {
- const getChildrenHeights = (kids: Element[] | undefined) => kids?.reduce((p, child) => p + toHgt(child), margins) ?? 0;
+ const getChildrenHeights = (kids: Element[] | undefined) => kids?.reduce((p, child) => p + toHgt(child), 0) ?? 0;
const toNum = (val: string) => Number(val.replace('px', ''));
const toHgt = (node: Element): number => {
const { height, marginTop, marginBottom } = getComputedStyle(node);
const childHeight = height === 'auto' ? getChildrenHeights(Array.from(node.children)) : toNum(height);
return childHeight + Math.max(0, toNum(marginTop)) + Math.max(0, toNum(marginBottom));
};
- const proseHeight = !this.ProseRef ? 0 : getChildrenHeights(children);
+ const proseHeight = !this.ProseRef ? 0 : getChildrenHeights(children) + margins;
const scrollHeight = this.ProseRef && proseHeight;
if (this._props.setHeight && !this._props.suppressSetHeight && scrollHeight && !this._props.dontRegisterView) {
// if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
@@ -2110,6 +2129,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
};
+ setRef = (r: HTMLDivElement | null) => this.fixWheelEvents(r, this._props.isContentActive, this.onPassiveWheel);
+ setScrollRef = (r: HTMLDivElement | null) => (this._scrollRef = r);
render() {
TraceMobx();
const scale = this.nativeScaling();
@@ -2124,7 +2145,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
) : styleFromLayout?.height === '0px' ? null : (
<div
className="formattedTextBox"
- ref={r => this.fixWheelEvents(r, this._props.isContentActive, this.onPassiveWheel)}
+ ref={this.setRef}
style={{
...(this._props.dontScale
? {}
@@ -2162,9 +2183,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
onDoubleClick={this.onDoubleClick}>
<div
className="formattedTextBox-outer"
- ref={r => {
- this._scrollRef = r;
- }}
+ ref={this.setScrollRef}
style={{
width: this.noSidebar ? '100%' : `calc(100% - ${this.sidebarWidthPercent})`,
overflow: this.layoutDoc._createDocOnCR || this.layoutDoc._layout_hideScroll ? 'hidden' : this.layout_autoHeight ? 'visible' : undefined,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
index bc0810f22..92f3e3290 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.scss
@@ -11,8 +11,8 @@
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
box-shadow: 3px 3px 1.5px grey;
- max-width: 400;
- max-height: 235;
+ max-width: 400px;
+ max-height: 235px;
height: max-content;
.formattedTextBox-tooltipText {
height: max-content;
@@ -22,26 +22,26 @@
.formattedTextBox-tooltip:before {
content: '';
- height: 0;
- width: 0;
+ height: 0px;
+ width: 0px;
position: absolute;
left: 50%;
margin-left: -5px;
bottom: -6px;
border: 5px solid transparent;
- border-bottom-width: 0;
+ border-bottom-width: 0px;
border-top-color: silver;
}
.formattedTextBox-tooltip:after {
content: '';
- height: 0;
- width: 0;
+ height: 0px;
+ width: 0px;
position: absolute;
left: 50%;
margin-left: -5px;
bottom: -4.5px;
border: 5px solid transparent;
- border-bottom-width: 0;
+ border-bottom-width: 0px;
border-top-color: white;
}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.scss b/src/client/views/nodes/formattedText/RichTextMenu.scss
index fcc816447..7c747de1e 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.scss
+++ b/src/client/views/nodes/formattedText/RichTextMenu.scss
@@ -23,7 +23,7 @@
.dropdown {
position: absolute;
top: 35px;
- left: 0;
+ left: 0px;
background-color: #323232;
color: global.$light-gray;
border: 1px solid #4d4d4d;
@@ -47,7 +47,7 @@
}
&:last-child {
- margin-bottom: 0;
+ margin-bottom: 0px;
}
}
}
diff --git a/src/client/views/nodes/formattedText/TooltipTextMenu.scss b/src/client/views/nodes/formattedText/TooltipTextMenu.scss
index 87320943d..8980a93a2 100644
--- a/src/client/views/nodes/formattedText/TooltipTextMenu.scss
+++ b/src/client/views/nodes/formattedText/TooltipTextMenu.scss
@@ -195,7 +195,7 @@
left: 1px;
width: 24px;
height: 4px;
- margin-top: 0;
+ margin-top: 0px;
}
}
@@ -221,7 +221,7 @@
display: inline-block;
width: 1em;
height: 1em;
- stroke-width: 0;
+ stroke-width: 0px;
stroke: currentColor;
fill: currentColor;
margin-right: 15px;
@@ -231,7 +231,7 @@
display: inline-block;
width: 1em;
height: 1em;
- stroke-width: 3;
+ stroke-width: 3px;
fill: greenyellow;
margin-right: 15px;
}
@@ -270,7 +270,7 @@
&.ProseMirror-menu-dropdown {
width: 10px;
height: 25px;
- margin: 0;
+ margin: 0px;
padding: 0 2px;
background-color: #323232;
text-align: center;
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index b7dae1ca3..333ee6be8 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -330,20 +330,6 @@ export const marks: { [index: string]: MarkSpec } = {
return ['span', { class: 'UM-' + uid + remote + ' UM-min-' + min + ' UM-hr-' + hr + ' UM-day-' + day }, 0];
},
},
- // the id of the user who entered the text
- user_tag: {
- attrs: {
- userid: { default: '' },
- modified: { default: 'when?' }, // 1 second intervals since 1970
- tag: { default: '' },
- },
- group: 'inline',
- inclusive: false,
- toDOM: node => {
- const uid = node.attrs.userid.replace('.', '').replace('@', '');
- return ['span', { class: 'UT-' + uid + ' UT-' + node.attrs.tag }, 0];
- },
- },
// :: MarkSpec Code font mark. Represented as a `<code>` element.
code: {