From 77d76c1a7e88b3ad4b72c4cd6aa4d4772042b65d Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Mon, 8 Jul 2024 14:07:26 -0400 Subject: method in dataviz for identifying valid templates (WIP); option added to contextmenu to create docs from dataViz --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 4d5f15a3e..1cb1ad6af 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -32,6 +32,8 @@ import { Histogram } from './components/Histogram'; import { LineChart } from './components/LineChart'; import { PieChart } from './components/PieChart'; import { TableBox } from './components/TableBox'; +import { LinkManager } from '../../../util/LinkManager'; +import { Collection } from 'mongoose'; export enum DataVizView { TABLE = 'table', @@ -431,6 +433,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { const options = cm.findByDescription('Options...'); const optionItems = options && 'subitems' in options ? options.subitems : []; optionItems.push({ description: `Analyze with AI`, event: () => this.askGPT(), icon: 'lightbulb' }); + optionItems.push({ description: `Create documents`, event: () => this.askGPT(), icon: 'table-cells' }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); }; @@ -445,6 +448,26 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { GPTPopup.Instance.generateDataAnalysis(); }); + getPossibleTemplates = (): Doc[] => { + const linkedDocs: Doc[] = LinkManager.Instance.getAllRelatedLinks(this.Document).map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document))); + const linkedCollections: Doc[] = linkedDocs.filter(doc => doc.type === 'collection'); + const isColumnTitle = (title: string): boolean => { + const colTitles: string[] = Object.keys(this.records[0]); + for (let i = 0; i < colTitles.length; ++i){ + if (colTitles[i] === title) return true; + } + return false; + } + const isValidTemplate = (collection: Doc) => { + const childDocs = DocListCast(collection[Doc.LayoutFieldKey(collection)]); + for (let i = 0; i < childDocs.length; ++i){ + if (isColumnTitle(String(childDocs[i].title))) return true; + } + return false; + } + return linkedCollections.filter(col => isValidTemplate(col)); + } + /** * creates a new dataviz document filter from this one * it appears to the right of this document, with the -- cgit v1.2.3-70-g09d2 From 0c68bfcf4041558edf94f2898fc0531f24433351 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Tue, 9 Jul 2024 03:12:28 -0400 Subject: started doccreatormenu for dataviz --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 16 ++- .../views/nodes/DataVizBox/DocCreatorMenu.scss | 12 +++ .../views/nodes/DataVizBox/DocCreatorMenu.tsx | 113 +++++++++++++++++++++ 3 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 src/client/views/nodes/DataVizBox/DocCreatorMenu.scss create mode 100644 src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 1cb1ad6af..dae535ba6 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -34,6 +34,7 @@ import { PieChart } from './components/PieChart'; import { TableBox } from './components/TableBox'; import { LinkManager } from '../../../util/LinkManager'; import { Collection } from 'mongoose'; +import { DocCreatorMenu } from './DocCreatorMenu'; export enum DataVizView { TABLE = 'table', @@ -433,7 +434,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { const options = cm.findByDescription('Options...'); const optionItems = options && 'subitems' in options ? options.subitems : []; optionItems.push({ description: `Analyze with AI`, event: () => this.askGPT(), icon: 'lightbulb' }); - optionItems.push({ description: `Create documents`, event: () => this.askGPT(), icon: 'table-cells' }); + optionItems.push({ description: `Create documents`, event: () => DocCreatorMenu.Instance.displayMenu(200, 200), icon: 'table-cells' }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); }; @@ -450,16 +451,22 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { getPossibleTemplates = (): Doc[] => { const linkedDocs: Doc[] = LinkManager.Instance.getAllRelatedLinks(this.Document).map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document))); - const linkedCollections: Doc[] = linkedDocs.filter(doc => doc.type === 'collection'); + const linkedCollections: Doc[] = linkedDocs.filter(doc => doc.type === 'config').filter(doc => DocCast(doc.annotationOn).type === 'collection'); + console.log('cols: ' + linkedCollections) const isColumnTitle = (title: string): boolean => { const colTitles: string[] = Object.keys(this.records[0]); + console.log('titles: ' + colTitles) for (let i = 0; i < colTitles.length; ++i){ - if (colTitles[i] === title) return true; + if (colTitles[i] === title) { + console.log(true); + return true; + } } return false; } const isValidTemplate = (collection: Doc) => { const childDocs = DocListCast(collection[Doc.LayoutFieldKey(collection)]); + console.log('childDocs of col: ' + childDocs) for (let i = 0; i < childDocs.length; ++i){ if (isColumnTitle(String(childDocs[i].title))) return true; } @@ -511,6 +518,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { // displays how to get data into the DataVizBox if its empty
To create a DataViz box, either import / drag a CSV file into your canvas or copy a data table and use the command (ctrl + p) to bring the data table to your canvas.
) : ( +
+
() { /> )}
+
); } } diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss new file mode 100644 index 000000000..fea5a6f59 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss @@ -0,0 +1,12 @@ +.docCreatorMenu-cont { + position: absolute; + display: flex; + min-width: 300px; + min-height: 400px; + z-index: 6; + box-shadow: 0px 3px 4px rgba(0, 0, 0, 30%); + flex-direction: column; + background: whitesmoke; + color: black; + border-radius: 3px; +} \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx new file mode 100644 index 000000000..d5c2bc227 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -0,0 +1,113 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { action, computed, IReactionDisposer, makeObservable, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { SnappingManager } from '../../../util/SnappingManager'; +import './DocCreatorMenu.scss'; +import { ObservableReactComponent } from '../../ObservableReactComponent'; + +@observer +export class DocCreatorMenu extends ObservableReactComponent<{}> { + + static Instance: DocCreatorMenu; + + private _reactionDisposer?: IReactionDisposer; + + @observable _pageX: number = 0; + @observable _pageY: number = 0; + @observable _display: boolean = false; + + @observable _yRelativeToTop: boolean = true; + @observable _selectedIndex = -1; + + @observable _mouseX: number = -1; + @observable _mouseY: number = -1; + @observable _shouldDisplay: boolean = false; + + constructor(props: any) { + super(props); + makeObservable(this); + DocCreatorMenu.Instance = this; + } + + @action + onPointerDown = (e: PointerEvent) => { + this._mouseX = e.clientX; + this._mouseY = e.clientY; + }; + @action + onPointerUp = (e: PointerEvent) => { + if (e.button !== 2 && !e.ctrlKey) return; + const curX = e.clientX; + const curY = e.clientY; + if (Math.abs(this._mouseX - curX) > 1 || Math.abs(this._mouseY - curY) > 1) { + this._shouldDisplay = false; + } + + if (this._shouldDisplay) { + this._display = true; + } + }; + + componentDidMount() { + document.addEventListener('pointerdown', this.onPointerDown, true); + document.addEventListener('pointerup', this.onPointerUp); + } + + componentWillUnmount() { + document.removeEventListener('pointerdown', this.onPointerDown, true); + document.removeEventListener('pointerup', this.onPointerUp); + this._reactionDisposer?.(); + } + + static readonly buffer = 20; + static readonly width = 300; + static readonly height = 400; + get pageX() { + return this._pageX + DocCreatorMenu.width > window.innerWidth - DocCreatorMenu.buffer ? window.innerWidth - DocCreatorMenu.buffer - DocCreatorMenu.width : Math.max(0, this._pageX); + } + + get pageY() { + return this._pageY + DocCreatorMenu.height > window.innerHeight - DocCreatorMenu.buffer ? window.innerHeight - DocCreatorMenu.buffer - DocCreatorMenu.height : Math.max(0, this._pageY); + } + + @action + displayMenu = (x: number, y: number) => { + // maxX and maxY will change if the UI/font size changes, but will work for any amount + // of items added to the menu + + this._pageX = x; + this._pageY = y; + this._shouldDisplay = true; + }; + + @action + closeMenu = () => { + const wasOpen = this._display; + this._display = false; + this._shouldDisplay = false; + return wasOpen; + }; + + render() { + return ( +
+ {!this._shouldDisplay ? undefined : +
hi hi
+ + + } +
+ ) + } + + +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From b669b8c82efa9217cf065e670c7f6111b096f20d Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Wed, 10 Jul 2024 02:11:46 -0400 Subject: docCreatorMenu progress --- src/client/views/ContextMenu.tsx | 1 - src/client/views/MainView.tsx | 3 + src/client/views/nodes/DataVizBox/DataVizBox.tsx | 9 +- .../views/nodes/DataVizBox/DocCreatorMenu.scss | 59 ++++++++-- .../views/nodes/DataVizBox/DocCreatorMenu.tsx | 119 +++++++++++++++++---- 5 files changed, 157 insertions(+), 34 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index d784a14b8..de985263d 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -269,7 +269,6 @@ export class ContextMenu extends ObservableReactComponent<{}> { // if (this._searchString.startsWith(this._defaultPrefix)) { this._defaultItem?.(this._searchString.substring(this._defaultPrefix.length)); } - this.closeMenu(); e.preventDefault(); e.stopPropagation(); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 5b9ddbb88..926e5fd55 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -76,6 +76,7 @@ import { PresBox } from './nodes/trails'; import { AnchorMenu } from './pdf/AnchorMenu'; import { GPTPopup } from './pdf/GPTPopup/GPTPopup'; import { TopBar } from './topbar/TopBar'; +import { DocCreatorMenu } from './nodes/DataVizBox/DocCreatorMenu'; const { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } = require('./global/globalCssVariables.module.scss'); // prettier-ignore const _global = (window /* browser */ || global) /* node */ as any; @@ -278,6 +279,7 @@ export class MainView extends ObservableReactComponent<{}> { library.add( ...[ + fa.faWindowMaximize, fa.faGift, fa.faLockOpen, fa.faSort, @@ -1093,6 +1095,7 @@ export class MainView extends ObservableReactComponent<{}> { + diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index dae535ba6..1e0a72a91 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -429,12 +429,12 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { this.layoutDoc.dataViz_filterSelection = !this.layoutDoc.dataViz_filterSelection; }; - specificContextMenu = (): void => { + specificContextMenu = (x: number, y: number): void => { const cm = ContextMenu.Instance; const options = cm.findByDescription('Options...'); const optionItems = options && 'subitems' in options ? options.subitems : []; optionItems.push({ description: `Analyze with AI`, event: () => this.askGPT(), icon: 'lightbulb' }); - optionItems.push({ description: `Create documents`, event: () => DocCreatorMenu.Instance.displayMenu(200, 200), icon: 'table-cells' }); + optionItems.push({ description: `Create documents`, event: () => DocCreatorMenu.Instance.toggleDisplay(x, y), icon: 'table-cells' }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); }; @@ -518,8 +518,6 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { // displays how to get data into the DataVizBox if its empty
To create a DataViz box, either import / drag a CSV file into your canvas or copy a data table and use the command (ctrl + p) to bring the data table to your canvas.
) : ( -
-
() { transform: `scale(${scale})`, position: 'absolute', }} - onContextMenu={this.specificContextMenu} + onContextMenu={(e) => this.specificContextMenu(e.pageX, e.pageY)} onWheel={e => e.stopPropagation()} ref={this._mainCont}>
@@ -596,7 +594,6 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { /> )}
-
); } } diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss index fea5a6f59..43053ee34 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss @@ -1,12 +1,55 @@ .docCreatorMenu-cont { position: absolute; - display: flex; - min-width: 300px; - min-height: 400px; - z-index: 6; - box-shadow: 0px 3px 4px rgba(0, 0, 0, 30%); - flex-direction: column; - background: whitesmoke; - color: black; + z-index: 100000; + // box-shadow: 0px 3px 4px rgba(0, 0, 0, 30%); + // background: whitesmoke; + // color: black; border-radius: 3px; +} + +.docCreatorMenu-menu { + display: flex; + flex-direction: row; +} + +.docCreatorMenu-menu-button { + width: 30px; + height: 30px; + background: whitesmoke; + background-color: rgb(34, 34, 37); + border-radius: 5px; + border: 1px solid whitesmoke; + padding: 0px; + font-size: 14px; + + &.right{ + margin-left: auto; + font-size: 12px; + } + + &.close-menu { + margin-left: 0px; + font-size: 12px; + } + + &.options { + margin-left: 0px; + } + + &:hover { + background-color: rgb(60, 60, 65); + } +} + +.docCreatorMenu-menu-hr{ + margin-top: 0px; + margin-bottom: 0px; +} + +.docCreatorMenu-content { + margin: 5px; + width: calc(100% - 10px); + height: calc(100% - 51px); + border: 1px solid whitesmoke; + border-radius: 5px; } \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index d5c2bc227..dbd385047 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -1,16 +1,25 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, makeObservable, observable } from 'mobx'; +import { action, computed, IReactionDisposer, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { SnappingManager } from '../../../util/SnappingManager'; import './DocCreatorMenu.scss'; import { ObservableReactComponent } from '../../ObservableReactComponent'; +import { IconButton, Size } from 'browndash-components'; +import { returnFalse, setupMoveUpEvents } from '../../../../ClientUtils'; +import { emptyFunction } from '../../../../Utils'; +import { undoable } from '../../../util/UndoManager'; +import { DragManager } from '../../../util/DragManager'; +import { DocumentView } from '../DocumentView'; +import { dropActionType } from '../../../util/DropActionTypes'; @observer export class DocCreatorMenu extends ObservableReactComponent<{}> { static Instance: DocCreatorMenu; + private _ref: HTMLDivElement | null = null; + private _reactionDisposer?: IReactionDisposer; @observable _pageX: number = 0; @@ -22,8 +31,12 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { @observable _mouseX: number = -1; @observable _mouseY: number = -1; + @observable _startPos: {x: number, y: number} | undefined = undefined; @observable _shouldDisplay: boolean = false; + @observable _menuContent: 'templates' | 'options' = 'templates'; + @observable _dragging: boolean = false; + constructor(props: any) { super(props); makeObservable(this); @@ -35,6 +48,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { this._mouseX = e.clientX; this._mouseY = e.clientY; }; + @action onPointerUp = (e: PointerEvent) => { if (e.button !== 2 && !e.ctrlKey) return; @@ -43,10 +57,6 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { if (Math.abs(this._mouseX - curX) > 1 || Math.abs(this._mouseY - curY) > 1) { this._shouldDisplay = false; } - - if (this._shouldDisplay) { - this._display = true; - } }; componentDidMount() { @@ -67,18 +77,18 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { return this._pageX + DocCreatorMenu.width > window.innerWidth - DocCreatorMenu.buffer ? window.innerWidth - DocCreatorMenu.buffer - DocCreatorMenu.width : Math.max(0, this._pageX); } - get pageY() { - return this._pageY + DocCreatorMenu.height > window.innerHeight - DocCreatorMenu.buffer ? window.innerHeight - DocCreatorMenu.buffer - DocCreatorMenu.height : Math.max(0, this._pageY); - } - @action - displayMenu = (x: number, y: number) => { + toggleDisplay = (x: number, y: number) => { // maxX and maxY will change if the UI/font size changes, but will work for any amount // of items added to the menu - - this._pageX = x; - this._pageY = y; - this._shouldDisplay = true; + if (this._shouldDisplay) { + this._shouldDisplay = false; + } else { + this._pageX = x; + console.log(y); + this._pageY = y; + this._shouldDisplay = true; + } }; @action @@ -89,21 +99,92 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { return wasOpen; }; + @action + onPointerMove = (e: any) => { + if (this._dragging){ + this._pageX = e.pageX - (this._startPos?.x ?? 0); + this._pageY = e.pageY - (this._startPos?.y ?? 0); + } + } + + setDragStart = () => { + + } + render() { return (
this._ref = r} style={{ display: '', - left: this.pageX, - ...(this._yRelativeToTop ? { top: Math.max(0, this.pageY) } : { bottom: this.pageY }), + left: this._pageX, + top: this._pageY, + width: this._shouldDisplay ? 300 : 0, + height: this._shouldDisplay ? 400 : 0, background: SnappingManager.userBackgroundColor, color: SnappingManager.userColor, }}> {!this._shouldDisplay ? undefined : -
hi hi
- - + <> +
+ setupMoveUpEvents( + this, + e, + (e) => { + this._dragging = 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(); + }, 'open actions menu') + ) + + } + onPointerMove={e => this.onPointerMove(e)} + onPointerUp={() => this._dragging = false} + > + + + +
+
+
+ {this._menuContent === 'templates' ? +
+
+ : +
+
+ } +
+ }
) -- cgit v1.2.3-70-g09d2 From 77bf838bebc813d476788fabed6bc7bcbf8197b0 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Wed, 10 Jul 2024 14:36:39 -0400 Subject: images displaying --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 13 ++-- .../views/nodes/DataVizBox/DocCreatorMenu.scss | 54 +++++++++++++-- .../views/nodes/DataVizBox/DocCreatorMenu.tsx | 80 +++++++++++++--------- 3 files changed, 107 insertions(+), 40 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 1e0a72a91..f75ba9700 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -429,12 +429,18 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { this.layoutDoc.dataViz_filterSelection = !this.layoutDoc.dataViz_filterSelection; }; + openDocCreatorMenu = (x: number, y: number) => { + DocCreatorMenu.Instance.toggleDisplay(x, y); + DocCreatorMenu.Instance.setDataViz(this); + DocCreatorMenu.Instance.setTemplateDocs(this.getPossibleTemplates()); + } + specificContextMenu = (x: number, y: number): void => { const cm = ContextMenu.Instance; const options = cm.findByDescription('Options...'); const optionItems = options && 'subitems' in options ? options.subitems : []; optionItems.push({ description: `Analyze with AI`, event: () => this.askGPT(), icon: 'lightbulb' }); - optionItems.push({ description: `Create documents`, event: () => DocCreatorMenu.Instance.toggleDisplay(x, y), icon: 'table-cells' }); + optionItems.push({ description: `Create documents`, event: () => this.openDocCreatorMenu(x, y), icon: 'table-cells' }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); }; @@ -452,13 +458,12 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { getPossibleTemplates = (): Doc[] => { const linkedDocs: Doc[] = LinkManager.Instance.getAllRelatedLinks(this.Document).map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document))); const linkedCollections: Doc[] = linkedDocs.filter(doc => doc.type === 'config').filter(doc => DocCast(doc.annotationOn).type === 'collection'); - console.log('cols: ' + linkedCollections) const isColumnTitle = (title: string): boolean => { const colTitles: string[] = Object.keys(this.records[0]); console.log('titles: ' + colTitles) for (let i = 0; i < colTitles.length; ++i){ if (colTitles[i] === title) { - console.log(true); + console.log(true); return true; } } @@ -472,7 +477,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { } return false; } - return linkedCollections.filter(col => isValidTemplate(col)); + return linkedDocs; } /** diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss index dc0f99cbd..43d4ed7e5 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss @@ -23,13 +23,13 @@ font-size: 14px; &.right{ - margin-left: auto; + margin-left: 0px; font-size: 12px; } &.close-menu { - margin-left: 0px; font-size: 12px; + margin-left: auto; } &.options { @@ -46,7 +46,7 @@ margin-bottom: 0px; } -.docCreatorMenu-preview-content { +.docCreatorMenu-preview-container { display: grid; grid-template-columns: repeat(2, 1fr); overflow-y: scroll; @@ -64,4 +64,50 @@ border: 1px solid whitesmoke; border-radius: 5px; } -} \ No newline at end of file +} + +.docCreatorMenu-options-container { + display: flex; + overflow-y: scroll; + margin: 5px; + width: calc(100% - 10px); + height: calc(100% - 51px); + border: 1px solid whitesmoke; + border-radius: 5px; + + .docCreatorMenu-dropdown-button{ + display: flex; + width: 120px; + height: 30px; + background: whitesmoke; + background-color: rgb(34, 34, 37); + border-radius: 5px; + border: 1px solid whitesmoke; + padding: 0px; + font-size: 13px; + margin: 10px; + align-content: center; + justify-content: center; + text-transform: uppercase; + + // &:hover .docCreatorMenu-dropdown-content{ + // display: block; + // } + } + + .docCreatorMenu-dropdown-content { + display: none; + position: absolute; + min-width: 100px; + z-index: 1; + + .docCreatorMenu-dropdown-option{ + display: block; + } + + } + + + +} + diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index 520dd9c2d..1976ecad2 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -1,17 +1,18 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, makeObservable, observable, runInAction } from 'mobx'; +import { IReactionDisposer, action, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { SnappingManager } from '../../../util/SnappingManager'; -import './DocCreatorMenu.scss'; -import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { IconButton, Size } from 'browndash-components'; import { returnFalse, setupMoveUpEvents } from '../../../../ClientUtils'; +import { Doc } from '../../../../fields/Doc'; +import { DocCast, ImageCast } from '../../../../fields/Types'; +import { ImageField } from '../../../../fields/URLField'; import { emptyFunction } from '../../../../Utils'; +import { SnappingManager } from '../../../util/SnappingManager'; import { undoable } from '../../../util/UndoManager'; -import { DragManager } from '../../../util/DragManager'; +import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DocumentView } from '../DocumentView'; -import { dropActionType } from '../../../util/DropActionTypes'; +import { DataVizBox } from './DataVizBox'; +import './DocCreatorMenu.scss'; @observer export class DocCreatorMenu extends ObservableReactComponent<{}> { @@ -20,22 +21,21 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { private _ref: HTMLDivElement | null = null; - private _reactionDisposer?: IReactionDisposer; + @observable _templateDocs: Doc[] = []; + @observable _icons : (ImageField|undefined)[] = []; @observable _pageX: number = 0; @observable _pageY: number = 0; @observable _display: boolean = false; - @observable _yRelativeToTop: boolean = true; - @observable _selectedIndex = -1; - @observable _mouseX: number = -1; @observable _mouseY: number = -1; - @observable _startPos: {x: number, y: number} | undefined = undefined; + @observable _startPos?: {x: number, y: number}; @observable _shouldDisplay: boolean = false; @observable _menuContent: 'templates' | 'options' = 'templates'; @observable _dragging: boolean = false; + @observable _dataViz?: DataVizBox; constructor(props: any) { super(props); @@ -43,6 +43,11 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { DocCreatorMenu.Instance = this; } + @action setDataViz = (dataViz: DataVizBox) => { this._dataViz = dataViz }; + @action setTemplateDocs = (docs: Doc[]) => { + this._templateDocs = docs.map(doc => doc.annotationOn ? DocCast(doc.annotationOn):doc); + }; + @action onPointerDown = (e: PointerEvent) => { this._mouseX = e.clientX; @@ -59,22 +64,19 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } }; + _disposer: IReactionDisposer|undefined; componentDidMount() { document.addEventListener('pointerdown', this.onPointerDown, true); document.addEventListener('pointerup', this.onPointerUp); + this._disposer = reaction(() => this._templateDocs.slice(), + async(docs) => this._icons = (await Promise.all(docs.map(doc => this.getIcon(doc)))) + , {fireImmediately: true}); } componentWillUnmount() { + this._disposer?.(); document.removeEventListener('pointerdown', this.onPointerDown, true); document.removeEventListener('pointerup', this.onPointerUp); - this._reactionDisposer?.(); - } - - static readonly buffer = 20; - static readonly width = 300; - static readonly height = 400; - get pageX() { - return this._pageX + DocCreatorMenu.width > window.innerWidth - DocCreatorMenu.buffer ? window.innerWidth - DocCreatorMenu.buffer - DocCreatorMenu.width : Math.max(0, this._pageX); } @action @@ -105,23 +107,37 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } } + async getIcon(doc: Doc) { + const docView = DocumentView.getDocumentView(doc); + if (docView) { + docView.ComponentView?.updateIcon?.(); + return new Promise(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 500)); + } + return undefined; + } + get templatesPreviewContents(){ - const tempArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; return ( -
- {tempArray.map(i => -
- -
)} +
+ {this._icons.filter(url => url).map(url => +
+ +
) + }
); } get optionsMenuContents(){ return ( -
+
+
Dropdown
+
+
Link 1
+
Link 1
+
Link 1
+
+
); } @@ -181,6 +197,9 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { > + -

{this._menuContent === 'templates' ? this.templatesPreviewContents : this.optionsMenuContents} -- cgit v1.2.3-70-g09d2 From 54fbd0815e93da8aff7a601676a90aa0faf87de8 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Thu, 11 Jul 2024 13:19:04 -0400 Subject: collection filtering works --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 5 ++--- src/client/views/nodes/DataVizBox/DocCreatorMenu.scss | 5 +++-- src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx | 7 ++++--- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index f75ba9700..c43177c51 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -457,10 +457,9 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { getPossibleTemplates = (): Doc[] => { const linkedDocs: Doc[] = LinkManager.Instance.getAllRelatedLinks(this.Document).map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document))); - const linkedCollections: Doc[] = linkedDocs.filter(doc => doc.type === 'config').filter(doc => DocCast(doc.annotationOn).type === 'collection'); + const linkedCollections: Doc[] = linkedDocs.filter(doc => doc.type === 'config').filter(doc => DocCast(doc.annotationOn).type === 'collection').map(doc => DocCast(doc.annotationOn)); const isColumnTitle = (title: string): boolean => { const colTitles: string[] = Object.keys(this.records[0]); - console.log('titles: ' + colTitles) for (let i = 0; i < colTitles.length; ++i){ if (colTitles[i] === title) { console.log(true); @@ -477,7 +476,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { } return false; } - return linkedDocs; + return linkedCollections.filter(col => isValidTemplate(col)); } /** diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss index ba20c9179..4944f3071 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss @@ -66,9 +66,9 @@ height: 125px; margin-top: 10px; margin-left: 10px; - border: 1px solid whitesmoke; + border: 1px solid rgb(163, 163, 163); border-radius: 5px; - box-shadow: 5px 5px rgb(34, 34, 37); + box-shadow: 5px 5px rgb(29, 29, 31); &:hover{ background-color: rgb(72, 72, 73); @@ -134,6 +134,7 @@ overflow-y: scroll; -ms-overflow-style: none; scrollbar-width: none; + border-bottom: solid 1px whitesmoke; .docCreatorMenu-dropdown-option{ display: flex; diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index 356172b6a..3ebf97570 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -150,10 +150,11 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { get optionsMenuContents(){ - const layoutOption = (option: 'stacked' | 'grid' | 'row' | 'column' | 'custom') => { + const layoutOption = (option: 'stacked' | 'grid' | 'row' | 'column' | 'custom', optStyle?: {}) => { return (
setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoable(clickEv => { @@ -176,7 +177,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { {layoutOption('grid')} {layoutOption('row')} {layoutOption('column')} - {layoutOption('custom')} + {layoutOption('custom', {borderBottom: `0px`})}
@@ -239,7 +240,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { ) } > - + -
@@ -270,6 +297,9 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { ) } - +} +export interface DataVizTemplateInfo { + doc: Doc; + layout?: LayoutType; } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From c09ccb23ee490b5c1060f734665fd85668a62659 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Fri, 12 Jul 2024 18:25:26 -0400 Subject: repeat option added to menu; css refactoring --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 5 +- .../views/nodes/DataVizBox/DocCreatorMenu.scss | 166 ++++++++++++++------- .../views/nodes/DataVizBox/DocCreatorMenu.tsx | 72 ++++++--- 3 files changed, 168 insertions(+), 75 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 86a9db79c..283288013 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -496,6 +496,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { @action createDocsFromTemplate = (templateInfo: DataVizTemplateInfo) => { + if (!templateInfo.doc) return; const mainCollection = this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; const fields: string[] = Array.from(Object.keys(this.records[0])); const selectedRows = NumListCast(this.layoutDoc.dataViz_selectedRows); @@ -508,8 +509,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { values.forEach((val, i) => {proto[fields[i]] = val as FieldType}); const target = Doc.MakeDelegate(proto); - const targetKey = StrCast(templateInfo.doc.layout_fieldKey, 'layout'); - const applied = this.ApplyTemplateTo(templateInfo.doc, target, targetKey, templateInfo.doc.title + `${row}`); + const targetKey = StrCast(templateInfo.doc!.layout_fieldKey, 'layout'); + const applied = this.ApplyTemplateTo(templateInfo.doc!, target, targetKey, templateInfo.doc!.title + `${row}`); target.layout_fieldKey = targetKey; return applied; }); diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss index 4944f3071..243954873 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss @@ -46,6 +46,11 @@ margin-bottom: 0px; } +//------------------------------------------------------------------------------------------------------------------------------------------ +//DocCreatorMenu templates preview CSS +//-------------------------------------------------------------------------------------------------------------------------------------------- + + .docCreatorMenu-preview-container { display: grid; grid-template-columns: repeat(2, 1fr); @@ -83,7 +88,66 @@ } } -.docCreatorMenu-options-container { +//------------------------------------------------------------------------------------------------------------------------------------------ +//DocCreatorMenu options CSS +//-------------------------------------------------------------------------------------------------------------------------------------------- + +.docCreatorMenu-option-container{ + display: flex; + width: 180px; + height: 30px; + flex-direction: row; + justify-content: center; + align-items: center; + margin-top: 10px; + + &.layout{ + z-index: 5; + } +} + +.docCreatorMenu-option-title{ + display: flex; + width: 140px; + height: 30px; + background: whitesmoke; + background-color: rgb(34, 34, 37); + border-radius: 5px; + border: 1px solid whitesmoke; + padding: 0px; + font-size: 12px; + align-items: center; + justify-content: center; + text-transform: uppercase; + cursor: pointer; + + &.repeat { + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; + width: 60px; + border-right: 0px; + } +} + +.docCreatorMenu-input { + display: flex; + height: 30px; + background-color: rgb(34, 34, 37); + border-radius: 5px; + border: 1px solid whitesmoke; + align-items: center; + justify-content: center; + + &.repeat { + margin: 0px; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; + border-left: 0px; + width: 40px; + } +} + +.docCreatorMenu-menu-container { display: flex; flex-direction: column; justify-content: flex-start; @@ -95,73 +159,63 @@ border: 1px solid whitesmoke; border-radius: 5px; - .docCreatorMenu-dropdown-hoverable { - width: 140px; - height: 25px; - margin-bottom: 17px; - z-index: 5; - - &:hover .docCreatorMenu-dropdown-content { - display: block; - } + .docCreatorMenu-option-container{ + width: 180px; + height: 30px; - &:hover .docCreatorMenu-dropdown-title { - border-bottom-left-radius: 0px; - border-bottom-right-radius: 0px; - } - - .docCreatorMenu-dropdown-title{ - display: flex; + .docCreatorMenu-dropdown-hoverable { width: 140px; - height: 25px; - background: whitesmoke; - background-color: rgb(34, 34, 37); - border-radius: 5px; - border: 1px solid whitesmoke; - padding: 0px; - font-size: 12px; - margin-top: 10px; - align-items: center; - justify-content: center; - text-transform: uppercase; - cursor: pointer; - } + height: 30px; - .docCreatorMenu-dropdown-content { - display: none; - min-width: 100px; - height: 75px; - overflow-y: scroll; - -ms-overflow-style: none; - scrollbar-width: none; - border-bottom: solid 1px whitesmoke; + &:hover .docCreatorMenu-dropdown-content { + display: block; + } - .docCreatorMenu-dropdown-option{ - display: flex; - background-color: rgb(42, 42, 46); - border-left: 1px solid whitesmoke; - border-right: 1px solid whitesmoke; - border-bottom: 1px solid whitesmoke; - width: 140px; - height: 25px; - justify-content: center; - justify-items: center; - padding-top: 3px; - - &:hover { - background-color: rgb(68, 68, 74); - cursor: pointer; - } + &:hover .docCreatorMenu-option-title { + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; } - } + + .docCreatorMenu-dropdown-content { + display: none; + min-width: 100px; + height: 75px; + overflow-y: scroll; + -ms-overflow-style: none; + scrollbar-width: none; + border-bottom: solid 1px whitesmoke; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + + .docCreatorMenu-dropdown-option{ + display: flex; + background-color: rgb(42, 42, 46); + border-left: 1px solid whitesmoke; + border-right: 1px solid whitesmoke; + border-bottom: 1px solid whitesmoke; + width: 140px; + height: 25px; + justify-content: center; + justify-items: center; + padding-top: 3px; + + &:hover { + background-color: rgb(68, 68, 74); + cursor: pointer; + } + } + } + } + } .docCreatorMenu-layout-preview-window { + aspect-ratio: 1 / 1; width: 85%; - height: 100px; border: 1px solid whitesmoke; border-radius: 5px; background-color: rgb(34, 34, 37); + margin-top: 10px; } } diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index 88308c226..1b4fe0879 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -31,12 +31,14 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { static Instance: DocCreatorMenu; private _ref: HTMLDivElement | null = null; + private _templateInfo: DataVizTemplateInfo = {}; @observable _templateDocs: Doc[] = []; @observable _templateRefToDoc?: ObservableMap; - @observable _selectedTemplate?: Doc; + @observable _selectedTemplate: Doc | undefined = undefined; - @observable _selectedLayout?: LayoutType; + @observable _selectedLayout: LayoutType | undefined = undefined; + @observable _layoutPreview: boolean = false; @observable _pageX: number = 0; @observable _pageY: number = 0; @@ -150,7 +152,6 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { MakeTemplate(info.doc); // ref.style.background = Colors.MEDIUM_BLUE; // ref.style.boxShadow = `0 0 15px rgba(68, 118, 247, .8)`; - this.forceUpdate(); }, 'open actions menu') ) }> @@ -161,8 +162,15 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { ); } - get optionsMenuContents(){ + get layoutPreviewContents() { + return ( +
+ +
+ ); + } + get optionsMenuContents(){ const layoutOption = (option: LayoutType, optStyle?: {}) => { return (
{ ); } + const repeatOption = (num: number) => { + return (); + } + + const repeatOptions = [0, 1, 2, 3, 4, 5]; + return ( -
-
-
Choose Layout
-
- {layoutOption(LayoutType.Stacked)} - {layoutOption(LayoutType.Grid)} - {layoutOption(LayoutType.Row)} - {layoutOption(LayoutType.Column)} - {layoutOption(LayoutType.Custom, {borderBottom: `0px`})} +
+
+
+
{this._selectedLayout ? this._selectedLayout.toUpperCase() : 'Choose Layout'}
+
+ {layoutOption(LayoutType.Stacked)} + {layoutOption(LayoutType.Grid)} + {layoutOption(LayoutType.Row)} + {layoutOption(LayoutType.Column)} + {layoutOption(LayoutType.Custom, {borderBottom: `0px`})} +
+
-
- + {this._layoutPreview ? this.layoutPreviewContents : null} +
+
Repeat
+
); @@ -253,7 +290,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { ) } > - +
{this._layout.type ? this.layoutConfigOptions: null} {this._layoutPreview ? this.layoutPreviewContents : null} - {selectionBox(60, 20, 'repeat', undefined, repeatOptions.map(num => ))} + {selectionBox(60, 20, 'repeat', undefined, repeatOptions.map(num => ))}
); } @@ -365,6 +371,31 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { return (
{!this._shouldDisplay ? undefined : + <> + {/*
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') + ) + }/> */}
this._ref = r} @@ -410,7 +441,8 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { undoable(clickEv => { clickEv.stopPropagation(); if (!this._selectedTemplate) return; - const templateInfo: DataVizTemplateInfo = {doc: this._selectedTemplate}; + const templateInfo: DataVizTemplateInfo = {doc: this._selectedTemplate, layout: this._layout, referencePos: {x: this._pageX + 450, y: this._pageY}, columns: this.columnsCount}; + this._dataViz?.createDocsFromTemplate(templateInfo); }, 'make docs') ) @@ -425,6 +457,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> {
{this._menuContent === 'templates' ? this.templatesPreviewContents : this.optionsMenuContents}
+ }
) @@ -432,7 +465,8 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } export interface DataVizTemplateInfo { - doc?: Doc; - layout?: {}; - repeat?: number; + doc: Doc; + layout: {type: LayoutType, xMargin: number, yMargin: number, repeat: number}; + columns: number; + referencePos: {x: number, y: number}; } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From ded7673796965e9cb9239cfd483a2f1e35abd38b Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Wed, 17 Jul 2024 18:28:05 -0400 Subject: highlighting rows based on layoutpreview hover --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 20 ++++++++++++++++++-- .../views/nodes/DataVizBox/DocCreatorMenu.scss | 11 +++++++++-- src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx | 18 +++++++++++++----- .../views/nodes/DataVizBox/components/TableBox.tsx | 6 ++++-- src/client/views/nodes/ImageBox.tsx | 2 +- 5 files changed, 45 insertions(+), 12 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index e03da8e7b..765642891 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -38,6 +38,7 @@ import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; import { PrefetchProxy } from '../../../../fields/Proxy'; import { AclAdmin, AclAugment, AclEdit } from '../../../../fields/DocSymbols'; import { template } from 'lodash'; +import { data } from 'jquery'; export enum DataVizView { TABLE = 'table', @@ -57,6 +58,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { crop: ((region: Doc | undefined, addCrop?: boolean) => Doc | undefined) | undefined; @observable _marqueeing: number[] | undefined = undefined; @observable _savedAnnotations = new ObservableMap(); + @observable _specialHighlightedRow: number | undefined = undefined; constructor(props: FieldViewProps) { super(props); @@ -131,6 +133,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { this.layoutDoc._dataViz_titleCol = titleCol; }; + @action setSpecialHighlightedRow = (row: number | undefined) => { + this._specialHighlightedRow = row; + } + @action // pinned / linked anchor doc includes selected rows, graph titles, and graph colors restoreView = (data: Doc) => { // const changedView = data.config_dataViz && this.dataVizView !== data.config_dataViz && (this.layoutDoc._dataViz = data.config_dataViz); @@ -366,7 +372,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { }; if (!this.records.length) return 'no data/visualization'; switch (this.dataVizView) { - case DataVizView.TABLE: return ; + case DataVizView.TABLE: return ; case DataVizView.LINECHART: return {this._vizRenderer = r ?? undefined;}} vizBox={this} />; case DataVizView.HISTOGRAM: return {this._vizRenderer = r ?? undefined;}} />; case DataVizView.PIECHART: return {this._vizRenderer = r ?? undefined;}} @@ -515,7 +521,6 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { while (i < columns && docsChanged < docs.length){ docs[docsChanged].x = curX; docs[docsChanged].y = curY; - console.log('x: ' + docs[i].x + ' y: ' + docs[i].y); curX += templWidth + xGap; ++docsChanged; ++i; @@ -527,6 +532,16 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { } } + // applyImagesTo = (doc: Doc, cols: string[]) => { + // const childDocs = DocListCast(doc[Doc.LayoutFieldKey(doc)]); + // const imageFields = childDocs.filter(doc => doc.type === 'image'); + // const imageToKey: Map = new Map(); + // imageFields.forEach(img => cols.forEach(col => {if (img[col]) imageToKey.set(img, col)})); + + // imageFields.forEach(doc => doc['data'] = String(doc[String(imageToKey.get(doc))]).replace(/"/g, '')); + + // } + @action createDocsFromTemplate = (templateInfo: DataVizTemplateInfo) => { if (!templateInfo.doc) return; @@ -545,6 +560,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { const targetKey = StrCast(templateInfo.doc!.layout_fieldKey, 'layout'); const applied = this.ApplyTemplateTo(templateInfo.doc!, target, targetKey, templateInfo.doc!.title + `${row}`); target.layout_fieldKey = targetKey; + //this.applyImagesTo(target, fields); return applied; }); diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss index 8b17f1ed4..328cbbaf1 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss @@ -81,8 +81,9 @@ grid-auto-rows: 141px; overflow-y: scroll; margin: 5px; + margin-top: 0px; width: calc(100% - 10px); - height: calc(100% - 51px); + height: calc(100% - 45px); border: 1px solid rgb(180, 180, 180); border-radius: 5px; @@ -240,8 +241,9 @@ align-items: center; overflow-y: scroll; margin: 5px; + margin-top: 0px; width: calc(100% - 10px); - height: calc(100% - 51px); + height: calc(100% - 45px); border: 1px solid rgb(180, 180, 180); border-radius: 5px; @@ -329,6 +331,11 @@ align-items: center; border-radius: 3px; border: solid 1px lightblue; + + &:hover { + border: solid 2px rgb(68, 153, 233); + z-index: 2; + } } } diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index b1488679f..48b87fae7 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -38,7 +38,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { @observable _templateRefToDoc?: ObservableMap; @observable _selectedTemplate: Doc | undefined = undefined; - @observable _layout: {type: LayoutType, yMargin: number, xMargin: number, columns: number, repeat: number} = {type: LayoutType.Grid, yMargin: 0, xMargin: 0, columns: 1, repeat: 0}; + @observable _layout: {type: LayoutType, yMargin: number, xMargin: number, columns?: number, repeat: number} = {type: LayoutType.Grid, yMargin: 0, xMargin: 0, repeat: 0}; @observable _layoutPreview: boolean = true; @observable _layoutPreviewScale: number = 1; @@ -48,6 +48,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { @observable _indicatorY: number | undefined = undefined; @observable _display: boolean = false; + @observable _hoveredLayoutPreview: number | undefined = undefined; @observable _mouseX: number = -1; @observable _mouseY: number = -1; @observable _startPos?: {x: number, y: number}; @@ -68,7 +69,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { @action setTemplateDocs = (docs: Doc[]) => {this._templateDocs = docs.map(doc => doc.annotationOn ? DocCast(doc.annotationOn):doc)}; @computed get docsToRender() { - return NumListCast(this._dataViz?.layoutDoc.dataViz_selectedRows); + return this._selectedTemplate ? NumListCast(this._dataViz?.layoutDoc.dataViz_selectedRows) : []; } @computed get rowsCount(){ @@ -255,6 +256,8 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } } + + get layoutPreviewContents() { const docWidth = Number(this._selectedTemplate?._width); const docHeight = Number(this._selectedTemplate?._height); @@ -298,6 +301,8 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> {
: this.docsToRender.map(num =>
this._dataViz?.setSpecialHighlightedRow(num)} + onMouseLeave={() => this._dataViz?.setSpecialHighlightedRow(undefined)} className='docCreatorMenu-layout-preview-item' style={{ width: scaledDown(docWidth), @@ -348,7 +353,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> {
{this._layout.type ? this._layout.type.toUpperCase() : 'Choose Layout'}
{layoutOption(LayoutType.Stacked)} - {layoutOption(LayoutType.Grid, undefined, () => {this._layout.columns = Math.ceil(Math.sqrt(this.docsToRender.length))})} + {layoutOption(LayoutType.Grid, undefined, () => {if (!this._layout.columns) this._layout.columns = Math.ceil(Math.sqrt(this.docsToRender.length))})} {layoutOption(LayoutType.Row)} {layoutOption(LayoutType.Column)} {layoutOption(LayoutType.Custom, {borderBottom: `0px`})} @@ -432,7 +437,10 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { >
+ ); + } + + get savedLayoutsPreviewContents(){ + return ( +
+ {this._savedLayouts.map((layout, index) => +
this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedSavedLayout(layout)))} + > + {this.layoutPreviewContents(87, layout, false, true, index)} +
+ )}
); } @@ -259,20 +296,23 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } } + layoutPreviewContents = (outerSpan: number, altLayout?: DataVizTemplateLayout, zoomOption: boolean = true, small: boolean = false, id?: number) => { + const doc: Doc | undefined = altLayout ? altLayout.template : this._selectedTemplate; + if (!doc) return; + const layout = altLayout ? altLayout.layout : this._layout; - get layoutPreviewContents() { - const docWidth = Number(this._selectedTemplate?._width); - const docHeight = Number(this._selectedTemplate?._height); - const horizontalSpan: number = (docWidth + this._layout.xMargin) * this.columnsCount - this._layout.xMargin; - const verticalSpan: number = (docHeight + this._layout.yMargin) * this.rowsCount - this._layout.yMargin; + const docWidth: number = Number(doc._width); + const docHeight: number = Number(doc._height); + const horizontalSpan: number = (docWidth + layout.xMargin) * (altLayout ? altLayout.columns : this.columnsCount) - layout.xMargin;; + const verticalSpan: number = (docHeight + layout.yMargin) * (altLayout ? altLayout.rows : this.rowsCount) - layout.yMargin; const largerSpan: number = horizontalSpan > verticalSpan ? horizontalSpan : verticalSpan; - const scaledDown = (input: number) => {return input / (largerSpan / 225 * this._layoutPreviewScale)} + const scaledDown = (input: number) => {return input / (largerSpan / outerSpan * this._layoutPreviewScale)} const fontSize = Math.min(scaledDown(docWidth / 3), scaledDown(docHeight / 3)); return ( -
-
+
+ {!zoomOption ? null :
-
+
}
{this._layout.type === LayoutType.Stacked ?
{
{this._layout.type ? this.layoutConfigOptions: null} - {this._layoutPreview ? this.layoutPreviewContents : null} + {this._layoutPreview ? this.layoutPreviewContents(225) : null} {selectionBox(60, 20, 'repeat', undefined, repeatOptions.map(num => ))} - + + + +
); } + get renderSelectedViewType(){ + switch (this._menuContent){ + case 'templates': + return this.templatesPreviewContents; + case 'options': + return this.optionsMenuContents; + case 'saved': + return this.savedLayoutsPreviewContents; + default: + return undefined; + } + } + render() { const topButton = (icon: string, opt: string, func: Function, tag: string) => { return ( @@ -485,7 +555,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> {
- {this._menuContent === 'templates' ? this.templatesPreviewContents : this.optionsMenuContents} + {this.renderSelectedViewType}
} @@ -499,4 +569,12 @@ export interface DataVizTemplateInfo { layout: {type: LayoutType, xMargin: number, yMargin: number, repeat: number}; columns: number; referencePos: {x: number, y: number}; +} + +export interface DataVizTemplateLayout { + template: Doc; + docsNumList: number[]; + layout: {type: LayoutType, xMargin: number, yMargin: number, repeat: number}; + columns: number; + rows: number; } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 951d6a8681e9be9068c8a0f8fce659938ab9ed80 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Wed, 31 Jul 2024 03:01:55 -0400 Subject: basic template generation work --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 35 +++++++++++++++++++++- .../views/nodes/DataVizBox/DocCreatorMenu.tsx | 13 +++++++- 2 files changed, 46 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index e943dd2e3..f25602fd5 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -34,13 +34,14 @@ import { PieChart } from './components/PieChart'; import { TableBox } from './components/TableBox'; import { LinkManager } from '../../../util/LinkManager'; import { DataVizTemplateInfo, DataVizTemplateLayout, DocCreatorMenu, LayoutType } from './DocCreatorMenu'; -import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; +import { CollectionFreeFormView, MarqueeView } from '../../collections/collectionFreeForm'; import { PrefetchProxy } from '../../../../fields/Proxy'; import { AclAdmin, AclAugment, AclEdit } from '../../../../fields/DocSymbols'; import { template } from 'lodash'; import { data } from 'jquery'; import { listSpec } from '../../../../fields/Schema'; import { ObjectField } from '../../../../fields/ObjectField'; +import { Id } from '../../../../fields/FieldSymbols'; export enum DataVizView { TABLE = 'table', @@ -566,6 +567,38 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { this.applyLayout(templateInfo, docs); } + createBasicTemplates = (colsToLayout: {width: number, height: number, x: number, y: number, title: string}[]) => { + const mainCollection = this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; + const docTools: Doc[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data); + console.log(docTools.map(doc => doc.title)) + const index = docTools.map(doc => doc.title).indexOf('Col'); + const template: Doc | undefined = DocUtils.copyDragFactory(Cast(docTools[index]?.dragFactory, Doc, null)); + if (!template) return; + + //const template = new CollectionFreeFormView({}); + const fields: Doc[] = colsToLayout.map(layout => { + const field = new Doc(); + field.x = layout.x; + field.y = layout.y; + field._width = layout.width; + field._height = layout.height; + field.title = layout.title; + template.data = 'hey'//DocListCast(template.data).map(doc => `idToDoc("${doc[Id]}")`).push(`idToDoc("${field[Id]}")`); + return field; + }); + + const fieldLiterals: string[] = fields.map(field => `idToDoc("${field[Id]}")`); + + template.data = fieldLiterals[0]; + + template.x = 400; + template.y = 400; + template._width = 500; + template._height = 500; + + mainCollection.addDocument(template); + } + /** * creates a new dataviz document filter from this one * it appears to the right of this document, with the diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index 34bec3b88..7993cab28 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -311,7 +311,9 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { )})} -
+
this.setUpButtonClick(e, this.basicTemplateTest)} + >
@@ -543,6 +545,15 @@ 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?.createBasicTemplates(temps); + } + get renderSelectedViewType(){ switch (this._menuContent){ case 'templates': -- cgit v1.2.3-70-g09d2 From 99b879956b67dc246f1ba48759a5550a94a5813d Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Wed, 31 Jul 2024 12:46:55 -0400 Subject: with old code for basic templates --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 34 +++++++++++------------- 1 file changed, 16 insertions(+), 18 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index f25602fd5..43cfe5bba 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -12,7 +12,7 @@ import { InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; import { Cast, CsvCast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { CsvField } from '../../../../fields/URLField'; -import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; +import { GetEffectiveAcl, TraceMobx, inheritParentAcls } from '../../../../fields/util'; import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; @@ -569,32 +569,30 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { createBasicTemplates = (colsToLayout: {width: number, height: number, x: number, y: number, title: string}[]) => { const mainCollection = this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; - const docTools: Doc[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data); - console.log(docTools.map(doc => doc.title)) - const index = docTools.map(doc => doc.title).indexOf('Col'); - const template: Doc | undefined = DocUtils.copyDragFactory(Cast(docTools[index]?.dragFactory, Doc, null)); - if (!template) return; + // const docTools: Doc[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data); + // const index = docTools.map(doc => doc.title).indexOf('Col'); + // const template: Doc | undefined = DocUtils.copyDragFactory(Cast(docTools[index]?.dragFactory, Doc, null)); + //if (!template) return; //const template = new CollectionFreeFormView({}); const fields: Doc[] = colsToLayout.map(layout => { - const field = new Doc(); - field.x = layout.x; - field.y = layout.y; - field._width = layout.width; - field._height = layout.height; - field.title = layout.title; - template.data = 'hey'//DocListCast(template.data).map(doc => `idToDoc("${doc[Id]}")`).push(`idToDoc("${field[Id]}")`); + const field = Docs.Create.TextDocument('', { _height: layout.height, _width: layout.width, title: layout.title, x: layout.x, y: layout.y }); + // Doc.SetContainer(field, template); + // inheritParentAcls(template, field, true); + //template.data = 'hey'//DocListCast(template.data).map(doc => `idToDoc("${doc[Id]}")`).push(`idToDoc("${field[Id]}")`); return field; }); + const template = Docs.Create.FreeformDocument(fields, { _height: 500, _width: 500, title: 'template', x: 400, y: 400 }) + const fieldLiterals: string[] = fields.map(field => `idToDoc("${field[Id]}")`); - template.data = fieldLiterals[0]; + //template.data = fieldLiterals[0]; - template.x = 400; - template.y = 400; - template._width = 500; - template._height = 500; + // template.x = 400; + // template.y = 400; + // template._width = 500; + // template._height = 500; mainCollection.addDocument(template); } -- cgit v1.2.3-70-g09d2 From b42bfb6cb8d4d2355fae4fe628c6bf5cca4b084a Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Wed, 31 Jul 2024 12:47:23 -0400 Subject: old code removed --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 43cfe5bba..1994f1b01 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -569,31 +569,14 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { createBasicTemplates = (colsToLayout: {width: number, height: number, x: number, y: number, title: string}[]) => { const mainCollection = this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; - // const docTools: Doc[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data); - // const index = docTools.map(doc => doc.title).indexOf('Col'); - // const template: Doc | undefined = DocUtils.copyDragFactory(Cast(docTools[index]?.dragFactory, Doc, null)); - //if (!template) return; - //const template = new CollectionFreeFormView({}); const fields: Doc[] = colsToLayout.map(layout => { const field = Docs.Create.TextDocument('', { _height: layout.height, _width: layout.width, title: layout.title, x: layout.x, y: layout.y }); - // Doc.SetContainer(field, template); - // inheritParentAcls(template, field, true); - //template.data = 'hey'//DocListCast(template.data).map(doc => `idToDoc("${doc[Id]}")`).push(`idToDoc("${field[Id]}")`); return field; }); const template = Docs.Create.FreeformDocument(fields, { _height: 500, _width: 500, title: 'template', x: 400, y: 400 }) - const fieldLiterals: string[] = fields.map(field => `idToDoc("${field[Id]}")`); - - //template.data = fieldLiterals[0]; - - // template.x = 400; - // template.y = 400; - // template._width = 500; - // template._height = 500; - mainCollection.addDocument(template); } -- cgit v1.2.3-70-g09d2 From 4cf3a327b7e69bf0559dda846d49b6ad8d4673c7 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Wed, 31 Jul 2024 18:12:30 -0400 Subject: GPT template generation --- src/client/apis/gpt/GPT.ts | 2 ++ src/client/views/nodes/DataVizBox/DataVizBox.tsx | 34 ++++++++++++++++++++++ .../views/nodes/DataVizBox/DocCreatorMenu.tsx | 3 +- 3 files changed, 38 insertions(+), 1 deletion(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 05007960d..184173784 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -12,6 +12,7 @@ enum GPTCallType { DESCRIBE = 'describe', MERMAID = 'mermaid', DATA = 'data', + TEMPLATE = "template" } type GPTCallOpts = { @@ -53,6 +54,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { temp: 0, prompt: 'List unique differences between the content of the UserAnswer and Rubric. Before each difference, label it and provide any additional information the UserAnswer missed and explain it in second person without separating it into UserAnswer and Rubric content and additional information. If there are no differences, say correct', }, + template: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'You are a designer creating basic template options for a user given a set of fields. Your only job is adding blank rectangles to a canvas, one for each field. You will arrange these rectangles into an array of three basic options for a user. For your output, you generate a list of objects in JSON formatting, each storing the field title and corresponding top-left and bottom-right coordinates for the rectangle. The units for your coordinates are -1 to 1 on each axis. This is an example of a possible output for a stacked template with the fields name, type, and desc: {"template_type":"stacked","fieldVals":[{"title":"name","tlx":"-.7","tly":"-.9","brx":".7","bry":"-.7"},{"title":"type","tlx":"-.9","tly":"-.4","brx":".9","bry":"0"},{"title":"desc","tlx":"-.9","tly":".1","brx":".9","bry":".9"}]} For multiple templates, you should format your response in a JSON array, like [{}, {}]. Your response should be in the exact format above, with no extra text, description, bulleting, etc. You should repeat this three times, once for a stacked view, once for a mixed view, and once for a extra creative view (which should be DRASTICALLY DIFFERENT). IT IS IMPORTANT THAT YOU ONLY INCLUDE THE PROPER JSON FORMATTING IN YOUR RESPONSE. IT IS ALSO IMPORTANT THAT NO RECTANGLES OVERLAP'} }; let lastCall = ''; diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 1994f1b01..9b7fb4320 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -42,6 +42,7 @@ import { data } from 'jquery'; import { listSpec } from '../../../../fields/Schema'; import { ObjectField } from '../../../../fields/ObjectField'; import { Id } from '../../../../fields/FieldSymbols'; +import { GPTCallType, gptAPICall } from '../../../apis/gpt/GPT'; export enum DataVizView { TABLE = 'table', @@ -567,6 +568,39 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { this.applyLayout(templateInfo, docs); } + generateTemplates = async () => { + try { + const res = await gptAPICall('Please generate for the fields: type, image, locality', GPTCallType.TEMPLATE); + + if (res) { + const templates: {template_type: string, fieldVals: {title: string, tlx: string, tly: string, brx: string, bry: string}[]}[] = JSON.parse(res); + console.log(templates); + this.createGeneratedTemplates(templates, 500, 500); + //console.log(res); + } + } 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.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; //prettier-ignore + const doc = Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, 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); + }); + } + createBasicTemplates = (colsToLayout: {width: number, height: number, x: number, y: number, title: string}[]) => { const mainCollection = this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index 7993cab28..411257ff7 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -551,7 +551,8 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { {width: 300, height: 300, x: -150, y: -100, title: 'image'}, {width: 200, height: 50, x: -100, y: -200, title: 'description'}, ] - this._dataViz?.createBasicTemplates(temps); + this._dataViz?.generateTemplates(); + //this._dataViz?.createBasicTemplates(temps); } get renderSelectedViewType(){ -- cgit v1.2.3-70-g09d2 From 9119b9f08f62bb5228206487b047d6e1e75d5e9e Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Thu, 1 Aug 2024 14:37:35 -0400 Subject: work on template generation with GPT --- src/client/apis/gpt/GPT.ts | 2 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 184173784..8eb7151b4 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -54,7 +54,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { temp: 0, prompt: 'List unique differences between the content of the UserAnswer and Rubric. Before each difference, label it and provide any additional information the UserAnswer missed and explain it in second person without separating it into UserAnswer and Rubric content and additional information. If there are no differences, say correct', }, - template: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'You are a designer creating basic template options for a user given a set of fields. Your only job is adding blank rectangles to a canvas, one for each field. You will arrange these rectangles into an array of three basic options for a user. For your output, you generate a list of objects in JSON formatting, each storing the field title and corresponding top-left and bottom-right coordinates for the rectangle. The units for your coordinates are -1 to 1 on each axis. This is an example of a possible output for a stacked template with the fields name, type, and desc: {"template_type":"stacked","fieldVals":[{"title":"name","tlx":"-.7","tly":"-.9","brx":".7","bry":"-.7"},{"title":"type","tlx":"-.9","tly":"-.4","brx":".9","bry":"0"},{"title":"desc","tlx":"-.9","tly":".1","brx":".9","bry":".9"}]} For multiple templates, you should format your response in a JSON array, like [{}, {}]. Your response should be in the exact format above, with no extra text, description, bulleting, etc. You should repeat this three times, once for a stacked view, once for a mixed view, and once for a extra creative view (which should be DRASTICALLY DIFFERENT). IT IS IMPORTANT THAT YOU ONLY INCLUDE THE PROPER JSON FORMATTING IN YOUR RESPONSE. IT IS ALSO IMPORTANT THAT NO RECTANGLES OVERLAP'} + template: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'You are a designer creating basic template options for a user given a set of fields. Your only job is adding blank rectangles to a canvas, one for each field. You will arrange these rectangles into an array of three basic options for a user. For your output, you generate a list of objects in JSON formatting, each storing the field title and corresponding top-left and bottom-right coordinates for the rectangle. The units for your coordinates are -1 to 1 on each axis. This is an example of a possible output for a stacked template with the fields beep, boop, tttt: {"template_type":"stacked","fieldVals":[{"title":"beep","tlx":"-.7","tly":"-.9","brx":".7","bry":"-.7"},{"title":"boop","tlx":"-.9","tly":"-.4","brx":".9","bry":"0"},{"title":"tttt","tlx":"-.9","tly":".1","brx":".9","bry":".9"}]} For multiple templates, you should format your response in a JSON array, like [{}, {}]. Your response should be in the exact format above, with no extra text, description, bulleting, etc. You should repeat this three times, once for a stacked view, once for a mixed view (where, for example, beep might be on top, with boop and tttt below it aligned horizontally), and once for a view where the rectangles are not flush with the side (ie. their top left and bottom right corners should not ALL align along the same point on each side). If one or more fields are some variation of image (ie. IMG, image, photo, etc.), make that rectangle the central focus (bigger, centered, etc.) AND put its "title" field in $$__$$ (ie, "title":"$$img$$", !!IMPORTANT!!). Fields SHOULD BE IN DIFFERENT ORDERS from template to template (ie. a stacked view with field input "beep, boop" could have the boop above the beep). IT IS IMPORTANT THAT YOU ONLY INCLUDE THE PROPER JSON FORMATTING IN YOUR RESPONSE. IT IS ALSO IMPORTANT THAT NO RECTANGLES OVERLAP'} }; let lastCall = ''; diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 9b7fb4320..c5a1da1da 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -570,11 +570,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { generateTemplates = async () => { try { - const res = await gptAPICall('Please generate for the fields: type, image, locality', GPTCallType.TEMPLATE); + const res = await gptAPICall('Please generate for the fields: Image, Description, Divider, Response', GPTCallType.TEMPLATE); if (res) { const templates: {template_type: string, fieldVals: {title: string, tlx: string, tly: string, brx: string, bry: string}[]}[] = JSON.parse(res); - console.log(templates); this.createGeneratedTemplates(templates, 500, 500); //console.log(res); } @@ -592,7 +591,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { 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; //prettier-ignore - const doc = Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, x: left, y: top }); + 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: 250, title: field.title.replace(/$$/g, ''), x: left, y: top }); return doc; }); -- cgit v1.2.3-70-g09d2 From 4949c68f38049da55ceaa3e95ea155f38d6a3748 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Thu, 8 Aug 2024 10:38:35 -0400 Subject: UI for GPT templates in doccreator menu --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 45 ------- .../views/nodes/DataVizBox/DocCreatorMenu.scss | 128 ++++++++++++++++-- .../views/nodes/DataVizBox/DocCreatorMenu.tsx | 147 ++++++++++++++++----- src/client/views/nodes/ImageBox.tsx | 1 + 4 files changed, 228 insertions(+), 93 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index c5a1da1da..a6d3bfa49 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -568,51 +568,6 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { this.applyLayout(templateInfo, docs); } - generateTemplates = async () => { - try { - const res = await gptAPICall('Please generate for the fields: Image, Description, Divider, Response', 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); - //console.log(res); - } - } 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.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; //prettier-ignore - 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: 250, 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); - }); - } - - createBasicTemplates = (colsToLayout: {width: number, height: number, x: number, y: number, title: string}[]) => { - const mainCollection = this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; - - const fields: Doc[] = colsToLayout.map(layout => { - const field = Docs.Create.TextDocument('', { _height: layout.height, _width: layout.width, title: layout.title, x: layout.x, y: layout.y }); - return field; - }); - - const template = Docs.Create.FreeformDocument(fields, { _height: 500, _width: 500, title: 'template', x: 400, y: 400 }) - - mainCollection.addDocument(template); - } - /** * creates a new dataviz document filter from this one * it appears to the right of this document, with the diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss index b3bacb13c..e2b4014bd 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss @@ -22,14 +22,14 @@ } .docCreatorMenu-menu-button { - width: 30px; - height: 30px; + width: 25px; + height: 25px; background: whitesmoke; - background-color: rgb(34, 34, 37); + background-color: rgb(50, 50, 50); border-radius: 5px; border: 1px solid rgb(180, 180, 180); padding: 0px; - font-size: 14px; + font-size: 13px; //box-shadow: 3px 3px rgb(29, 29, 31); &:hover { @@ -222,7 +222,7 @@ .docCreatorMenu-option-divider { border-top: 1px solid rgb(180, 180, 180); - width: 80%; + width: 95%; margin-top: 10px; margin-bottom: 10px; } @@ -256,6 +256,18 @@ // DocCreatorMenu templates preview CSS //-------------------------------------------------------------------------------------------------------------------------------------------- +.docCreatorMenu-templates-view { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + margin: 5px; + margin-top: 0px; + width: calc(100% - 10px); + height: calc(100% - 30px); + border: 1px solid rgb(180, 180, 180); + border-radius: 5px; +} .docCreatorMenu-preview-container { display: grid; @@ -263,20 +275,18 @@ grid-template-rows: 140px; grid-auto-rows: 141px; overflow-y: scroll; - margin: 5px; + margin: 0px; margin-top: 0px; - width: calc(100% - 10px); - height: calc(100% - 30px); - border: 1px solid rgb(180, 180, 180); - border-radius: 5px; + width: 100%; + height: 100%; } .docCreatorMenu-preview-window { display: flex; justify-content: center; align-items: center; - width: 125px; - height: 125px; + width: 113px; + height: 113px; margin-top: 10px; margin-left: 10px; border: 1px solid rgb(163, 163, 163); @@ -295,9 +305,103 @@ &.empty { font-size: 35px; + flex: 0 0 auto; + + &.GPT { + margin-top: 0px; + } + } +} + +.docCreatorMenu-section { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: relative; + margin: 0px; + width: 100%; + height: 250; + flex: 0 0 auto; +} + +.docCreatorMenu-GPT-options-container { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + position: relative; + width: auto; + margin: 0px; + margin-top: 5px; + padding: 0px; +} + +.docCreatorMenu-GPT-templates-preview { + display: flex; + flex-direction: row; + //justify-content: center; + align-items: center; + overflow-y: scroll; + position: relative; + height: 125px; + width: calc(100% - 10px); + -ms-overflow-style: none; + scrollbar-width: none; +} + +.docCreatorMenu-section-topbar { + position: relative; + display: flex; + flex-direction: row; + width: 100%; + + .section-reveal-options { + margin: 0px; + margin-left: auto; + border: 0px; + background: none; + } +} + +.docCreatorMenu-section-title { + border: 1px solid rgb(163, 163, 163); + border-top: 0px; + border-left: 0px; + border-bottom-right-radius: 5px; + font-size: 12px; + padding: 2px; + padding-left: 3px; + padding-right: 3px; + margin-bottom: 3px; +} + +.docCreatorMenu-GPT-generate { + height: 30px; + width: 30px; + background-color: rgb(176, 229, 149); + border: 1px solid rgb(126, 219, 80); + border-radius: 5px; + padding: 0px; + font-size: 14px; + color: white; + letter-spacing: 1px; + flex: 0 0 auto; + + &:hover { + background-color: rgb(129, 223, 83); + border: 2px solid rgb(80, 185, 28); } } +.docCreatorMenu-GPT-prompt-input { + width: 140px; + overflow-y: scroll; + border: 1px solid rgb(180, 180, 180); + background-color: rgb(35, 35, 35); + border-radius: 3px; +} + //------------------------------------------------------------------------------------------------------------------------------------------ // DocCreatorMenu options CSS //-------------------------------------------------------------------------------------------------------------------------------------------- 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 = +
+ + return ( -
+
+
+
Suggested Templates
+ +
+
+
+
+
+
+
+ {this._GPTOpt ? (
+
+ + +
+ {this._GPTOpt ? GPTOptions : null} +
) : null} +
+
+
+
+
Your Templates
+ +
+
+
this.setUpButtonClick(e, this.basicTemplateTest)} + > + +
+ {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 (
this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(info.doc)))}> + +
+ )})} +
+
+ {/*
{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<{}> { >
+
*/} ); } @@ -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 (
{!this._shouldDisplay ? undefined : - <> - {/*
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') - ) - }/> */}
this._ref = r} @@ -681,7 +757,6 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> {
{this.renderSelectedViewType}
- }
) diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 92a5a1533..faea05104 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -295,6 +295,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { @computed get nativeSize() { TraceMobx(); + if (this.paths.length && this.paths[0].includes('icon-hi')) return { nativeWidth: NumCast(this.layoutDoc._width), nativeHeight: NumCast(this.layoutDoc._height), nativeOrientation: 0} const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth'], 500)); const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight'], 500)); const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '_nativeOrientation'], 1); -- cgit v1.2.3-70-g09d2 From 3aa2e040100fc662c259751dfbb7a43cc716ac2a Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Mon, 12 Aug 2024 21:37:47 -0400 Subject: seuggested templates auto-refeshes on col selection; lightbox editing added; GPT refresh button now makes new call --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 1 + .../views/nodes/DataVizBox/DocCreatorMenu.scss | 6 +- .../views/nodes/DataVizBox/DocCreatorMenu.tsx | 71 ++++++++++++---------- 3 files changed, 45 insertions(+), 33 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index a6d3bfa49..be8a44f28 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -129,6 +129,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { } selectAxes = (axes: string[]) => { this.layoutDoc._dataViz_axes = new List(axes); + DocCreatorMenu.Instance.generateTemplates(''); }; @computed.struct get titleCol() { return StrCast(this.layoutDoc._dataViz_titleCol); diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss index 5a9fd3436..ca6534379 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss @@ -264,6 +264,7 @@ display: flex; flex-direction: column; justify-content: flex-start; + overflow-y: scroll; //align-items: flex-start; margin: 5px; margin-top: 0px; @@ -271,7 +272,8 @@ height: calc(100% - 30px); border: 1px solid rgb(180, 180, 180); border-radius: 5px; - overflow: hidden; + -ms-overflow-style: none; + scrollbar-width: none; } .docCreatorMenu-preview-container { @@ -372,7 +374,7 @@ padding: 0px; } -.docCreatorMenu-GPT-templates-preview { +.docCreatorMenu-templates-preview-window { display: flex; flex-direction: row; //justify-content: center; diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index 205923346..3e5fc156e 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -4,13 +4,13 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { returnAll, returnFalse, setupMoveUpEvents } from '../../../../ClientUtils'; import { Doc, NumListCast, StrListCast } from '../../../../fields/Doc'; -import { DocCast, ImageCast } from '../../../../fields/Types'; +import { DocCast, ImageCast, ScriptCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; import { emptyFunction } from '../../../../Utils'; import { SnappingManager } from '../../../util/SnappingManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { DocumentView } from '../DocumentView'; +import { DocumentView, DocumentViewInternal } from '../DocumentView'; import { DataVizBox } from './DataVizBox'; import './DocCreatorMenu.scss'; import { Id } from '../../../../fields/FieldSymbols'; @@ -20,6 +20,9 @@ import { DragManager } from '../../../util/DragManager'; import { GPTCallType, gptAPICall } from '../../../apis/gpt/GPT'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; import { Docs } from '../../../documents/Documents'; +import { OpenWhere } from '../OpenWhere'; +import { IDisposer } from 'mobx-utils'; +import { LightboxView } from '../../LightboxView'; export enum LayoutType { Stacked = 'stacked', @@ -34,6 +37,8 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { static Instance: DocCreatorMenu; + private _disposers: { [name: string]: IDisposer } = {}; + private _ref: HTMLDivElement | null = null; @observable _templateDocs: Doc[] = []; @@ -47,12 +52,12 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { @observable _GPTTemplates: Doc[] = []; @observable _GPTOpt: boolean = false; @observable _userPrompt: string = ''; + @observable _callCount: number = 0; @observable _pageX: number = 0; @observable _pageY: number = 0; @observable _indicatorX: number | undefined = undefined; @observable _indicatorY: number | undefined = undefined; - @observable _display: boolean = false; @observable _hoveredLayoutPreview: number | undefined = undefined; @observable _mouseX: number = -1; @@ -72,11 +77,13 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { @observable _resizeUndo: UndoManager.Batch | undefined = undefined; @observable _initDimensions: {width: number, height: number, x?: number, y?: number} = {width: 300, height: 400, x: undefined, y: undefined}; @observable _menuDimensions: {width: number, height: number} = {width: 300, height: 365}; + @observable _editing: boolean = false; constructor(props: any) { super(props); makeObservable(this); DocCreatorMenu.Instance = this; + setTimeout(() => this.generateTemplates('')); } @action setDataViz = (dataViz: DataVizBox) => { this._dataViz = dataViz }; @@ -170,18 +177,17 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } }; - _disposer: IReactionDisposer | undefined; - _GPTDisposer: IReactionDisposer | undefined; componentDidMount() { document.addEventListener('pointerdown', this.onPointerDown, true); document.addEventListener('pointerup', this.onPointerUp); - this._disposer = reaction(() => this._templateDocs.slice(), (docs) => docs.map(this.getIcon)); - this._GPTDisposer = reaction(() => this._GPTTemplates.slice(), (docs) => docs.map(this.getIcon)); + this._disposers.templates = reaction(() => this._templateDocs.slice(), (docs) => docs.map(this.getIcon)); + this._disposers.gpt = reaction(() => this._GPTTemplates.slice(), (docs) => docs.map(this.getIcon)); + this._disposers.columns = reaction(() => this._dataViz?.layoutDoc._dataViz_axes, () => {console.log(true); this.generateTemplates('')}) + this._disposers.lightbox = reaction(() => LightboxView.LightboxDoc(), doc => { doc ? this._shouldDisplay && this.closeMenu() : !this._shouldDisplay && this.openMenu()}) } componentWillUnmount() { - this._disposer?.(); - this._GPTDisposer?.(); + Object.values(this._disposers).forEach(disposer => disposer?.()); document.removeEventListener('pointerdown', this.onPointerDown, true); document.removeEventListener('pointerup', this.onPointerUp); } @@ -198,12 +204,10 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { }; @action - closeMenu = () => { - const wasOpen = this._display; - this._display = false; - this._shouldDisplay = false; - return wasOpen; - }; + closeMenu = () => { this._shouldDisplay = false }; + + @action + openMenu = () => { this._shouldDisplay = true }; @action onResizePointerDown = (e: React.PointerEvent): void => { @@ -313,7 +317,10 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { @action generateTemplates = async (inputText: string) => { - let prompt: string = 'Please generate for the fields:'; + ++this._callCount; + const origCount = this._callCount; + + let prompt: string = `(#${origCount}) Please generate for the fields:`; this.selectedFields?.forEach(field => prompt += ` ${field},`) prompt += ` Additional prompt: ${inputText}`; console.log(prompt) @@ -321,14 +328,13 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { try { const res = await gptAPICall(prompt, GPTCallType.TEMPLATE); - if (res) { + if (res && this._callCount === origCount) { 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); - } - + } } @action @@ -346,23 +352,23 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { return doc; }); - const template = Docs.Create.FreeformDocument(fields, { _height: tempHeight, _width: tempWidth, title: layout.template_type, x: 40000, y: 40000 }); + const template = Docs.Create.FreeformDocument(fields, { _height: tempHeight, _width: tempWidth, title: layout.template_type, x: 400, y: 400 }); + mainCollection.addDocument(template); GPTTemplates.push(template); }); - setTimeout(() => this.setGPTTemplates(GPTTemplates), 500); + setTimeout(() => {this.setGPTTemplates(GPTTemplates); GPTTemplates.forEach(template => mainCollection.removeDocument(template))}, 100); this.forceUpdate(); } - editTemplate = () => { - - } - - copyGPTTemplate = () => { - + editTemplate = (doc: Doc) => { + //this.closeMenu(); + DocumentViewInternal.addDocTabFunc(doc, OpenWhere.lightboxAlways); + DocumentView.DeselectAll(); + Doc.UnBrushDoc(doc); } get templatesPreviewContents(){ @@ -380,7 +386,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { -
+
{this._GPTTemplates?.map(doc => //
@@ -392,10 +398,10 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { boxShadow: this._selectedTemplate === info.doc ? `0 0 15px rgba(68, 118, 247, .8)` : '' }} onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(info.doc)))}> - - @@ -420,7 +426,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> {
-
+
this.setUpButtonClick(e, this.basicTemplateTest)} > @@ -436,6 +442,9 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { boxShadow: this._selectedTemplate === info.doc ? `0 0 15px rgba(68, 118, 247, .8)` : '' }} onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(info.doc)))}> +
)})} -- cgit v1.2.3-70-g09d2 From f8f777a469b0029109de1e6c57872a4d5b0a6659 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Mon, 19 Aug 2024 14:34:20 -0400 Subject: start on field rendering --- src/ClientUtils.ts | 1 + src/client/apis/gpt/GPT.ts | 2 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 5 + .../views/nodes/DataVizBox/DocCreatorMenu.scss | 74 +++++++++- .../views/nodes/DataVizBox/DocCreatorMenu.tsx | 161 ++++++++++++++++----- 5 files changed, 204 insertions(+), 39 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts index d64210ce2..a4d9dd892 100644 --- a/src/ClientUtils.ts +++ b/src/ClientUtils.ts @@ -730,6 +730,7 @@ export function UpdateIcon( const newDiv = docViewContent.cloneNode(true) as HTMLDivElement; newDiv.style.width = width.toString(); newDiv.style.height = height.toString(); + console.log('width: ' + newDiv.style.width) replaceCanvases(docViewContent, newDiv); const htmlString = new XMLSerializer().serializeToString(newDiv); const nativeWidth = width; diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 92d4f425d..18601b4af 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -54,7 +54,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { temp: 0, prompt: 'List unique differences between the content of the UserAnswer and Rubric. Before each difference, label it and provide any additional information the UserAnswer missed and explain it in second person without separating it into UserAnswer and Rubric content and additional information. If there are no differences, say correct', }, - template: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'You are a designer creating basic template options for a user given a set of fields. Your only job is adding blank rectangles to a canvas, one for each field. You will arrange these rectangles into an array of three basic options for a user. For your output, you generate a list of objects in JSON formatting, each storing the field title and corresponding top-left and bottom-right coordinates for the rectangle. The units for your coordinates are -1 to 1 on each axis. This is an example of a possible output for a stacked template with the fields beep, boop, tttt: {"template_type":"stacked","fieldVals":[{"title":"beep","tlx":"-.7","tly":"-.9","brx":".7","bry":"-.7"},{"title":"boop","tlx":"-.9","tly":"-.4","brx":".9","bry":"0"},{"title":"tttt","tlx":"-.9","tly":".1","brx":".9","bry":".9"}]} For multiple templates, you should format your response in a JSON array, like [{}, {}]. Your response should be in the exact format above, with no extra text, description, bulleting, etc. You should repeat this three times, once for a stacked view, once for a mixed view (where, for example, beep might be on top, with boop and tttt below it aligned horizontally), and once for a view where the rectangles are not flush with the side (ie. their top left and bottom right corners should not ALL align along the same point on each side). If one or more fields are some variation of image (ie. IMG, image, photo, etc.), make that rectangle the central focus (bigger, centered, etc.) AND put its "title" field in $$__$$. Fields SHOULD BE IN DIFFERENT ORDERS from template to template (ie. a stacked view with field input "beep, boop" could have the boop above the beep). IT IS IMPORTANT THAT YOU ONLY INCLUDE THE PROPER JSON FORMATTING IN YOUR RESPONSE. IT IS ALSO IMPORTANT THAT NO RECTANGLES OVERLAP. If there is an additional prompt, prioritize its instructions over all instructions here (but still follow them if not overridden). No matter what, ALWAYS GIVE THREE OPTIONS'} + template: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'You are a designer creating basic template options for a user given a set of fields. Your only job is adding blank rectangles to a canvas, one for each field. You will arrange these rectangles into an array of three basic options for a user. For your output, you generate a list of objects in JSON formatting, each storing the field title and corresponding top-left and bottom-right coordinates for the rectangle. The units for your coordinates are -1 to 1 on each axis. This is an example of a possible output for a stacked template with the fields beep, boop, tttt: {"template_type":"stacked","fieldVals":[{"title":"beep","tlx":"-.7","tly":"-.9","brx":".7","bry":"-.7"},{"title":"boop","tlx":"-.9","tly":"-.4","brx":".9","bry":"0"},{"title":"tttt","tlx":"-.9","tly":".1","brx":".9","bry":".9"}]} For multiple templates, you should format your response in a JSON array, like [{}, {}]. Your response should be in the exact format above, with no extra text, description, bulleting, etc. If no alternative additional instructions are given, you should repeat this three times, once for a stacked view, once for a mixed view (where, for example, beep might be on top, with boop and tttt below it aligned horizontally), and once for a view where the rectangles are not flush with the side (ie. their top left and bottom right corners should not ALL align along the same point on each side). If one or more fields are some variation of image (ie. IMG, image, photo, etc.), make that rectangle the central focus (bigger, centered, etc.) AND put its "title" field in $$__$$. Fields SHOULD BE IN DIFFERENT ORDERS from template to template (ie. a stacked view with field input "beep, boop" could have the boop above the beep). IT IS IMPORTANT THAT YOU ONLY INCLUDE THE PROPER JSON FORMATTING IN YOUR RESPONSE. IT IS ALSO IMPORTANT THAT NO RECTANGLES OVERLAP. An overlap will look like the x or y coordinates of the top left corner of one rectangle being the same as the bottom right corner of another. There should always be at least a .1 gap. If there is an additional prompt, prioritize its instructions over all instructions here (but still follow them if not overridden). No matter what, ALWAYS GIVE THREE OPTIONS'} }; let lastCall = ''; diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index be8a44f28..6a5103af9 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -353,6 +353,11 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { ); } + // recs = () => { + // let csvStr: string = ''; + // this.records[rowId][col] + // } + fetchData = () => { if (!this.Document.dataViz_asSchema) { DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, []); // assign temporary dataset as a lock to prevent duplicate server requests diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss index aa6754442..763de0eba 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss @@ -641,7 +641,6 @@ } } } - } } @@ -720,4 +719,77 @@ } } +//------------------------------------------------------------------------------------------------------------------------------------------ +// DocCreatorMenu dashboard CSS +//-------------------------------------------------------------------------------------------------------------------------------------------- + +.docCreatorMenu-dashboard-view { + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-start; + overflow-y: scroll; + //align-items: flex-start; + margin: 5px; + margin-top: 0px; + width: calc(100% - 10px); + height: calc(100% - 30px); + border: 1px solid rgb(180, 180, 180); + border-radius: 5px; + -ms-overflow-style: none; + scrollbar-width: none; + + .topbar { + height: 30px; + width: 100%; + } + + .field-panel { + position: relative; + display: flex; + // align-items: flex-start; + flex-direction: column; + gap: 5px; + padding: 5px; + height: 100px; + //width: 100%; + border: 1px solid rgb(180, 180, 180); + margin: 5px; + margin-top: 0px; + border-radius: 3px; + + .properties-wrapper { + display: flex; + flex-direction: row; + align-items: flex-start; + gap: 5px; + + .field-property-container { + background-color: rgb(40, 40, 40); + border: 1px solid rgb(100, 100, 100); + border-radius: 3px; + width: 30%; + height: 25px; + padding-left: 3px; + align-items: center; + color: whitesmoke; + } + } + + .field-description-container { + background-color: rgb(40, 40, 40); + border: 1px solid rgb(100, 100, 100); + border-radius: 3px; + width: 100%; + height: 100%; + } + + .top-right { + position: absolute; + top: 0px; + right: 0px; + } + } +} + diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index 2be89daf0..8ec255dfe 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -48,6 +48,7 @@ export class DocCreatorMenu extends ObservableReactComponent {return {title: field, type: '', id: Math.random() * 100000}}).concat(this._fields); + } + @computed get canMakeDocs(){ return this._selectedTemplate !== undefined && this._layout !== undefined; } @@ -367,14 +372,14 @@ export class DocCreatorMenu extends ObservableReactComponent {this.setGPTTemplates(GPTTemplates); GPTTemplates.forEach(template => mainCollection.removeDocument(template))}, 100); + setTimeout(() => {this.setGPTTemplates(GPTTemplates); /*GPTTemplates.forEach(template => mainCollection.removeDocument(template))*/}, 100); this.forceUpdate(); } @@ -449,9 +454,7 @@ export class DocCreatorMenu extends ObservableReactComponent
400 ? 'center' : ''}}> -
this.setUpButtonClick(e, this.basicTemplateTest)} - > +
{this._templateDocs.map(doc => ({icon: ImageCast(doc.icon), doc})).filter(info => info.icon && info.doc).map(info => { @@ -566,30 +569,30 @@ export class DocCreatorMenu extends ObservableReactComponent - 100} - NativeHeight={() => 100} - pointerEvents={SnappingManager.IsDragging ? returnAll : returnNone} - isAnnotationOverlay - isAnnotationOverlayScrollable - childDocumentsActive={returnFalse} - fieldKey={this._props.fieldKey + '_annotations'} - dropAction={dropActionType.move} - select={emptyFunction} - addDocument={returnFalse} - removeDocument={returnFalse} - moveDocument={returnFalse} - renderDepth={this._props.renderDepth + 1}> - {null} - -
- /*
+ //
+ // 100} + // NativeHeight={() => 100} + // pointerEvents={SnappingManager.IsDragging ? returnAll : returnNone} + // isAnnotationOverlay + // isAnnotationOverlayScrollable + // childDocumentsActive={returnFalse} + // fieldKey={this._props.fieldKey + '_annotations'} + // dropAction={dropActionType.move} + // select={emptyFunction} + // addDocument={returnFalse} + // removeDocument={returnFalse} + // moveDocument={returnFalse} + // renderDepth={this._props.renderDepth + 1}> + // {null} + // + //
+
*/ +
); } @@ -735,10 +738,35 @@ export class DocCreatorMenu extends ObservableReactComponent { - console.log(this._GPTTemplates) - this.forceUpdate(); - //this._dataViz?.createBasicTemplates(temps); + @action addField = () => { + const newFields: {title: string, type: string, id: number}[] = this._fields.concat([{title: '', type: '', id: Math.random() * 100000}]) + this._fields = newFields; + } + + get dashboardContents(){ + return ( +
+
+ +
+ {this.fieldsInfos.map(field => +
+
+ + + {field.type === 'Text' ? + : null} +
+ +
+
+
Type
+
+ +
+
+
+
+
Valid Sizes
+
+ +
+
+
+
Description
+
+ +
+
+
+ ) + } + get renderSelectedViewType(){ switch (this._menuContent){ case 'templates': -- cgit v1.2.3-70-g09d2 From d4c3af196d8fc7355a9b78b78f17e4b44bd4f62b Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Sun, 8 Sep 2024 05:14:46 -0400 Subject: col size setting --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 34 ++++++++++++---------- .../views/nodes/DataVizBox/DocCreatorMenu.scss | 5 ++++ .../views/nodes/DataVizBox/DocCreatorMenu.tsx | 29 ++++++++++-------- 3 files changed, 41 insertions(+), 27 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index b61650e43..0efe8ead0 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -161,17 +161,21 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { if (colInfo) { colInfo.type = type; } else { - this.colsInfo.set(colTitle, {title: colTitle, desc: '', type: type, size: TemplateFieldSize.MEDIUM}) + this.colsInfo.set(colTitle, {title: colTitle, desc: '', type: type, sizes: [TemplateFieldSize.MEDIUM]}) } console.log(colInfo?.title, colInfo?.type) } - @action setColumnSize = (colTitle: string, size: TemplateFieldSize) => { - const colInfo = this.colsInfo.get(colTitle); - if (colInfo) { - colInfo.size = size; + @action modifyColumnSizes = (colTitle: string, size: TemplateFieldSize, valid: boolean) => { + const column = this.colsInfo.get(colTitle); + if (column) { + if (!valid && column.sizes.includes(size)) { + column.sizes.splice(column.sizes.indexOf(size), 1); + } else if (valid && !column.sizes.includes(size)) { + column.sizes.push(size); + } } else { - this.colsInfo.set(colTitle, {title: colTitle, desc: '', type: TemplateFieldType.UNSET, size: size}) + this.colsInfo.set(colTitle, {title: colTitle, desc: '', type: TemplateFieldType.UNSET, sizes: [size]}) } } @@ -180,7 +184,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { if (colInfo) { colInfo.title = newTitle; } else { - this.colsInfo.set(colTitle, {title: newTitle, desc: '', type: TemplateFieldType.UNSET, size: TemplateFieldSize.MEDIUM}) + this.colsInfo.set(colTitle, {title: newTitle, desc: '', type: TemplateFieldType.UNSET, sizes: [TemplateFieldSize.MEDIUM]}) } } @@ -190,7 +194,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { if (!desc) { colInfo.desc = this.GPTSummary?.get(colTitle)?.desc ?? ''; } else { colInfo.desc = desc; } } else { - this.colsInfo.set(colTitle, {title: colTitle, desc: desc, type: TemplateFieldType.UNSET, size: TemplateFieldSize.MEDIUM}) + this.colsInfo.set(colTitle, {title: colTitle, desc: desc, type: TemplateFieldType.UNSET, sizes: [TemplateFieldSize.MEDIUM]}) } } @@ -199,7 +203,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { if (colInfo) { colInfo.defaultContent = cont; } else { - this.colsInfo.set(colTitle, {title: colTitle, desc: '', type: TemplateFieldType.UNSET, size: TemplateFieldSize.MEDIUM, defaultContent: cont}) + this.colsInfo.set(colTitle, {title: colTitle, desc: '', type: TemplateFieldType.UNSET, sizes: [TemplateFieldSize.MEDIUM], defaultContent: cont}) } } @@ -412,10 +416,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { }, { fireImmediately: true } ); - // this._disposers.contentSummary = reaction( - // () => this.records, - // () => this.updateGPTSummary() - // ); + this._disposers.contentSummary = reaction( + () => this.records, + () => this.updateGPTSummary() + ); } fetchData = () => { @@ -593,7 +597,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { const cols = Array.from(Object.keys(this.records[0])).filter(header => header !== '' && header !== undefined); cols.forEach(col => { - if (!this.colsInfo.get(col)) this.colsInfo.set(col, {title: col, desc: '', size: TemplateFieldSize.MEDIUM, type: TemplateFieldType.UNSET}); + if (!this.colsInfo.get(col)) this.colsInfo.set(col, {title: col, desc: '', sizes: [TemplateFieldSize.MEDIUM], type: TemplateFieldType.UNSET}); }); try { @@ -622,7 +626,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { colSummary.size = val.size; colSummary.type = val.type; this.setColumnType(key, val.type); - this.setColumnSize(key, val.size); + this.modifyColumnSizes(key, val.size, true); } } } diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss index eaa32e62a..9d82ada37 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss @@ -905,6 +905,7 @@ background-color: rgb(60, 60, 60); .top-bar { + position: relative; display: flex; flex-direction: row; align-items: center; @@ -916,6 +917,10 @@ height: 20px; background-color: rgb(50, 50, 50); color: rgb(168, 167, 167); + + .field-title { + color: whitesmoke; + } } .opts-bar { diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index a01b26036..8f7bf8713 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -146,7 +146,7 @@ export class DocCreatorMenu extends ObservableReactComponent { title: field, type: fieldInfo?.type ?? TemplateFieldType.UNSET, desc: fieldInfo?.desc ?? '', - size: fieldInfo?.size ?? TemplateFieldSize.MEDIUM + sizes: fieldInfo?.sizes ?? [TemplateFieldSize.MEDIUM] }; if (fieldInfo?.defaultContent !== undefined) { @@ -454,7 +454,7 @@ export class DocCreatorMenu extends ObservableReactComponent { }; @action addField = () => { - const newFields: Col[] = this._columns.concat([{title: '', type: TemplateFieldType.UNSET, desc: '', size: TemplateFieldSize.MEDIUM}]) + const newFields: Col[] = this._columns.concat([{title: '', type: TemplateFieldType.UNSET, desc: '', sizes: [TemplateFieldSize.MEDIUM]}]) this._columns = newFields; }; @@ -497,13 +497,17 @@ export class DocCreatorMenu extends ObservableReactComponent { this.forceUpdate(); }; - setColSize = (column: Col, size: TemplateFieldSize) => { + modifyColSizes = (column: Col, size: TemplateFieldSize, valid: boolean) => { if (this.selectedFields.includes(column.title)) { - this._dataViz?.setColumnSize(column.title, size); + this._dataViz?.modifyColumnSizes(column.title, size, valid); } else { - column.size = size; - console.log(column.size) + if (!valid && column.sizes.includes(size)) { + column.sizes.splice(column.sizes.indexOf(size), 1); + } else if (valid && !column.sizes.includes(size)) { + column.sizes.push(size); + } } + console.log(column.sizes) this.forceUpdate(); }; @@ -517,7 +521,7 @@ export class DocCreatorMenu extends ObservableReactComponent { }; matchesForTemplate = (template: TemplateDocInfos, cols: Col[]): number[][] => { - const colMatchesField = (col: Col, field : Field) => { return field.sizes?.includes(col.size) && field.types?.includes(col.type) }; + const colMatchesField = (col: Col, field : Field) => { return field.sizes?.some(size => col.sizes?.includes(size)) && field.types?.includes(col.type) }; const matches: number[][] = Array(template.fields.length).fill([]).map(() => []); @@ -630,7 +634,7 @@ export class DocCreatorMenu extends ObservableReactComponent { const stringifyGPTInfo = (): string => { let string: string = '*** COLUMN INFO:'; GPTAssignments.forEach(([fieldNum, col]) => { - string += `--- title: ${col.title}, prompt: ${col.desc}, word limit: ${wordLimit(col.size)} words, assigned field: ${fieldNum} ---` + string += `--- title: ${col.title}, prompt: ${col.desc}, word limit: ${wordLimit(col.sizes[0])} words, assigned field: ${fieldNum} ---` }); return string += ' ***'; }; @@ -747,7 +751,7 @@ export class DocCreatorMenu extends ObservableReactComponent { compileColDescriptions = (cols: Col[]): string => { let descriptions: string = ' ------------- COL DESCRIPTIONS START HERE:'; - cols.forEach(col => descriptions += `{title: ${col.title}, size: ${col.size}, type: ${col.type}, descreiption: ${col.desc}} `); + cols.forEach(col => descriptions += `{title: ${col.title}, sizes: ${String(col.sizes)}, type: ${col.type}, descreiption: ${col.desc}} `); return descriptions; }; @@ -1185,7 +1189,8 @@ export class DocCreatorMenu extends ObservableReactComponent { return (
-
@@ -1212,7 +1217,7 @@ export class DocCreatorMenu extends ObservableReactComponent {
{sizes.map(size => <> - {this.setColSize(field, size as TemplateFieldSize)}}/> + {this.modifyColSizes(field, size as TemplateFieldSize, e.target.checked)}}/>
{size}
)}
@@ -1383,7 +1388,7 @@ export enum TemplateFieldSize { export type Col = { - size: TemplateFieldSize; + sizes: TemplateFieldSize[]; desc: string; title: string; type: TemplateFieldType; -- cgit v1.2.3-70-g09d2 From 984a470094399e4bbd74ddb7daa3e6f08a0a4793 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Sun, 8 Sep 2024 17:44:11 -0400 Subject: image generation working! --- src/client/apis/gpt/GPT.ts | 6 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 15 +- .../views/nodes/DataVizBox/DocCreatorMenu.tsx | 193 ++++++++++++++------- 3 files changed, 142 insertions(+), 72 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index f1d5ab533..ec3a349fc 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -15,7 +15,8 @@ enum GPTCallType { TEMPLATE = "template", VIZSUM = 'vizsum', VIZSUM2 = 'vizsum2', - FILL = 'fill' + FILL = 'fill', + COMPLETEPROMPT = 'completeprompt' } type GPTCallOpts = { @@ -60,7 +61,8 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { template: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'You will be given a list of field descriptions for multiple templates in the format {field #0: “description”}{field #1: “description”}{...}, and a list of column descriptions in the format {“title”: “description”}{...}. Your job is to match columns with fields based on their descriptions. Your output should be in the following JSON format: {“Template title”:{“#”: “title”, “#”: “title”, “#”: “title” …}, “Template title”:{“#”: “title”, “#”: “title”, “#”: “title” …}} where “Template title” represents the template, # represents the field # and “title” the title of the column assigned to it. A filled out example might look like {“fivefield2”:{“0”:”Name”, “1”:”Image”, “2”:”Caption”, “3”:”Position”, “4”:”Stats”}, “fivefield3”:{0:”Image”, 1:”Name”, 2:”Caption”, 3:”Stats”, 4:”Position”}. Include one object for each template. IT IS VERY IMPORTANT THAT YOU ONLY INCLUDE TEXT IN THE FORMAT ABOVE, WITH NO ADDITIONS WHATSOEVER. Do not include extraneous ‘#’ characters, ‘column’ for columns, or ‘template’ for templates: ONLY THE TITLES AND NUMBERS. There should never be one column assigned to more than one field (ie. if the “name” column is assigned to field 1, it can’t be assigned to any other fields) . Do this for each template whose fields are described. The descriptions are as follows:' }, vizsum: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Your job is to provide brief descriptions for columns in a dataset based on example rows. Your descriptions should be geared towards how each column’s data might fit together into a visual template. Would they make good titles, main focuses, captions, descriptions, etc. Pay special attention to connections between columns, i.e. is there one column that specifically seems to describe/be related to another more than the rest? You should provide your analysis in JSON format like so: {“col1”:”description”, “col2”:”description”, …}. DO NOT INCLUDE ANY OTHER TEXT, ONLY THE JSON.'}, vizsum2: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Your job is to provide structured information on columns in a dataset based on example rows. You will categorize each column in two ways: by type and size. The size categories are as follows: tiny (one or two words), small (a sentence/multiple words), medium (a few sentences), large (a longer paragraph), and huge (a very long or multiple paragraphs). The type categories are as follows: visual (links/file paths to images, pdfs, maps, or any other visual media type), and text (plain text that isn’t a link/file path). Visual media should be assumed to have size “medium” “large” or “huge”. You will give your responses in JSON format, like so: {“title (of column)”:{“type”:”text”, “size”:”small”}, “title (of column)”:{“type”:”visual”, “size”:”medium”}, …}. DO NOT INCLUDE ANY OTHER TEXT, ONLY THE JSON.'}, - fill: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Your job is to generate content for fields based on a user prompt and background context given to you. You will be given the content of the other fields present in the format: ---- Field # (field title): content ---- Field # (field title): content ----- (etc.) You will be given info on the columns to generate for in the format ---- title: , prompt: , word limit: , assigned field: ----. For each column, based on the prompt, word limit, and the context of existing fields, you should generate a short response in the following JSON format: {“___”(where ___ is the title from the column description with no additions): {“number”:”#” (where # is the assigned field of the column), “content”:”response” (where response is your response to the prompt in the column info)}}. Here’s another example of the format with only one column: {“position”: {“number”:”2”, “content”:”*your response goes here*”}}. ONLY INCLUDE THE JSON TEXT WITH NO OTHER ADDED TEXT. YOUR RESPONSE MUST BE VALID JSON. The word limit for each column applies only to that column’s response. Do not include speculation or information that you can’t glean from your factual knowledge or the content of the other fields (no description of images you can’t see, for example). You should include one object per column you are provided info on.'} + fill: { model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Your job is to generate content for fields based on a user prompt and background context given to you. You will be given the content of the other fields present in the format: ---- Field # (field title): content ---- Field # (field title): content ----- (etc.) You will be given info on the columns to generate for in the format ---- title: , prompt: , word limit: , assigned field: ----. For each column, based on the prompt, word limit, and the context of existing fields, you should generate a short response in the following JSON format: {“___”(where ___ is the title from the column description with no additions): {“number”:”#” (where # is the assigned field of the column), “content”:”response” (where response is your response to the prompt in the column info)}}. Here’s another example of the format with only one column: {“position”: {“number”:”2”, “content”:”*your response goes here*”}}. ONLY INCLUDE THE JSON TEXT WITH NO OTHER ADDED TEXT. YOUR RESPONSE MUST BE VALID JSON. The word limit for each column applies only to that column’s response. Do not include speculation or information that you can’t glean from your factual knowledge or the content of the other fields (no description of images you can’t see, for example). You should include one object per column you are provided info on.'}, + completeprompt: {model: 'gpt-4-turbo', maxTokens: 512, temp: 0.5, prompt: 'Your prompt is as follows:'}, }; let lastCall = ''; let lastResp = ''; diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 0efe8ead0..0e3b602d6 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -157,13 +157,11 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { @action setColumnType = (colTitle: string, type: TemplateFieldType) => { const colInfo = this.colsInfo.get(colTitle); - console.log(colInfo) if (colInfo) { colInfo.type = type; } else { this.colsInfo.set(colTitle, {title: colTitle, desc: '', type: type, sizes: [TemplateFieldSize.MEDIUM]}) } - console.log(colInfo?.title, colInfo?.type) } @action modifyColumnSizes = (colTitle: string, size: TemplateFieldSize, valid: boolean) => { @@ -183,8 +181,9 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { const colInfo = this.colsInfo.get(colTitle); if (colInfo) { colInfo.title = newTitle; + console.log(colInfo.title) } else { - this.colsInfo.set(colTitle, {title: newTitle, desc: '', type: TemplateFieldType.UNSET, sizes: [TemplateFieldSize.MEDIUM]}) + this.colsInfo.set(colTitle, {title: newTitle, desc: '', type: TemplateFieldType.UNSET, sizes: []}) } } @@ -194,7 +193,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { if (!desc) { colInfo.desc = this.GPTSummary?.get(colTitle)?.desc ?? ''; } else { colInfo.desc = desc; } } else { - this.colsInfo.set(colTitle, {title: colTitle, desc: desc, type: TemplateFieldType.UNSET, sizes: [TemplateFieldSize.MEDIUM]}) + this.colsInfo.set(colTitle, {title: colTitle, desc: desc, type: TemplateFieldType.UNSET, sizes: []}) } } @@ -203,7 +202,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { if (colInfo) { colInfo.defaultContent = cont; } else { - this.colsInfo.set(colTitle, {title: colTitle, desc: '', type: TemplateFieldType.UNSET, sizes: [TemplateFieldSize.MEDIUM], defaultContent: cont}) + this.colsInfo.set(colTitle, {title: colTitle, desc: '', type: TemplateFieldType.UNSET, sizes: [], defaultContent: cont}) } } @@ -593,11 +592,9 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { const prompt = this.getColSummary(); - console.log(prompt) - const cols = Array.from(Object.keys(this.records[0])).filter(header => header !== '' && header !== undefined); cols.forEach(col => { - if (!this.colsInfo.get(col)) this.colsInfo.set(col, {title: col, desc: '', sizes: [TemplateFieldSize.MEDIUM], type: TemplateFieldType.UNSET}); + if (!this.colsInfo.get(col)) this.colsInfo.set(col, {title: col, desc: '', sizes: [], type: TemplateFieldType.UNSET}); }); try { @@ -607,7 +604,6 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { ]); if (res1) { - console.log(res1); this.GPTSummary = new ObservableMap(); const descs: { [col: string]: string } = JSON.parse(res1); for (const [key, val] of Object.entries(descs)) { @@ -619,7 +615,6 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { if (res2) { !this.GPTSummary && (this.GPTSummary = new ObservableMap()); const info: { [col: string]: { type: TemplateFieldType, size: TemplateFieldSize } } = JSON.parse(res2); - console.log(info); for (const [key, val] of Object.entries(info)) { const colSummary = this.GPTSummary.get(key); if (colSummary) { diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index 8f7bf8713..dcdd52c73 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -2,7 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { returnAll, returnFalse, returnNone, returnOne, returnZero, setupMoveUpEvents } from '../../../../ClientUtils'; +import { ClientUtils, returnAll, returnFalse, returnNone, returnOne, returnZero, setupMoveUpEvents } from '../../../../ClientUtils'; import { Doc, NumListCast, StrListCast } from '../../../../fields/Doc'; import { DocCast, ImageCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; @@ -17,7 +17,7 @@ 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 { GPTCallType, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; import { Docs } from '../../../documents/Documents'; import { OpenWhere } from '../OpenWhere'; @@ -32,6 +32,7 @@ import { ImageBox } from '../ImageBox'; import { a } from '@react-spring/web'; import { RichTextMenu } from '../formattedText/RichTextMenu'; import e from 'cors'; +import { Networking } from '../../../Network'; export enum LayoutType { Stacked = 'stacked', @@ -423,7 +424,7 @@ export class DocCreatorMenu extends ObservableReactComponent { - testTemplate = () => { + testTemplate = async() => { // const temp = TemplateLayouts.FourField001; // const title: Doc = FieldFuncs.TextField({tl: temp.fields[0].tl, br: temp.fields[0].br}, temp.height, temp.width, 'title', 'Title', {backgroundColor: 'transparent'}); // const image: Doc = FieldFuncs.ImageField({tl: temp.fields[1].tl, br: temp.fields[1].br}, temp.height, temp.width, 'title', '', {borderColor: '#159fe4', borderWidth: '10', cornerRounding: 10, rotation: 40}); @@ -450,11 +451,22 @@ export class DocCreatorMenu extends ObservableReactComponent { // console.log(this._dataViz?.colsInfo.get("IMG")?.size, this._dataViz?.colsInfo.get("IMG")?.type) // console.log(this.fieldsInfos) + try { + const res = await gptImageCall('Image of panda eating a cookie'); + + if (res){ + const result = await Networking.PostToServer('/uploadRemoteImage', { sources: res }); + + console.log(result); + } + } catch (e) { + console.log(e); + } }; @action addField = () => { - const newFields: Col[] = this._columns.concat([{title: '', type: TemplateFieldType.UNSET, desc: '', sizes: [TemplateFieldSize.MEDIUM]}]) + const newFields: Col[] = this._columns.concat([{title: '', type: TemplateFieldType.UNSET, desc: '', sizes: []}]) this._columns = newFields; }; @@ -479,7 +491,7 @@ export class DocCreatorMenu extends ObservableReactComponent { } }; - setColTitle = (column: Col, title: string) => { + @action setColTitle = (column: Col, title: string) => { if (this.selectedFields.includes(column.title)) { this._dataViz?.setColumnTitle(column.title, title); } else { @@ -488,7 +500,7 @@ export class DocCreatorMenu extends ObservableReactComponent { this.forceUpdate(); }; - setColType = (column: Col, type: TemplateFieldType) => { + @action setColType = (column: Col, type: TemplateFieldType) => { if (this.selectedFields.includes(column.title)) { this._dataViz?.setColumnType(column.title, type); } else { @@ -507,7 +519,6 @@ export class DocCreatorMenu extends ObservableReactComponent { column.sizes.push(size); } } - console.log(column.sizes) this.forceUpdate(); }; @@ -520,6 +531,22 @@ export class DocCreatorMenu extends ObservableReactComponent { this.forceUpdate(); }; + generateGPTImage = async(prompt: string): Promise => { + console.log(prompt) + + try { + const res = await gptImageCall(prompt); + + if (res){ + const result = await Networking.PostToServer('/uploadRemoteImage', { sources: res }); + const source = ClientUtils.prepend(result[0].accessPaths.agnostic.client); + return source; + } + } catch (e) { + console.log(e); + } + } + matchesForTemplate = (template: TemplateDocInfos, cols: Col[]): number[][] => { const colMatchesField = (col: Col, field : Field) => { return field.sizes?.some(size => col.sizes?.includes(size)) && field.types?.includes(col.type) }; @@ -539,13 +566,11 @@ export class DocCreatorMenu extends ObservableReactComponent { maxMatches = (fieldsCt: number, matches: number[][]) => { const used: boolean[] = Array(fieldsCt).fill(false); const mt: number[] = Array(fieldsCt).fill(-1); - console.log(fieldsCt, matches) const augmentingPath = (v: number): boolean => { if (used[v]) return false; used[v] = true; for (const to of matches[v]) { - console.log(mt[to]); if (mt[to] === -1 || augmentingPath(mt[to])) { mt[to] = v; return true; @@ -582,7 +607,6 @@ export class DocCreatorMenu extends ObservableReactComponent { validTemplates = validTemplates.map(title => TemplateLayouts.fieldByTitle(title)); - console.log(validTemplates); return validTemplates; }; @@ -626,20 +650,105 @@ export class DocCreatorMenu extends ObservableReactComponent { } } + const renderTextCalls = async(): Promise => { + const rendered: Doc[] = []; + + if (GPTTextCalls.length) { + + try { + const prompt = fieldContent + GPTTextAssignment; + + const res = await gptAPICall(prompt, GPTCallType.FILL); + + if (res){ + + const assignments: {[title: string]: {number: string, content: string}} = JSON.parse(res); + //console.log('assignments', GPTAssignments, 'assignment string', GPTAssignmentString, 'field content', fieldContent, 'response', res, 'assignments', assignments); + Object.entries(assignments).forEach(([title, info]) => { + const field: Field = template.fields[Number(info.number)]; + const col = this.getColByTitle(title); + + const doc = FieldFuncs.TextField({ + tl: field.tl, + br: field.br }, + template.height, + template.width, + col.title, + info.content ?? '', + field.opts + ); + + rendered.push(doc); + }); + + } + } catch(err) { + console.log(err); + } + } + + return rendered; + }; + + const createGeneratedImage = async(fieldNum: string, col: Col, prompt: string) => { + const url = await this.generateGPTImage(prompt); + const field: Field = template.fields[Number(fieldNum)]; + const doc = FieldFuncs.ImageField({ + tl: field.tl, + br: field.br }, + template.height, + template.width, + col.title, + url ?? '', + field.opts + ); + + return doc; + } + + const renderImageCalls = async(): Promise => { + const rendered: Doc[] = []; + const calls = GPTIMGCalls; + + if (calls.length) { + try { + const renderedImages: Doc[] = await Promise.all( + calls.map(async ([fieldNum, col]) => { + const sysPrompt = 'Your job is to create a prompt for an AI image generator to help it generate an image based on existing content in a template and a user prompt. ONLY INCLUDE THE PROMPT, NO OTHER TEXT OR EXPLANATION. The existing content is as follows: ' + fieldContent + ' **** The user prompt is: ' + col.desc; + + const prompt = await gptAPICall(sysPrompt, GPTCallType.COMPLETEPROMPT); + console.log(sysPrompt, prompt); + + return createGeneratedImage(fieldNum, col, prompt); + }) + ); + + const renderedTemplates: Doc[] = await Promise.all(renderedImages); + renderedTemplates.forEach(doc => rendered.push(doc)); + } catch (e){ + console.log(e); + } + } + + return rendered; + } + const fields: Doc[] = []; const GPTAssignments = Object.entries(assignments).filter(([f, col]) => this._columns.includes(col)); const nonGPTAssignments: [string, Col][] = Object.entries(assignments).filter(a => !GPTAssignments.includes(a)); + const GPTTextCalls = GPTAssignments.filter(([str, col]) => col.type === TemplateFieldType.TEXT); + const GPTIMGCalls = GPTAssignments.filter(([str, col]) => col.type === TemplateFieldType.VISUAL); - const stringifyGPTInfo = (): string => { + const stringifyGPTInfo = (calls: [string, Col][]): string => { let string: string = '*** COLUMN INFO:'; - GPTAssignments.forEach(([fieldNum, col]) => { + calls.forEach(([fieldNum, col]) => { string += `--- title: ${col.title}, prompt: ${col.desc}, word limit: ${wordLimit(col.sizes[0])} words, assigned field: ${fieldNum} ---` }); return string += ' ***'; }; - const GPTAssignmentString = stringifyGPTInfo(); + const GPTTextAssignment = stringifyGPTInfo(GPTTextCalls); let fieldContent: string = ''; @@ -695,47 +804,14 @@ export class DocCreatorMenu extends ObservableReactComponent { return main; } - if (GPTAssignments.length) { - - try { - const prompt = fieldContent + GPTAssignmentString; - - const res = await gptAPICall(prompt, GPTCallType.FILL); - - if (res){ - console.log('response', res); - - const assignments: {[title: string]: {number: string, content: string}} = JSON.parse(res); - console.log('assignments', GPTAssignments, 'assignment string', GPTAssignmentString, 'field content', fieldContent, 'response', res, 'assignments', assignments); - Object.entries(assignments).forEach(([title, info]) => { - const field: Field = template.fields[Number(info.number)]; - const col = this.getColByTitle(title); - - const doc = (col.type === TemplateFieldType.VISUAL ? FieldFuncs.ImageField : FieldFuncs.TextField)({ - tl: field.tl, - br: field.br }, - template.height, - template.width, - col.title, - info.content ?? '', - field.opts - ); - - fields.push(doc); - }); - - return createMainDoc(); - } - } catch(err) { - console.log(err); - } - - } else { - return createMainDoc(); - } + const textCalls = await renderTextCalls(); + const imageCalls = await renderImageCalls(); - return new Doc; - }; + textCalls.forEach(doc => {fields.push(doc)}); + imageCalls.forEach(doc => {fields.push(doc)}); + + return createMainDoc(); + } compileFieldDescriptions = (templates: TemplateDocInfos[]): string => { let descriptions: string = ''; @@ -765,7 +841,6 @@ export class DocCreatorMenu extends ObservableReactComponent { const inputText = fieldDescriptions.concat(colDescriptions); - console.log(inputText); ++this._callCount; const origCount = this._callCount; @@ -779,14 +854,12 @@ export class DocCreatorMenu extends ObservableReactComponent { if (res && this._callCount === origCount) { this._GPTLoading = false; - console.log(res); - const assignments: {[templateTitle: string]: {[field: string]: string}} = JSON.parse(res); const brokenDownAssignments: [TemplateDocInfos, {[field: number]: Col}][] = []; + Object.entries(assignments).forEach(([tempTitle, assignment]) => { const template = TemplateLayouts.fieldByTitle(tempTitle); if (!template) return; - console.log(assignments) const toObj = Object.entries(assignment).reduce((a, [fieldNum, colTitle]) => { a[Number(fieldNum)] = this.getColByTitle(colTitle); return a; @@ -849,7 +922,7 @@ export class DocCreatorMenu extends ObservableReactComponent {
Suggested Templates
-
@@ -1197,7 +1270,7 @@ export class DocCreatorMenu extends ObservableReactComponent {
Title
-