diff options
author | Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> | 2024-08-08 10:38:35 -0400 |
---|---|---|
committer | Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> | 2024-08-08 10:38:35 -0400 |
commit | 4949c68f38049da55ceaa3e95ea155f38d6a3748 (patch) | |
tree | e32645b6c0df8a6ef3aec0595d4f2c44e5d367ed /src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx | |
parent | 9119b9f08f62bb5228206487b047d6e1e75d5e9e (diff) |
UI for GPT templates in doccreator menu
Diffstat (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx')
-rw-r--r-- | src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx | 147 |
1 files changed, 111 insertions, 36 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index 411257ff7..781cbcb55 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -3,7 +3,7 @@ import { IReactionDisposer, ObservableMap, action, computed, makeObservable, obs import { observer } from 'mobx-react'; import * as React from 'react'; import { returnAll, returnFalse, setupMoveUpEvents } from '../../../../ClientUtils'; -import { Doc, NumListCast } from '../../../../fields/Doc'; +import { Doc, NumListCast, StrListCast } from '../../../../fields/Doc'; import { DocCast, ImageCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; import { emptyFunction } from '../../../../Utils'; @@ -17,6 +17,9 @@ import { Id } from '../../../../fields/FieldSymbols'; import { Colors, IconButton, Size } from 'browndash-components'; import { MakeTemplate } from '../../../util/DropConverter'; import { DragManager } from '../../../util/DragManager'; +import { GPTCallType, gptAPICall } from '../../../apis/gpt/GPT'; +import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; +import { Docs } from '../../../documents/Documents'; export enum LayoutType { Stacked = 'stacked', @@ -41,6 +44,10 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { @observable _layoutPreviewScale: number = 1; @observable _savedLayouts: DataVizTemplateLayout[] = []; + @observable _GPTTemplates: Doc[] | undefined = undefined; + @observable _GPTOpt: boolean = false; + @observable _userPrompt: string = ''; + @observable _pageX: number = 0; @observable _pageY: number = 0; @observable _indicatorX: number | undefined = undefined; @@ -105,6 +112,10 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } } + @computed get selectedFields(){ + return StrListCast(this._dataViz?.layoutDoc._dataViz_axes); + } + @computed get canMakeDocs(){ return this._selectedTemplate !== undefined && this._layout !== undefined; } @@ -254,8 +265,8 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { if (this._initDimensions.y === undefined) this._initDimensions.y = this._pageY; const {height, width, x, y} = this._initDimensions; - this._menuDimensions.width = Math.max(100, scale.x * width); - this._menuDimensions.height = Math.max(100, scale.y * height); + this._menuDimensions.width = Math.max(300, scale.x * width); + this._menuDimensions.height = Math.max(200, scale.y * height); this._pageX = x + translation.x; this._pageY = y + translation.y; }; @@ -293,10 +304,104 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { && this._layout.columns === layout.columns; } + generateTemplates = async (inputText: string) => { + let prompt: string = 'Please generate for the fields:'; + this.selectedFields?.forEach(field => prompt += ` ${field},`) + prompt += ` Additional prompt: ${inputText}`; + + try { + const res = await gptAPICall(prompt, GPTCallType.TEMPLATE); + + if (res) { + const templates: {template_type: string, fieldVals: {title: string, tlx: string, tly: string, brx: string, bry: string}[]}[] = JSON.parse(res); + this.createGeneratedTemplates(templates, 500, 500); + } + } catch (err) { + console.error(err); + } + + } + + createGeneratedTemplates = (layouts: {template_type: string, fieldVals: {title: string, tlx: string, tly: string, brx: string, bry: string}[]}[], tempWidth: number, tempHeight: number) => { + const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; + + layouts.forEach(layout => { + const fields: Doc[] = layout.fieldVals.map(field => { + const left: number = Number(field.tlx) * tempWidth / 2; const top: number = Number(field.tly) * tempHeight / 2; //prettier-ignore + const right: number = Number(field.brx) * tempWidth / 2; const bottom: number = Number(field.bry) * tempHeight / 2; //prettier-ignore + const height = bottom - top; + const width = right - left; + const doc = !field.title.includes('$$') ? Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, x: left, y: top, _text_fontSize: `${height/2}` }) : Docs.Create.ImageDocument('', { _height: height, _width: width, title: field.title.replace(/$$/g, ''), x: left, y: top }); + return doc; + }); + + const template = Docs.Create.FreeformDocument(fields, { _height: tempHeight, _width: tempWidth, title: layout.template_type, x: 400, y: 400 }); + mainCollection.addDocument(template); + }); + } + get templatesPreviewContents(){ const renderedTemplates: Doc[] = []; + + const GPTOptions = + <div></div> + + return ( - <div className='docCreatorMenu-preview-container' + <div className='docCreatorMenu-templates-view'> + <div className='docCreatorMenu-section'> + <div className='docCreatorMenu-section-topbar'> + <div className='docCreatorMenu-section-title'>Suggested Templates</div> + <button className='docCreatorMenu-menu-button section-reveal-options' onPointerDown={e => this.setUpButtonClick(e, () => this._GPTOpt = !this._GPTOpt)}> + <FontAwesomeIcon icon='gear'/> + </button> + </div> + <div className='docCreatorMenu-GPT-templates-preview'> + <div className='docCreatorMenu-preview-window empty GPT'></div> + <div className='docCreatorMenu-preview-window empty GPT'></div> + <div className='docCreatorMenu-preview-window empty GPT'></div> + <div className='docCreatorMenu-preview-window empty GPT'></div> + </div> + {this._GPTOpt ? (<div className='docCreatorMenu-GPT-options'> + <div className='docCreatorMenu-GPT-options-container'> + <button className='docCreatorMenu-menu-button' onPointerDown={e => this.setUpButtonClick(e, () => this.generateTemplates(this._userPrompt))}> + <FontAwesomeIcon icon='plus'/> + </button> + <input className='docCreatorMenu-GPT-prompt-input'/> + </div> + {this._GPTOpt ? GPTOptions : null} + </div>) : null} + </div> + <hr className='docCreatorMenu-option-divider'/> + <div className='docCreatorMenu-section'> + <div className='docCreatorMenu-section-topbar'> + <div className='docCreatorMenu-section-title'>Your Templates</div> + <button className='docCreatorMenu-menu-button section-reveal-options' onPointerDown={e => this.setUpButtonClick(e, () => this._GPTOpt = !this._GPTOpt)}> + <FontAwesomeIcon icon='gear'/> + </button> + </div> + <div className='docCreatorMenu-GPT-templates-preview'> + <div className='docCreatorMenu-preview-window empty' + onPointerDown={e => this.setUpButtonClick(e, this.basicTemplateTest)} + > + <FontAwesomeIcon icon='plus' color='rgb(160, 160, 160)'/> + </div> + {this._templateDocs.map(doc => ({icon: ImageCast(doc.icon), doc})).filter(info => info.icon && info.doc).map(info => { + if (renderedTemplates.includes(info.doc)) return undefined; + renderedTemplates.push(info.doc); + return (<div + className='docCreatorMenu-preview-window' + style={{ + border: this._selectedTemplate === info.doc ? `solid 3px ${Colors.MEDIUM_BLUE}` : '', + boxShadow: this._selectedTemplate === info.doc ? `0 0 15px rgba(68, 118, 247, .8)` : '' + }} + onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(info.doc)))}> + <img className='docCreatorMenu-preview-image' src={info.icon!.url.href.replace(".png", "_o.png")} /> + </div> + )})} + </div> + </div> + {/* <div className='docCreatorMenu-preview-container' style={{gridTemplateColumns: `repeat(${Math.floor((this._menuDimensions.width - 10) / 140)}, 140px)`}}> {this._templateDocs.map(doc => ({icon: ImageCast(doc.icon), doc})).filter(info => info.icon && info.doc).map(info => { if (renderedTemplates.includes(info.doc)) return undefined; @@ -316,6 +421,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { > <FontAwesomeIcon icon='plus' color='rgb(160, 160, 160)'/> </div> + </div> */} </div> ); } @@ -546,12 +652,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } basicTemplateTest = () => { - const temps: {width: number; height: number; x: number; y: number; title: string}[] = [ - {width: 200, height: 50, x: -100, y: -200, title: 'title'}, - {width: 300, height: 300, x: -150, y: -100, title: 'image'}, - {width: 200, height: 50, x: -100, y: -200, title: 'description'}, - ] - this._dataViz?.generateTemplates(); + this.generateTemplates(this._userPrompt); //this._dataViz?.createBasicTemplates(temps); } @@ -609,31 +710,6 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { return ( <div className='docCreatorMenu'> {!this._shouldDisplay ? undefined : - <> - {/* <div className='docCreatorMenu-placement-indicator' - style={{ - display: '', - left: this._indicatorX ?? this._pageX + 300, - top: this._indicatorY ?? this._pageY, - }} - onPointerMove={e => this.onPointerMove(e)} - onPointerDown={e => - setupMoveUpEvents( - this, - e, - (e) => { - this._draggingIndicator = true; - this._startPos = {x: 0, y: 0}; - this._startPos.x = e.pageX - (this._ref?.getBoundingClientRect().left ?? 0); - this._startPos.y = e.pageY - (this._ref?.getBoundingClientRect().top ?? 0); - return true; - }, - emptyFunction, - undoable(clickEv => { - clickEv.stopPropagation(); - }, 'drag menu') - ) - }/> */} <div className="docCreatorMenu-cont" ref={r => this._ref = r} @@ -681,7 +757,6 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { </div> {this.renderSelectedViewType} </div> - </> } </div> ) |