aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/apis/gpt/GPT.ts21
-rw-r--r--src/client/views/nodes/DiagramBox.scss88
-rw-r--r--src/client/views/nodes/DiagramBox.tsx192
3 files changed, 202 insertions, 99 deletions
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 49c56f04e..027c10e28 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -1,4 +1,5 @@
import { ClientOptions, OpenAI } from 'openai';
+import { ChatCompletionMessageParam } from 'openai/resources';
enum GPTCallType {
SUMMARY = 'summary',
@@ -18,11 +19,11 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
summary: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: 'Summarize this text in simpler terms: ' },
edit: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: 'Reword this: ' },
completion: { model: 'gpt-3.5-turbo-instruct', maxTokens: 256, temp: 0.5, prompt: '' },
- mermaid:{model:'gpt-3.5-turbo-instruct',maxTokens:256,temp:0,prompt:"Write this in mermaid code (keep in note thatPut this at the end of the code to add colors heres an example and be very care about the order:pie title Example Pie Chart \"Red\" : 25 \"Blue\" : 75%%{init: {'theme': 'base', 'themeVariables': { 'pie1': '#0000FF', 'pie2': '#FF0000'}}}%%): "}
+ mermaid:{model:'gpt-4-turbo',maxTokens:2048,temp:0,prompt:"Write this in mermaid code and only give me the mermaid code (Heres an example of changing color of a pie chart to help you pie title Example \"Red\": 20 \"Blue\": 50 \"Green\": 30 %%{init: {'theme': 'base', 'themeVariables': {'pie1': '#0000FF', 'pie2': '#00FF00', 'pie3': '#FF0000'}}}%% keep in mind that pie1 is the highest since its sorted in descending order. Heres an example of a mindmap: mindmap root((mindmap)) Origins Long history ::icon(fa fa-book) Popularisation British popular psychology author Tony Buzan Research On effectivness<br/>and features On Automatic creation Uses Creative techniques Strategic planning Argument mapping Tools Pen and paper Mermaid. "}
};
-/**
+/**`
* Calls the OpenAI API.
*
* @param inputText Text to process
@@ -37,14 +38,20 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => {
dangerouslyAllowBrowser: true,
};
const openai = new OpenAI(configuration);
- const response = await openai.completions.create({
+
+ let messages: ChatCompletionMessageParam[] = [
+ { role: 'system', content: opts.prompt },
+ { role: 'user', content: inputText },
+ ];
+
+ const response = await openai.chat.completions.create({
model: opts.model,
- max_tokens: opts.maxTokens,
+ messages: messages,
temperature: opts.temp,
- prompt: `${opts.prompt}${inputText}`,
-
+ max_tokens: opts.maxTokens,
});
- return response.choices[0].text;
+ const content = response.choices[0].message.content;
+ return content;
} catch (err) {
console.log(err);
return 'Error connecting with API.';
diff --git a/src/client/views/nodes/DiagramBox.scss b/src/client/views/nodes/DiagramBox.scss
index e69de29bb..d2749f1ad 100644
--- a/src/client/views/nodes/DiagramBox.scss
+++ b/src/client/views/nodes/DiagramBox.scss
@@ -0,0 +1,88 @@
+.DIYNodeBox {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ 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;
+
+ }
+ }
+
+ .search-bar {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ padding: 10px;
+
+ input[type="text"] {
+ flex: 1;
+ margin-right: 10px;
+ }
+
+ button {
+ padding: 5px 10px;
+ }
+ }
+
+ .content {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width:100%;
+ height:100%;
+ .diagramBox{
+ 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%;
+ }
+ }
+ }
+
+ .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);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DiagramBox.tsx b/src/client/views/nodes/DiagramBox.tsx
index 64d0d6d78..902134fb7 100644
--- a/src/client/views/nodes/DiagramBox.tsx
+++ b/src/client/views/nodes/DiagramBox.tsx
@@ -12,18 +12,30 @@ import { List } from '../../../fields/List';
import { RichTextField } from '../../../fields/RichTextField';
import { ContextMenu } from '../ContextMenu';
import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT';
+import { ChatCompletionMessageParam } from 'openai/resources/chat/completions';
+import OpenAI, { ClientOptions } from 'openai';
@observer
export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(DiagramBox, fieldKey);
}
+ private _ref: React.RefObject<HTMLDivElement> = React.createRef();
+ private _dragRef = React.createRef<HTMLDivElement>();
constructor(props: FieldViewProps) {
super(props);
makeObservable(this);
//this.chartContent = 'flowchart LR;A-->B:::wide;B-->C:::wide; B-->D[hello];';
}
+ @observable inputValue = '';
+ @observable loading = false;
+ @observable errorMessage = '';
+ @observable mermaidCode = '';
+
+ @action handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this.inputValue = e.target.value;
+ };
componentDidMount() {
this._props.setContentViewBox?.(this);
mermaid.initialize({
@@ -31,112 +43,91 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
startOnLoad: true,
flowchart: { useMaxWidth: true, htmlLabels: true, curve: 'cardinal' },
});
+ }
+ renderMermaid = async (str:string) => {
+ try {
+ const { svg, bindFunctions } = await this.mermaidDiagram(str);
+ return { svg, bindFunctions };
+ } catch (error) {
+ console.error('Error rendering mermaid diagram:', error);
+ return { svg: '', bindFunctions: undefined };
+ }
+ };
+ mermaidDiagram = async (str:string) => {
+ return await mermaid.render('graph' + Date.now(), str);
+ };
- const renderMermaid = async (str: string) => {
- try {
- const { svg, bindFunctions } = await mermaidDiagram(str);
- return { svg, bindFunctions };
- } catch (error) {
- console.error('Error rendering mermaid diagram:', error);
- return { svg: '', bindFunctions: undefined };
- }
- };
- const mermaidDiagram = async (str: string) => {
- return await mermaid.render('graph' + Date.now(), str);
- };
- async function renderMermaidAsync(this: DiagramBox) {
- try {
- const mermaidCode: string = await this.createMermaidCode();
- const { svg, bindFunctions } = await renderMermaid(mermaidCode);
- const dashDiv = document.getElementById('dashDiv');
- if (dashDiv) {
- dashDiv.innerHTML = svg;
- if (bindFunctions) {
- bindFunctions(dashDiv);
- }
+ async renderMermaidAsync(mermaidCode:string) {
+ try {
+ const { svg, bindFunctions } = await this.renderMermaid(mermaidCode);
+ const dashDiv = document.getElementById('dashDiv');
+ if (dashDiv) {
+ dashDiv.innerHTML = svg;
+ if (bindFunctions) {
+ bindFunctions(dashDiv);
}
- } catch (error) {
- console.error('Error rendering Mermaid:', error);
}
+ } catch (error) {
+ console.error('Error rendering Mermaid:', error);
}
-
- renderMermaidAsync.call(this);
}
+ @action handleRenderClick = () => {
+ // Call the GPT model and get the HTML output
+ // const modelOutput = getHtmlOutput(this.inputValue);
+ // this.htmlCode = modelOutput;
+ this.generateMermaidCode();
+ };
+ @action generateMermaidCode=async()=>{
+ console.log('Generating Mermaid Code');
+ this.loading = true;
- askGPT = async (): Promise<string | undefined> => {
- try {
- let notes = 'mermaid syntax for pie charts do not include %';
- let text = notes + (DocListCast(this.Document.data)[0].text as RichTextField)?.Text;
- if (!text) {
- console.error('Text extraction failed');
- return;
- }
-
- let res = await gptAPICall(text, GPTCallType.MERMAID);
- if (!res) {
- console.error('GPT call failed');
- return;
+ let res = await gptAPICall(this.inputValue, 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.log(res)
- return this.removeWords(res);
+ console.error('GPT call succeeded but invalid html; please try again.');
+ this.errorMessage = 'GPT call succeeded but invalid html; please try again.';
}
- } catch (err) {
- console.error('Error:', err);
- return;
}
+ this.renderMermaidAsync.call(this,this.removeWords(this.mermaidCode));
+ this.loading = false;
+ }
+ isValidCode = (html: string) => {
+ return true;
};
+
removeWords(inputStr:string) {
- let presetArray=["pie","flowchart","diagramChart","graph TD","flowchart TD","flowchart LR","graph LR"]
- const lines = inputStr.split('\n');
- let foundPresetWord = false;
- let result = '';
-
- for (const line of lines) {
- if (!foundPresetWord) {
- for (const word of presetArray) {
- if (line.toLowerCase().includes(word.toLowerCase())) {
- foundPresetWord = true;
- result += line + '\n';
- break;
- }
- }
- } else {
- result += line + '\n';
- }
- }
-
- return result;
+ inputStr=inputStr.replace("```mermaid","")
+ return inputStr.replace("```","");
}
+
createMermaidCode = async (): Promise<string> => {
- let mermaidCode = '';
- let docArray: Doc[] = DocListCast(this.Document.data);
- if (docArray.length === 1) {
- if (docArray[0].type == 'rich text') {
- const gptResponse = await this.askGPT();
- console.log(gptResponse);
- if (gptResponse) {
- mermaidCode = gptResponse;
- }
- }
- } else {
- let mermaidCode = 'graph LR;';
- if (this.Document.data instanceof List) {
- let docArray: Doc[] = DocListCast(this.Document.data);
- let rectangleArray = docArray.filter(doc => doc.title == 'rectangle' || doc.title == 'circle');
- let lineArray = docArray.filter(doc => doc.title == 'line' || doc.title == 'stroke');
- let textArray = docArray.filter(doc => doc.type == 'rich text');
- for (let i = 0; i < rectangleArray.length; i++) {
- const rectangle = rectangleArray[i];
- for (let j = 0; j < lineArray.length; j++) {
- const line = lineArray[j];
- if (this.isLineInFirstBox(rectangle, line)) {
- for (let k = 0; k < rectangleArray.length; k++) {
- const rectangle2 = rectangleArray[k];
- if (this.isLineInSecondBox(rectangle2, line) && typeof rectangle.x === 'number' && typeof rectangle2.x === 'number') {
- mermaidCode += Math.abs(rectangle.x) + this.getTextInBox(rectangle, textArray) + '---' + Math.abs(rectangle2.x) + this.getTextInBox(rectangle2, textArray) + ';';
- }
+ let mermaidCode = 'graph LR;';
+ if (this.Document.data instanceof List) {
+ let docArray: Doc[] = DocListCast(this.Document.data);
+ let rectangleArray = docArray.filter(doc => doc.title == 'rectangle' || doc.title == 'circle');
+ let lineArray = docArray.filter(doc => doc.title == 'line' || doc.title == 'stroke');
+ let textArray = docArray.filter(doc => doc.type == 'rich text');
+ for (let i = 0; i < rectangleArray.length; i++) {
+ const rectangle = rectangleArray[i];
+ for (let j = 0; j < lineArray.length; j++) {
+ const line = lineArray[j];
+ if (this.isLineInFirstBox(rectangle, line)) {
+ for (let k = 0; k < rectangleArray.length; k++) {
+ const rectangle2 = rectangleArray[k];
+ if (this.isLineInSecondBox(rectangle2, line) && typeof rectangle.x === 'number' && typeof rectangle2.x === 'number') {
+ mermaidCode += Math.abs(rectangle.x) + this.getTextInBox(rectangle, textArray) + '---' + Math.abs(rectangle2.x) + this.getTextInBox(rectangle2, textArray) + ';';
}
}
}
@@ -179,6 +170,23 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
};
render() {
- return <div id="dashDiv" className="diagramBox"></div>;
+ console.log(this.loading)
+ 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 onClick={this.handleRenderClick}>Generate</button>
+ </div>
+ <div className="content">
+ {this.mermaidCode ? (
+ <div id="dashDiv" className="diagramBox"></div>
+ ) : (
+ <div>{this.loading ? <div className="loading-circle"></div> : <div>{this.errorMessage ? this.errorMessage : 'Insert prompt to generate diagram'}</div>}</div>
+ )}
+ </div>
+ </div>
+ </div>
+ );
}
}