aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-08-19 16:42:56 -0400
committerbobzel <zzzman@gmail.com>2024-08-19 16:42:56 -0400
commite57584a1be9d428fb40fc789494a7ac0ac14fb84 (patch)
tree55e6204ea0c676ae1d2fe8ba162cd6c3cb0ff661
parentb6f6acb80f57011594d39b9ce576a5e77862cb7f (diff)
extensive fixes to DiagramBox
-rw-r--r--src/client/documents/Documents.ts1
-rw-r--r--src/client/util/CurrentUserUtils.ts16
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx6
-rw-r--r--src/client/views/nodes/DiagramBox.scss112
-rw-r--r--src/client/views/nodes/DiagramBox.tsx360
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx3
6 files changed, 206 insertions, 292 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d7fdf016c..b108b73db 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -256,6 +256,7 @@ export class DocumentOptions {
_layout_nativeDimEditable?: BOOLt = new BoolInfo('native dimensions can be modified using document decoration reizers', false);
_layout_reflowVertical?: BOOLt = new BoolInfo('permit vertical resizing with content "reflow"');
_layout_reflowHorizontal?: BOOLt = new BoolInfo('permit horizontal resizing with content reflow');
+ _layout_noSidebar?: BOOLt = new BoolInfo('whether to display the sidebar toggle button');
layout_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
layout_maxShown?: NUMt = new NumInfo('maximum number of children to display at one time (see multicolumnview)');
_layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index c99ce1832..311b86fa4 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -321,20 +321,16 @@ export class CurrentUserUtils {
const rtfield = new RichTextField(JSON.stringify(
{doc: {type:"doc",content:[
{type:"code_block",content:[
- {type:"text",text:"^@mermaids"},
- {type:"text",text:"\n\n"},
- {type:"text",text:"pie "},
- {type:"text",text:"title"},
- {type:"text",text:" "},
- {type:"text",text:"Minerals in my tap water"},
- {type:"text",text:"\n \"Calcium\" : "},
+ {type:"text",text:`^@mermaids\n`},
+ {type:"text",text:`\n pie title Minerals in my tap water`},
+ {type:"text",text:`\n "Calcium" : `},
{type:"dashField",attrs:{fieldKey:"calcium",docId:"",hideKey:true,hideValue:false,editable:true}},
- {type:"text",text:"\n \"Potassium\" : "},
+ {type:"text",text:`\n "Potassium" : `},
{type:"dashField",attrs:{fieldKey:"pot",docId:"",hideKey:true,hideValue:false,editable:true}},
- {type:"text",text:"\n \"Magnesium\" : 10.01"}
+ {type:"text",text:`\n "Magnesium" : 10.01`}
]}
]},
- selection:{type:"text",anchor:109,head:109}
+ selection:{type:"text",anchor:1,head:1}
}),
`^@mermaids
pie title Minerals in my tap water
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 2d9191dd7..61bd0241c 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -13,7 +13,7 @@ import { undoBatch } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { ContextMenuProps } from '../../ContextMenuItem';
import { DocumentView } from '../../nodes/DocumentView';
-import { CollectionSubView } from '../CollectionSubView';
+import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
import './CollectionGridView.scss';
import Grid, { Layout } from './Grid';
@@ -26,7 +26,7 @@ export class CollectionGridView extends CollectionSubView() {
@observable private _scroll: number = 0; // required to make sure the decorations box container updates on scroll
private dropLocation: object = {}; // sets the drop location for external drops
- constructor(props: any) {
+ constructor(props: SubCollectionViewProps) {
super(props);
makeObservable(this);
}
@@ -200,7 +200,7 @@ export class CollectionGridView extends CollectionSubView() {
whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
onClickScript={this.onChildClickHandler}
renderDepth={this._props.renderDepth + 1}
- dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
+ dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
/>
);
}
diff --git a/src/client/views/nodes/DiagramBox.scss b/src/client/views/nodes/DiagramBox.scss
index d2749f1ad..323638bff 100644
--- a/src/client/views/nodes/DiagramBox.scss
+++ b/src/client/views/nodes/DiagramBox.scss
@@ -1,3 +1,5 @@
+$searchbarHeight: 50px;
+
.DIYNodeBox {
width: 100%;
height: 100%;
@@ -6,83 +8,75 @@
align-items: center;
justify-content: center;
- .DIYNodeBox-wrapper {
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- .DIYNodeBox {
- /* existing code */
-
- .DIYNodeBox-iframe {
- height: 100%;
- width: 100%;
- border: none;
+ .DIYNodeBox {
+ /* existing code */
- }
+ .DIYNodeBox-iframe {
+ height: 100%;
+ width: 100%;
+ border: none;
}
+ }
- .search-bar {
- display: flex;
- justify-content: center;
- align-items: center;
- width: 100%;
- padding: 10px;
+ .DIYNodeBox-searchbar {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: $searchbarHeight;
+ padding: 10px;
- input[type="text"] {
- flex: 1;
- margin-right: 10px;
- }
+ input[type='text'] {
+ flex: 1;
+ margin-right: 10px;
+ }
- button {
- padding: 5px 10px;
- }
+ button {
+ padding: 5px 10px;
}
+ }
- .content {
+ .DIYNodeBox-content {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: calc(100% - $searchbarHeight);
+ .diagramBox {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
- width:100%;
- height:100%;
- .diagramBox{
+ width: 100%;
+ height: 100%;
+ svg {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
- width:100%;
- height:100%;
- svg{
- flex: 1;
- display: flex;
- justify-content: center;
- align-items: center;
- width:100%;
- height:100%;
- }
+ width: 100%;
+ height: 100%;
}
}
+ }
- .loading-circle {
- position: relative;
- width: 50px;
- height: 50px;
- border-radius: 50%;
- border: 3px solid #ccc;
- border-top-color: #333;
- animation: spin 1s infinite linear;
- }
+ .loading-circle {
+ position: relative;
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ border: 3px solid #ccc;
+ border-top-color: #333;
+ animation: spin 1s infinite linear;
+ }
- @keyframes spin {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
+ @keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
}
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/DiagramBox.tsx b/src/client/views/nodes/DiagramBox.tsx
index 32969fa53..36deb2d8d 100644
--- a/src/client/views/nodes/DiagramBox.tsx
+++ b/src/client/views/nodes/DiagramBox.tsx
@@ -1,284 +1,198 @@
import mermaid from 'mermaid';
-import { action, makeObservable, observable, reaction } from 'mobx';
+import { action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast } from '../../../fields/Doc';
-import { List } from '../../../fields/List';
+import { DocData } from '../../../fields/DocSymbols';
import { RichTextField } from '../../../fields/RichTextField';
-import { DocCast, NumCast } from '../../../fields/Types';
+import { Cast, DocCast, NumCast } from '../../../fields/Types';
+import { Gestures } from '../../../pen-gestures/GestureTypes';
import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT';
import { DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
import { DocumentManager } from '../../util/DocumentManager';
import { LinkManager } from '../../util/LinkManager';
+import { undoable } from '../../util/UndoManager';
import { ViewBoxAnnotatableComponent } from '../DocComponent';
import { InkingStroke } from '../InkingStroke';
import './DiagramBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
@observer
export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(DiagramBox, fieldKey);
}
- private _ref: React.RefObject<HTMLDivElement> = React.createRef();
- private _dragRef = React.createRef<HTMLDivElement>();
+ static isPointInBox = (box: Doc, pt: number[]): boolean => {
+ if (typeof pt[0] === 'number' && typeof box.x === 'number' && typeof box.y === 'number' && typeof pt[1] === 'number') {
+ return pt[0] < box.x + NumCast(box.width) && pt[0] > box.x && pt[1] > box.y && pt[1] < box.y + NumCast(box.height);
+ }
+ return false;
+ };
+
constructor(props: FieldViewProps) {
super(props);
makeObservable(this);
}
- @observable inputValue = '';
- @observable loading = false;
- @observable errorMessage = '';
- @observable mermaidCode = '';
+ @observable _showCode = false;
+ @observable _inputValue = '';
+ @observable _generating = false;
+ @observable _errorMessage = '';
- @action handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- this.inputValue = e.target.value;
- };
- async componentDidMount() {
+ @computed get mermaidcode() {
+ return Cast(this.Document[DocData].text, RichTextField, null)?.Text ?? '';
+ }
+
+ componentDidMount() {
this._props.setContentViewBox?.(this);
mermaid.initialize({
securityLevel: 'loose',
startOnLoad: true,
flowchart: { useMaxWidth: true, htmlLabels: true, curve: 'cardinal' },
});
- this.mermaidCode = 'asdasdasd';
- const docArray: Doc[] = DocListCast(this.Document.data);
- let mermaidCodeDoc = docArray.filter(doc => doc.type === 'rich text');
- mermaidCodeDoc = mermaidCodeDoc.filter(doc => (doc.text as RichTextField).Text === 'mermaidCodeTitle');
- if (mermaidCodeDoc[0]) {
- if (typeof mermaidCodeDoc[0].title === 'string') {
- console.log(mermaidCodeDoc[0].title);
- if (mermaidCodeDoc[0].title !== '') {
- this.renderMermaidAsync(mermaidCodeDoc[0].title);
- }
- }
- }
- // this will create a text doc far away where the user cant to save the mermaid code, where it will then be accessed when flipped to the diagram box side
- // the code is stored in the title since it is much easier to change than in the text
- else {
- DocumentManager.Instance.AddViewRenderedCb(this.Document, docViewForYourCollection => {
- if (docViewForYourCollection && docViewForYourCollection.ComponentView) {
- if (docViewForYourCollection.ComponentView.addDocument && docViewForYourCollection.ComponentView.removeDocument) {
- const newDoc = Docs.Create.TextDocument('mermaidCodeTitle', { title: '', x: 9999 + NumCast(this.layoutDoc._width), y: 9999 });
- docViewForYourCollection.ComponentView?.addDocument(newDoc);
- }
- }
- });
- }
- console.log(this.Document.title);
- // this is so that ever time a new doc, text node or ink node, is created, this.createMermaidCode will run which will create a save
+ // when a new doc/text/ink/shape is created in the freeform view, this generates the corresponding mermaid diagram code
reaction(
() => DocListCast(this.Document.data),
- () => this.convertDrawingToMermaidCode(),
+ docArray => docArray.length && this.convertDrawingToMermaidCode(docArray),
{ fireImmediately: true }
);
}
- renderMermaid = async (str: string) => {
+ renderMermaid = (str: string) => {
try {
- const { svg, bindFunctions } = await this.mermaidDiagram(str);
- return { svg, bindFunctions };
+ return mermaid.render('graph' + Date.now(), str);
} catch (error) {
- console.error('Error rendering mermaid diagram:', error);
return { svg: '', bindFunctions: undefined };
}
};
- mermaidDiagram = async (str: string) => mermaid.render('graph' + Date.now(), str);
- async renderMermaidAsync(mermaidCode: string) {
+ renderMermaidAsync = async (mermaidCode: string, dashDiv: HTMLDivElement) => {
try {
const { svg, bindFunctions } = await this.renderMermaid(mermaidCode);
- const dashDiv = document.getElementById('dashDiv' + this.Document.title);
- if (dashDiv) {
- dashDiv.innerHTML = svg;
- if (bindFunctions) {
- bindFunctions(dashDiv);
- }
- }
+ dashDiv.innerHTML = svg;
+ bindFunctions?.(dashDiv);
} catch (error) {
console.error('Error rendering Mermaid:', error);
}
- }
- @action handleRenderClick = () => {
- this.generateMermaidCode();
};
- @action async generateMermaidCode() {
- console.log('Generating Mermaid Code');
- this.loading = true;
- let prompt = '';
- // let docArray: Doc[] = DocListCast(this.Document.data);
- // let mermaidCodeDoc = docArray.filter(doc => doc.type == 'rich text')
- // mermaidCodeDoc=mermaidCodeDoc.filter(doc=>(doc.text as RichTextField).Text=='mermaidCodeTitle')
- // if(mermaidCodeDoc[0]){
- // console.log(mermaidCodeDoc[0].title)
- // if(typeof mermaidCodeDoc[0].title=='string'){
- // console.log(mermaidCodeDoc[0].title)
- // if(mermaidCodeDoc[0].title!=""){
- // prompt="Edit this code "+this.inputValue+": "+mermaidCodeDoc[0].title
- // console.log("you have to see me")
- // }
- // }
- // }
- // else{
- prompt = 'Write this in mermaid code and only give me the mermaid code: ' + this.inputValue;
- console.log('there is no text save');
- // }
- const res = await gptAPICall(prompt, GPTCallType.MERMAID);
- this.loading = false;
- if (res === 'Error connecting with API.') {
- // If GPT call failed
- console.error('GPT call failed');
- this.errorMessage = 'GPT call failed; please try again.';
- } else if (res !== null) {
- // If GPT call succeeded, set htmlCode;;; TODO: check if valid html
- if (this.isValidCode(res)) {
- this.mermaidCode = res;
- console.log('GPT call succeeded:' + res);
- this.errorMessage = '';
- } else {
- console.error('GPT call succeeded but invalid html; please try again.');
- this.errorMessage = 'GPT call succeeded but invalid html; please try again.';
- }
- }
- this.renderMermaidAsync.call(this, this.removeWords(this.mermaidCode));
- this.loading = false;
- }
- isValidCode = (html: string) => true;
- removeWords(inputStrIn: string) {
- const inputStr = inputStrIn.replace('```mermaid', '');
- return inputStr.replace('```', '');
- }
+
+ setMermaidCode = undoable((res: string) => {
+ this.Document[DocData].text = new RichTextField(
+ JSON.stringify({
+ doc: {
+ type: 'doc',
+ content: [
+ {
+ type: 'code_block',
+ content: [
+ { type: 'text', text: `^@mermaids\n` },
+ { type: 'text', text: this.removeWords(res) },
+ ],
+ },
+ ],
+ },
+ selection: { type: 'text', anchor: 1, head: 1 },
+ }),
+ res
+ );
+ }, 'set mermaid code');
+
+ generateMermaidCode = action(() => {
+ this._generating = true;
+ const prompt = 'Write this in mermaid code and only give me the mermaid code: ' + this._inputValue;
+ gptAPICall(prompt, GPTCallType.MERMAID).then(
+ action(res => {
+ this._generating = false;
+ if (res === 'Error connecting with API.') {
+ this._errorMessage = 'GPT call failed; please try again.';
+ }
+ // If GPT call succeeded, set mermaid code on Doc which will trigger a rendering if _showCode is false
+ else if (res && this.isValidCode(res)) {
+ this.setMermaidCode(res);
+ this._errorMessage = '';
+ } else {
+ this._errorMessage = 'GPT call succeeded but invalid html; please try again.';
+ }
+ })
+ );
+ });
+ isValidCode = (html: string) => (html ? true : false);
+ removeWords = (inputStrIn: string) => inputStrIn.replace('```mermaid', '').replace(`^@mermaids`, '').replace('```', '');
+
// method to convert the drawings on collection node side the mermaid code
- async convertDrawingToMermaidCode() {
- let mermaidCode = '';
- let diagramExists = false;
- if (this.Document.data instanceof List) {
- const docArray: Doc[] = DocListCast(this.Document.data);
- const rectangleArray = docArray.filter(doc => doc.title === 'rectangle' || doc.title === 'circle');
- const lineArray = docArray.filter(doc => doc.title === 'line' || doc.title === 'stroke');
- const textArray = docArray.filter(doc => doc.type === 'rich text');
- const timeoutPromise = () =>
- new Promise(resolve => {
- setTimeout(resolve, 0);
- });
- await timeoutPromise();
- const inkStrokeArray = lineArray.map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())).filter(inkView => inkView?.ComponentView instanceof InkingStroke);
- console.log(inkStrokeArray.length);
- console.log(lineArray.length);
- if (inkStrokeArray[0] && inkStrokeArray.length === lineArray.length) {
- mermaidCode = 'graph TD;';
- const inkingStrokeArray = inkStrokeArray.map(stroke => stroke?.ComponentView);
- for (let i = 0; i < rectangleArray.length; i++) {
- const rectangle = rectangleArray[i];
- for (let j = 0; j < lineArray.length; j++) {
- const inkScaleX = (inkingStrokeArray[j] as InkingStroke)?.inkScaledData().inkScaleX;
- const inkScaleY = (inkingStrokeArray[j] as InkingStroke)?.inkScaledData().inkScaleY;
- const inkStrokeXArray = (inkingStrokeArray[j] as InkingStroke)
- ?.inkScaledData()
- .inkData.map(coord => coord.X)
- .map(doc => doc * inkScaleX);
- const inkStrokeYArray = (inkingStrokeArray[j] as InkingStroke)
- ?.inkScaledData()
- .inkData.map(coord => coord.Y)
- .map(doc => doc * inkScaleY);
- console.log(inkingStrokeArray.length);
- console.log(lineArray.length);
- // need to minX and minY to since the inkStroke.x and.y is not relative to the doc. so I have to do some calcluations
- const minX: number = Math.min(...inkStrokeXArray);
- const minY: number = Math.min(...inkStrokeYArray);
- const startX = inkStrokeXArray[0] - minX + (lineArray[j]?.x as number);
- const startY = inkStrokeYArray[0] - minY + (lineArray[j]?.y as number);
- const endX = inkStrokeXArray[inkStrokeXArray.length - 1] - minX + (lineArray[j].x as number);
- const endY = inkStrokeYArray[inkStrokeYArray.length - 1] - minY + (lineArray[j].y as number);
- if (this.isPointInBox(rectangle, [startX, startY])) {
- for (let k = 0; k < rectangleArray.length; k++) {
- const rectangle2 = rectangleArray[k];
- if (this.isPointInBox(rectangle2, [endX, endY]) && typeof rectangle.x === 'number' && typeof rectangle2.x === 'number') {
- diagramExists = true;
- const linkedDocs: Doc[] = LinkManager.Instance.getAllRelatedLinks(lineArray[j]).map(d => DocCast(LinkManager.getOppositeAnchor(d, lineArray[j])));
- console.log(linkedDocs.length);
- if (linkedDocs.length !== 0) {
- const linkedText = (linkedDocs[0].text as RichTextField).Text;
- mermaidCode += Math.abs(rectangle.x) + this.getTextInBox(rectangle, textArray) + '-->|' + linkedText + '|' + Math.abs(rectangle2.x) + this.getTextInBox(rectangle2, textArray) + ';';
- } else {
- mermaidCode += Math.abs(rectangle.x) + this.getTextInBox(rectangle, textArray) + '-->' + Math.abs(rectangle2.x) + this.getTextInBox(rectangle2, textArray) + ';';
- }
- }
+ convertDrawingToMermaidCode = async (docArray: Doc[]) => {
+ const rectangleArray = docArray.filter(doc => doc.title === Gestures.Rectangle || doc.title === Gestures.Circle);
+ const lineArray = docArray.filter(doc => doc.title === Gestures.Line || doc.title === Gestures.Stroke);
+ const textArray = docArray.filter(doc => doc.type === DocumentType.RTF);
+ await new Promise(resolve => setTimeout(resolve));
+ const inkStrokeArray = lineArray.map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())).filter(inkView => inkView?.ComponentView instanceof InkingStroke);
+ if (inkStrokeArray[0] && inkStrokeArray.length === lineArray.length) {
+ let mermaidCode = `graph TD \n`;
+ const inkingStrokeArray = inkStrokeArray.map(stroke => stroke?.ComponentView as InkingStroke).filter(stroke => stroke);
+ for (const rectangle of rectangleArray) {
+ for (const inkStroke of inkingStrokeArray) {
+ const inkData = inkStroke.inkScaledData();
+ const { inkScaleX, inkScaleY } = inkData;
+ const inkStrokeXArray = inkData.inkData.map(coord => coord.X * inkScaleX);
+ const inkStrokeYArray = inkData.inkData.map(coord => coord.Y * inkScaleY);
+ // need to minX and minY to since the inkStroke.x and.y is not relative to the doc. so I have to do some calcluations
+ const offX = Math.min(...inkStrokeXArray) - NumCast(inkStroke.Document.x);
+ const offY = Math.min(...inkStrokeYArray) - NumCast(inkStroke.Document.y);
+
+ const startX = inkStrokeXArray[0] - offX;
+ const startY = inkStrokeYArray[0] - offY;
+ const endX = inkStrokeXArray.lastElement() - offX;
+ const endY = inkStrokeYArray.lastElement() - offY;
+ if (DiagramBox.isPointInBox(rectangle, [startX, startY])) {
+ for (const rectangle2 of rectangleArray) {
+ if (DiagramBox.isPointInBox(rectangle2, [endX, endY])) {
+ const linkedDocs = LinkManager.Instance.getAllRelatedLinks(inkStroke.Document).map(d => DocCast(LinkManager.getOppositeAnchor(d, inkStroke.Document)));
+ const linkedDocText = Cast(linkedDocs[0]?.text, RichTextField, null)?.Text;
+ const linkText = linkedDocText ? `|${linkedDocText}|` : '';
+ mermaidCode += ' ' + Math.abs(NumCast(rectangle.x)) + this.getTextInBox(rectangle, textArray) + '-->' + linkText + Math.abs(NumCast(rectangle2.x)) + this.getTextInBox(rectangle2, textArray) + `\n`;
}
}
}
}
- // this will save the text
- DocumentManager.Instance.AddViewRenderedCb(this.Document, docViewForYourCollection => {
- if (docViewForYourCollection && docViewForYourCollection.ComponentView) {
- if (docViewForYourCollection.ComponentView.addDocument && docViewForYourCollection.ComponentView.removeDocument) {
- let docs: Doc[] = DocListCast(this.Document.data);
- docs = docs.filter(doc => doc.type === 'rich text');
- const mermaidCodeDoc = docs.filter(doc => (doc.text as RichTextField).Text === 'mermaidCodeTitle');
- if (mermaidCodeDoc[0]) {
- if (diagramExists) {
- mermaidCodeDoc[0].title = mermaidCode;
- } else {
- mermaidCodeDoc[0].title = '';
- }
- }
- }
- }
- });
+ this.setMermaidCode(mermaidCode);
}
}
- }
- testInkingStroke = () => {
- if (this.Document.data instanceof List) {
- const docArray: Doc[] = DocListCast(this.Document.data);
- const lineArray = docArray.filter(doc => doc.title === 'line' || doc.title === 'stroke');
- setTimeout(() => {
- const inkStrokeArray = lineArray.map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())).filter(inkView => inkView?.ComponentView instanceof InkingStroke);
- console.log(inkStrokeArray);
- });
- }
};
- getTextInBox = (box: Doc, richTextArray: Doc[]): string => {
- for (let i = 0; i < richTextArray.length; i++) {
- const textDoc = richTextArray[i];
- if (typeof textDoc.x === 'number' && typeof textDoc.y === 'number' && typeof box.x === 'number' && typeof box.height === 'number' && typeof box.width === 'number' && typeof box.y === 'number') {
- if (textDoc.x > box.x && textDoc.x < box.x + box.width && textDoc.y > box.y && textDoc.y < box.y + box.height) {
- if (box.title === 'rectangle') {
- return '(' + ((textDoc.text as RichTextField)?.Text ?? '') + ')';
- }
- if (box.title === 'circle') {
- return '((' + ((textDoc.text as RichTextField)?.Text ?? '') + '))';
- }
- }
+
+ getTextInBox = (box: Doc, richTextArray: Doc[]) => {
+ for (const textDoc of richTextArray) {
+ if (DiagramBox.isPointInBox(box, [NumCast(textDoc.x), NumCast(textDoc.y)])) {
+ switch (box.title) {
+ case Gestures.Rectangle: return '(' + ((textDoc.text as RichTextField)?.Text ?? '') + ')';
+ case Gestures.Circle: return '((' + ((textDoc.text as RichTextField)?.Text ?? '') + '))';
+ default:
+ } // prettier-ignore
}
}
return '( )';
};
- isPointInBox = (box: Doc, line: number[]): boolean => {
- if (typeof line[0] === 'number' && typeof box.x === 'number' && typeof box.width === 'number' && typeof box.height === 'number' && typeof box.y === 'number' && typeof line[1] === 'number') {
- return line[0] < box.x + box.width && line[0] > box.x && line[1] > box.y && line[1] < box.y + box.height;
- }
- return false;
- };
render() {
return (
- <div ref={this._ref} className="DIYNodeBox">
- <div ref={this._dragRef} className="DIYNodeBox-wrapper">
- <div className="search-bar">
- <input type="text" value={this.inputValue} onChange={this.handleInputChange} />
- <button type="button" onClick={this.handleRenderClick}>
- Generate
- </button>
- </div>
- <div className="content">
- {this.mermaidCode ? (
- <div id={'dashDiv' + this.Document.title} className="diagramBox" />
- ) : (
- <div>{this.loading ? <div className="loading-circle" /> : <div>{this.errorMessage ? this.errorMessage : 'Insert prompt to generate diagram'}</div>}</div>
- )}
- </div>
+ <div className="DIYNodeBox">
+ <div className="DIYNodeBox-searchbar">
+ <input type="text" value={this._inputValue} onKeyDown={action(e => e.key === 'Enter' && this.generateMermaidCode())} onChange={action(e => (this._inputValue = e.target.value))} />
+ <button type="button" onClick={this.generateMermaidCode}>
+ Gen
+ </button>
+ <input type="checkbox" onClick={action(() => (this._showCode = !this._showCode))} />
+ </div>
+ <div className="DIYNodeBox-content">
+ {this._showCode ? (
+ <FormattedTextBox {...this._props} fieldKey="text" />
+ ) : this._generating ? (
+ <div className="loading-circle" />
+ ) : (
+ <div className="diagramBox" ref={r => r && this.renderMermaidAsync.call(this, this.removeWords(this.mermaidcode), r)}>
+ {this._errorMessage || 'Type a prompt to generate a diagram'}
+ </div>
+ )}
</div>
</div>
);
@@ -286,6 +200,14 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
Docs.Prototypes.TemplateMap.set(DocumentType.DIAGRAM, {
- layout: { view: DiagramBox, dataField: 'dadta' },
- options: { _height: 300, _layout_fitWidth: true, _layout_nativeDimEditable: true, _layout_reflowVertical: true, waitForDoubleClickToClick: 'always', systemIcon: 'BsGlobe' },
+ layout: { view: DiagramBox, dataField: 'data' },
+ options: {
+ _height: 300, //
+ _layout_fitWidth: true,
+ _layout_nativeDimEditable: true,
+ _layout_reflowVertical: true,
+ _layout_reflowHorizontal: true,
+ waitForDoubleClickToClick: 'always',
+ systemIcon: 'BsGlobe',
+ },
});
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index e21902fdd..a88bd8920 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1464,7 +1464,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
} else if (!FormattedTextBox.DontSelectInitialText) {
const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
selectAll(this._editorView.state, (tx: Transaction) => {
- this._editorView?.dispatch(tx.deleteSelection().addStoredMark(mark));
+ this._editorView?.dispatch(tx.addStoredMark(mark));
});
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
} else {
@@ -2090,6 +2090,7 @@ Docs.Prototypes.TemplateMap.set(DocumentType.RTF, {
_layout_nativeDimEditable: true,
_layout_reflowVertical: true,
_layout_reflowHorizontal: true,
+ _layout_noSidebar: true,
defaultDoubleClick: 'ignore',
systemIcon: 'BsFileEarmarkTextFill',
},